source: ResearchApps/PHY/WARPLAB/WARPLab7/M_Code_Reference/mex/wl_mex_udp_transport.c

Last change on this file was 6295, checked in by murphpo, 6 years ago

adding u64 cast to fix problematic behavior observed by user (https://warpproject.org/forums/viewtopic.php?pid=17171)

File size: 179.3 KB
RevLine 
[2150]1/******************************************************************************
2*
3* File   :  wl_mex_udp_transport.c
4* Authors:  Chris Hunter (chunter [at] mangocomm.com)
5*           Patrick Murphy (murphpo [at] mangocomm.com)
6*           Erik Welsh (welsh [at] mangocomm.com)
7* License:  Copyright 2013, Mango Communications. All rights reserved.
8*           Distributed under the WARP license  (http://warpproject.org/license)
9*
10******************************************************************************/
11/**
12*
13* @file wl_mex_udp_transport.c
14*
15* Implements the basic socket layer in MEX for the WARPLab Transport protocol
16*
[2185]17* Compile command:
[2150]18*
[2185]19*   Windows (Visual C++):
20*       mex -g -O wl_mex_udp_transport.c -lwsock32 -lKernel32 -DWIN32
[2150]21*
[2185]22*   MAC / Unix:
23*       mex -g -O wl_mex_udp_transport.c
24*
25*
[2150]26* MODIFICATION HISTORY:
27*
28* Ver   Who  Date     Changes
29* ----- ---- -------- -------------------------------------------------------
30* 1.00a ejw  7/15/13  Initial release
[2185]31* 1.01a ejw  8/28/13  Updated to support MAC / Unix as well as Windows
[2150]32*
33*
34******************************************************************************/
35
36
37
38/***************************** Include Files *********************************/
39#include <stdio.h>
40#include <stdlib.h>
41#include <string.h>
42#include <math.h>
43#include <ctype.h>
44
45#include <matrix.h>
46#include <mex.h>  
47
[2185]48
49#ifdef WIN32
50
[2150]51#include <Windows.h>
52#include <WinBase.h>
53#include <winsock.h>
54
[2185]55#else
[2150]56
[2185]57#include <errno.h>
58#include <netdb.h>
59#include <sys/types.h>
60#include <sys/socket.h>
61#include <netinet/in.h>
62#include <arpa/inet.h>
63#include <unistd.h>
64#include <fcntl.h>
65
66#endif
67
68
69
[2150]70/*************************** Constant Definitions ****************************/
71
72// Use to print debug message to the console
73// #define _DEBUG_
74
[2185]75
[4818]76// Version WARPLab MEX Transport Driver
77//
78// NOTE:  This version must match the required version in:
79//     - wl_setup.m
80//     - classes/wl_transport_eth_udp_mex.m
81//     - classes/wl_transport_eth_udp_mex_bcast.m
82//
83#define WL_MEX_UDP_TRANSPORT_VERSION                       "1.0.4a"
84
85
86// Windows / Unix compatibility
[2185]87#ifdef WIN32
88
[2150]89// Define printf for future compatibility
[4293]90#define printf                                             mexPrintf
91#define malloc(x)                                          mxMalloc(x)
92#define free(x)                                            mxFree(x)
93#define make_persistent(x)                                 mexMakeMemoryPersistent(x)
94#define usleep(x)                                          Sleep((x)/1000)
95#define close(x)                                           closesocket(x)
96#define non_blocking_socket(x)                           { unsigned long optval = 1; ioctlsocket( x, FIONBIO, &optval ); }
97#define wl_usleep(x)                                       wl_mex_udp_transport_usleep(x)
98#define wl_timestamp                                       wl_mex_udp_transport_usec_timestamp()
99#define SOCKET                                             SOCKET
100#define get_last_error                                     WSAGetLastError()
101#define EWOULDBLOCK                                        WSAEWOULDBLOCK
102#define socklen_t                                          int
[2150]103
[2185]104#else
[2150]105
[2185]106// Define printf for future compatibility
[4293]107#define printf                                             mexPrintf
108#define malloc(x)                                          mxMalloc(x)
109#define free(x)                                            mxFree(x)
110#define make_persistent(x)                                 mexMakeMemoryPersistent(x)
111#define usleep(x)                                          usleep(x)
112#define close(x)                                           close(x)
113#define non_blocking_socket(x)                             fcntl( x, F_SETFL, O_NONBLOCK )
114#define wl_usleep(x)                                       usleep(x)
115#define wl_timestamp                                       0
116#define SOCKET                                             int
117#define get_last_error                                     errno
118#define INVALID_SOCKET                                     0xFFFFFFFF
119#define SOCKET_ERROR                                       -1
[2185]120
121#endif
122
123
[2150]124// WL_MEX_UDP_TRANSPORT Commands
[4293]125#define TRANSPORT_REVISION                                 0
126#define TRANSPORT_INIT_SOCKET                              1
127#define TRANSPORT_SET_SO_TIMEOUT                           2
128#define TRANSPORT_SET_SEND_BUF_SIZE                        3
129#define TRANSPORT_GET_SEND_BUF_SIZE                        4
130#define TRANSPORT_SET_RCVD_BUF_SIZE                        5
131#define TRANSPORT_GET_RCVD_BUF_SIZE                        6
132#define TRANSPORT_CLOSE                                    7
133#define TRANSPORT_SEND                                     8
134#define TRANSPORT_RECEIVE                                  9
135#define TRANSPORT_READ_IQ                                  10
136#define TRANSPORT_READ_RSSI                                11
137#define TRANSPORT_WRITE_IQ                                 12
138#define TRANSPORT_WRITE_IQ_SET_PKT_WAIT_TIME               13
139#define TRANSPORT_READ_IQ_SET_MAX_REQUEST_SIZE             14
140#define TRANSPORT_SUPPRESS_IQ_WARNINGS                     15
[2150]141
[2880]142
[2150]143// Maximum number of sockets that can be allocated
[4293]144#define TRANSPORT_MAX_SOCKETS                              65
[2150]145
146// Maximum size of a packet
[4293]147#define TRANSPORT_MAX_PKT_LENGTH                           9050
[2150]148
149// Socket state
[4293]150#define TRANSPORT_SOCKET_FREE                              0
151#define TRANSPORT_SOCKET_IN_USE                            1
[2150]152
153// Transport defines
[4293]154#define TRANSPORT_NUM_PENDING                              20
155#define TRANSPORT_MIN_SEND_SIZE                            1000
156#define TRANSPORT_SLEEP_TIME                               10000
157#define TRANSPORT_FLAG_ROBUST                              0x0001
158#define TRANSPORT_PADDING_SIZE                             2
159#define TRANSPORT_TIMEOUT                                  10000000
160#define TRANSPORT_MAX_RETRY                                50
161#define TRANSPORT_NOT_READY_WAIT_TIME                      100000
162#define TRANSPORT_NOT_READY_MAX_RETRY                      50
163#define TRANSPORT_HDR_NODE_NOT_READY_FLAG                  0x8000
[2150]164
[4416]165// Command defines
166#define CMD_PARAM_SUCCESS                                  0x00000000
167#define CMD_PARAM_ERROR                                    0xFF000000
168
[2150]169// Sample defines
[4416]170#define SAMPLE_RESPONSE_SUCCESS                            0x00000000
171#define SAMPLE_RESPONSE_ERROR                              0xFFFFFFFF
[4293]172#define SAMPLE_IQ_ERROR                                    0x01
173#define SAMPLE_IQ_NOT_READY                                0x02
[4416]174#define SAMPLE_CHECKSUM_FAILED                             0x03
[2150]175
[4293]176#define SAMPLE_IQ_WAIT_TIME                                TRANSPORT_NOT_READY_WAIT_TIME
177#define SAMPLE_IQ_MAX_RETRY                                TRANSPORT_NOT_READY_MAX_RETRY
[4236]178
[4293]179#define SAMPLE_CHKSUM_RESET                                0x10
180#define SAMPLE_CHKSUM_NOT_RESET                            0x00
181
182#define SAMPLE_LAST_WRITE                                  0x20
183
[2166]184// WARP HW version defines
[4293]185#define TRANSPORT_WARP_HW_v2                               2
186#define TRANSPORT_WARP_HW_v3                               3
[2150]187
[2166]188// WARP Buffers defines
[4293]189#define TRANSPORT_WARP_RF_BUFFER_MAX                       4
[2166]190
[4393]191// IQ defines
192#define IQ_DATA_TYPE_DOUBLE                                0
193#define IQ_DATA_TYPE_SINGLE                                1
194#define IQ_DATA_TYPE_INT16                                 2
195#define IQ_DATA_TYPE_RAW                                   3
[2166]196
[4815]197// RF defines
198#define BUFFER_ID_RFA                                      0x00000001
199#define BUFFER_ID_RFB                                      0x00000002
200#define BUFFER_ID_RFC                                      0x00000004
201#define BUFFER_ID_RFD                                      0x00000008
[4293]202
[4815]203// Sequence number defines
204#define SEQ_NUM_MATCH_IGNORE                               "ignore"
205#define SEQ_NUM_MATCH_WARNING                              "warning"
206#define SEQ_NUM_MATCH_ERROR                                "error"
207
208
[2150]209/*************************** Variable Definitions ****************************/
210
211// Define types for different size data
212typedef unsigned char   uint8;
213typedef unsigned short  uint16;
214typedef unsigned int    uint32;
215
216typedef char            int8;
217typedef short           int16;
218typedef int             int32;
219
220
221// Data packet structure
222typedef struct
223{
[2880]224    char              *buf;                 // Pointer to the data buffer
225    int                length;              // Length of the buffer (buffer must be pre-allocated)
226    int                offset;              // Offset of data to be sent or received
227    struct sockaddr_in address;             // Address information of data to be sent / recevied   
[2150]228} wl_trans_data_pkt;
229
230// Socket structure
231typedef struct
232{
[2880]233    SOCKET              handle;             // Handle to the socket
234    int                 timeout;            // Timeout value
235    int                 status;             // Status of the socket
236    wl_trans_data_pkt  *packet;             // Pointer to a data_packet
237    uint32              rx_buffer_size;     // Rx buffer size of the socket
238    uint32              tx_buffer_size;     // Tx buffer size of the socket
[2150]239} wl_trans_socket;
240
241// WARPLAB Transport Header
242typedef struct
243{
244    uint16             padding;        // Padding for memory alignment
245    uint16             dest_id;        // Destination ID
246    uint16             src_id;         // Source ID
247    uint8              rsvd;           // Reserved
248    uint8              pkt_type;       // Packet type
249    uint16             length;         // Length
250    uint16             seq_num;        // Sequence Number
251    uint16             flags;          // Flags
252} wl_transport_header;
253
254// WARPLAB Command Header
255typedef struct
256{
257    uint32             command_id;     // Command ID
258    uint16             length;         // Length
259    uint16             num_args;       // Number of Arguments
260} wl_command_header;
261
262// WARPLAB Sample Header
263typedef struct
264{
265    uint16             buffer_id;      // Buffer ID
266    uint8              flags;          // Flags
[4416]267    uint8              sample_iq_id;   // IQ ID
[2150]268    uint32             start;          // Starting sample
269    uint32             num_samples;    // Number of samples
270} wl_sample_header;
271
272// WARPLAB Sample Tracket
273typedef struct
274{
275    uint32             start_sample;   // Starting sample
276    uint32             num_samples;    // Number of samples
277} wl_sample_tracker;
278
279
[4293]280typedef int (*wl_function_ptr_t)();
281
282
[2150]283/*********************** Global Variable Definitions *************************/
284
285static int       initialized    = 0;   // Global variable to initialize the driver
[2880]286
287// Global structure of socket connections
[2150]288wl_trans_socket  sockets[TRANSPORT_MAX_SOCKETS];
289
[2880]290// Global variables to allow M control of write IQ inter-packet wait time
291static uint32    use_user_write_iq_wait_time     = 0;
292static uint32    user_write_iq_wait_time         = 0;
293
294// Global variables to allow M control of read IQ max request size
295static uint32    use_user_read_iq_max_req_size   = 0;
296static uint32    user_read_iq_max_req_size       = 0;
297
298// Global variable to suppress Read IQ / Write IQ warnings
299static uint32    suppress_iq_warnings            = 0;
300
[4416]301// Global variables for Read / Write IQ IDs
302static uint8     sample_read_iq_id               = 0;
303static uint8     sample_write_iq_id              = 0;
304
305
[2185]306#ifdef WIN32
307WSADATA          wsaData;              // Structure for WinSock setup communication
308#endif
[2150]309
[4293]310
311
[2150]312/*************************** Function Prototypes *****************************/
313
314// Main function
315void         mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]);
316
317// Socket functions
318void         print_version( void );
319void         init_wl_mex_udp_transport( void );
320int          init_socket( void );
321void         set_so_timeout( int index, int value );
322void         set_reuse_address( int index, int value );
323void         set_broadcast( int index, int value );
324void         set_send_buffer_size( int index, int size );
325int          get_send_buffer_size( int index );
326void         set_receive_buffer_size( int index, int size );
327int          get_receive_buffer_size( int index );
328void         close_socket( int index );
329int          send_socket( int index, char *buffer, int length, char *ip_addr, int port );
330int          receive_socket( int index, int length, char * buffer );
331
332// Debug / Error functions
333void         print_usage( void );
334void         print_sockets( void );
335void         print_buffer(unsigned char *buf, int size);
336void         die( void );
337void         die_with_error( char *errorMessage );
338void         cleanup( void );
339
[4293]340
[2150]341// Helper functions
342void         convert_to_uppercase( char *input, char *output, unsigned int len );
343unsigned int find_transport_function( char *input, unsigned int len );
344
345uint16       endian_swap_16(uint16 value);
346uint32       endian_swap_32(uint32 value);
347
348unsigned int wl_update_checksum(unsigned short int newdata, unsigned char reset );
[4416]349uint32       wl_compute_sample_wait_time(uint32 * command_args);
350
[2185]351int          wl_read_iq_sample_error( wl_sample_tracker *tracker, uint32 num_samples, uint32 start_sample, uint32 num_pkts, uint32 max_sample_size );
352int          wl_read_iq_find_error( wl_sample_tracker *tracker, uint32 num_samples, uint32 start_sample, uint32 num_pkts, uint32 max_sample_size,
353                                    uint32 *ret_num_samples, uint32 *ret_start_sample, uint32 *ret_num_pkts );
[2150]354
[4401]355uint32       wl_compute_write_wait_time(uint32 hw_ver, uint32 buffer_id, uint32 max_samples);
[4416]356uint32       wl_process_write_iq_response(uint32 * command_args, uint32 sample_iq_id, uint32 checksum, uint32 iq_ready_warn);
[4815]357
358void         wl_update_seq_num(uint32 function, uint32 buffer_id, uint32 seq_num, uint32 *seq_num_tracker);
[5265]359void         wl_check_seq_num(uint32 function, char * node_id_str, uint32 buffer_id, uint32 seq_num, uint32 *seq_num_tracker, char *seq_num_severity);
[4815]360
[4293]361#ifdef WIN32
362// Windows specific helper functions
363void         wl_mex_udp_transport_usleep( int wait_time );
364uint32       wl_mex_udp_transport_usec_timestamp();
365
366#endif
367
368
[2150]369// WARPLab Functions
370int          wl_read_baseband_buffer( int index, char *buffer, int length, char *ip_addr, int port,
[4701]371                                      uint32 initial_offset, uint32 num_samples, uint32 start_sample, uint32 buffer_id, 
[4393]372                                      uint32 function, uint32 data_type,
[4815]373                                      void **output_array, uint32 *num_cmds, uint32 *seq_num );
[2150]374
375int          wl_write_baseband_buffer( int index, char *buffer, int max_length, char *ip_addr, int port,
[4484]376                                       uint32 num_samples, uint32 start_sample, const void *samples, uint32 buffer_id, uint32 num_pkts, 
[4393]377                                       uint32 max_samples, uint32 hw_ver, uint32 check_chksum, uint32 data_type, uint32 iteration,
378                                       uint32 *num_cmds, uint32 *checksum );
[2150]379
380/******************************** Functions **********************************/
381
382
383/*****************************************************************************/
384/**
385*  Function:  init_wl_mex_udp_transport
386*
387*  Initialize the driver
388*
389******************************************************************************/
390
391void init_wl_mex_udp_transport( void ) {
392    int i;
393
394    // Print initalization information
395    printf("Loaded wl_mex_udp_transport version %s \n", WL_MEX_UDP_TRANSPORT_VERSION );
396
[4416]397    // Initialize global variables
398    sample_read_iq_id  = 0;
399    sample_write_iq_id = 0;
400   
[2150]401    // Initialize Socket datastructure
402    for ( i = 0; i < TRANSPORT_MAX_SOCKETS; i++ ) {
403        memset( &sockets[i], 0, sizeof(wl_trans_socket) );
404       
[2880]405        sockets[i].handle         = INVALID_SOCKET;
406        sockets[i].status         = TRANSPORT_SOCKET_FREE;
407        sockets[i].timeout        = 0;
408        sockets[i].packet         = NULL;
409        sockets[i].rx_buffer_size = 0;
410        sockets[i].tx_buffer_size = 0;
[2150]411    }
412
[2185]413#ifdef WIN32
[2150]414    // Load the Winsock 2.0 DLL
415    if ( WSAStartup(MAKEWORD(2, 0), &wsaData) != 0 ) {
416        die_with_error("WSAStartup() failed");
417    }
[2185]418#endif
[2150]419       
420    // Set cleanup function to remove persistent data   
421    mexAtExit(cleanup);
422   
423    // Set initialized flag to '1' so this is not run again
424    initialized = 1;
425}
426
427
428/*****************************************************************************/
429/**
430*  Function:  init_socket
431*
432*  Initializes a socket and returns the index in to the sockets array
433*
434******************************************************************************/
435
436int init_socket( void ) {
437    int i;
438   
439    // Allocate a socket in the datastructure
440    for ( i = 0; i < TRANSPORT_MAX_SOCKETS; i++ ) {
441        if ( sockets[i].status == TRANSPORT_SOCKET_FREE ) {  break; }   
442    }
443
444    // Check to see if we cannot allocate a socket
445    if ( i == TRANSPORT_MAX_SOCKETS) {
446        die_with_error("Error:  Cannot allocate a socket");
447    }
448       
449    // Create a best-effort datagram socket using UDP
450    if ( ( sockets[i].handle = socket( PF_INET, SOCK_DGRAM, IPPROTO_UDP ) ) < 0) {
451        die_with_error("socket() failed");
452    }
[2880]453   
[2150]454    // Update the status field of the socket
455    sockets[i].status = TRANSPORT_SOCKET_IN_USE;
456   
457    // Set the reuse_address and broadcast flags for all sockets
458    set_reuse_address( i, 1 );
459    set_broadcast( i, 1 );
460   
[2880]461    // Set the buffer sizes for all sockets
462    get_send_buffer_size(i);
463    get_receive_buffer_size(i);
464   
[2150]465    // Listen on the socket; Make sure we have a non-blocking socket
466    listen( sockets[i].handle, TRANSPORT_NUM_PENDING );
467    non_blocking_socket( sockets[i].handle );
468
469    return i;   
470}
471
472
473/*****************************************************************************/
474/**
475*  Function:  set_so_timeout
476*
477*  Sets the Socket Timeout to the value (in ms)
478*
479******************************************************************************/
480void set_so_timeout( int index, int value ) {
481
482    sockets[index].timeout = value;
483}
484
485
486/*****************************************************************************/
487/**
488*  Function:  set_reuse_address
489*
490*  Sets the Reuse Address option on the socket
491*
492******************************************************************************/
493void set_reuse_address( int index, int value ) {
494    int optval;
495
496    if ( value ) {
497        optval = 1;
498        setsockopt( sockets[index].handle, SOL_SOCKET, SO_REUSEADDR, (const char *)&optval, sizeof(optval) );
499    } else {
500        optval = 0;
501        setsockopt( sockets[index].handle, SOL_SOCKET, SO_REUSEADDR, (const char *)&optval, sizeof(optval) );
502    }   
503}
504
505/*****************************************************************************/
506/**
507*  Function:  set_broadcast
508*
509*  Sets the Broadcast option on the socket
510*
511******************************************************************************/
512void set_broadcast( int index, int value ) {
513    int optval;
514
515    if ( value ) {
516        optval = 1;
517        setsockopt( sockets[index].handle, SOL_SOCKET, SO_BROADCAST, (const char *)&optval, sizeof(optval) );
518    } else {
519        optval = 0;
520        setsockopt( sockets[index].handle, SOL_SOCKET, SO_BROADCAST, (const char *)&optval, sizeof(optval) );
521    }   
522}
523
524
525/*****************************************************************************/
526/**
527*  Function:  set_send_buffer_size
528*
529*  Sets the send buffer size on the socket
530*
531******************************************************************************/
532void set_send_buffer_size( int index, int size ) {
533    int optval = size;
[2880]534    int optlen = sizeof(int);
535    int retval = 0;
[2150]536
[2880]537    // Request size bytes for the socket buffer size
[2150]538    setsockopt( sockets[index].handle, SOL_SOCKET, SO_SNDBUF, (const char *)&optval, sizeof(optval) );
[2880]539   
540    // Get the socket buffer size and set it in the global data structure
541    if ( (retval = getsockopt( sockets[index].handle, SOL_SOCKET, SO_SNDBUF, (char *)&optval, (socklen_t *)&optlen )) != 0 ) {
542        die_with_error("Error:  Could not get socket option - send buffer size"); 
543    } else {
544        sockets[index].tx_buffer_size = optval;
545    }
[2150]546}
547
548
549/*****************************************************************************/
550/**
551*  Function:  get_send_buffer_size
552*
553*  Gets the send buffer size on the socket
554*
555******************************************************************************/
556int get_send_buffer_size( int index ) {
557    int optval = 0;
558    int optlen = sizeof(int);
559    int retval = 0;
560   
[2880]561    // Get the socket buffer size and set it in the global data structure
[2185]562    if ( (retval = getsockopt( sockets[index].handle, SOL_SOCKET, SO_SNDBUF, (char *)&optval, (socklen_t *)&optlen )) != 0 ) {
[2150]563        die_with_error("Error:  Could not get socket option - send buffer size"); 
[2880]564    } else {
565        sockets[index].tx_buffer_size = optval;
[2150]566    }
567   
568#ifdef _DEBUG_   
569    printf("Send Buffer Size:  %d \n", optval );
570#endif
571   
572    return optval;
573}
574
575
576/*****************************************************************************/
577/**
578*  Function:  set_receive_buffer_size
579*
580*  Sets the receive buffer size on the socket
581*
582******************************************************************************/
583void set_receive_buffer_size( int index, int size ) {
584    int optval = size;
[2880]585    int optlen = sizeof(int);
586    int retval = 0;
[2150]587
[2880]588    // Request size bytes for the socket buffer size
589    setsockopt( sockets[index].handle, SOL_SOCKET, SO_RCVBUF, (const char *)&optval, sizeof(optval) );
[2150]590
[2880]591    // Get the socket buffer size and set it in the global data structure
592    if ( (retval = getsockopt( sockets[index].handle, SOL_SOCKET, SO_RCVBUF, (char *)&optval, (socklen_t *)&optlen )) != 0 ) {
593        die_with_error("Error:  Could not get socket option - send buffer size"); 
594    } else {
595        sockets[index].rx_buffer_size = optval;
596    }
[2150]597}
598
599
600/*****************************************************************************/
601/**
602*  Function:  get_receive_buffer_size
603*
604*  Gets the receive buffer size on the socket
605*
606******************************************************************************/
607int get_receive_buffer_size( int index ) {
608    int optval = 0;
609    int optlen = sizeof(int);
610    int retval = 0;
611   
[2880]612    // Get the socket buffer size and set it in the global data structure
[2185]613    if ( (retval = getsockopt( sockets[index].handle, SOL_SOCKET, SO_RCVBUF, (char *)&optval, (socklen_t *)&optlen )) != 0 ) {
[2150]614        die_with_error("Error:  Could not get socket option - send buffer size"); 
[2880]615    } else {
616        sockets[index].rx_buffer_size = optval;
[2150]617    }
618   
619#ifdef _DEBUG_   
620    printf("Rcvd Buffer Size:  %d \n", optval );
621#endif
622   
623    return optval;
624}
625
626
627/*****************************************************************************/
628/**
629*  Function:  close_socket
630*
631*  Closes the socket based on the index
632*
633******************************************************************************/
634void close_socket( int index ) {
635
636#ifdef _DEBUG_
637    printf("Close Socket: %d\n", index);
638#endif   
639
640    if ( sockets[index].handle != INVALID_SOCKET ) {
641        close( sockets[index].handle );
642       
643        if ( sockets[index].packet != NULL ) {
644            free( sockets[index].packet );
645        }
646    } else {
647        printf( "WARNING:  Connection %d already closed.\n", index );
648    }
649
[2880]650    sockets[index].handle         = INVALID_SOCKET;
651    sockets[index].status         = TRANSPORT_SOCKET_FREE;
652    sockets[index].timeout        = 0;
653    sockets[index].packet         = NULL;
654    sockets[index].rx_buffer_size = 0;
655    sockets[index].tx_buffer_size = 0;
[2150]656}
657
658
659/*****************************************************************************/
660/**
661*  Function:  send_socket
662*
663*  Sends the buffer to the IP address / Port that is passed in
664*
665******************************************************************************/
666int send_socket( int index, char *buffer, int length, char *ip_addr, int port ) {
667
668    struct sockaddr_in socket_addr;  // Socket address
669    int                length_sent;
670    int                size;
671
672    // Construct the address structure
673    memset( &socket_addr, 0, sizeof(socket_addr) );        // Zero out structure
674    socket_addr.sin_family      = AF_INET;                 // Internet address family
675    socket_addr.sin_addr.s_addr = inet_addr(ip_addr);      // IP address
676    socket_addr.sin_port        = htons(port);             // Port
677
678    // If we are sending a large amount of data, we need to make sure the entire
679    // buffer has been sent.
680   
681    length_sent = 0;
682    size        = 0xFFFF;
683   
684    if ( sockets[index].status != TRANSPORT_SOCKET_IN_USE ) {
685        return length_sent;
686    }
687
688    while ( length_sent < length ) {
689   
690        // If we did not send more than MIN_SEND_SIZE, then wait a bit
691        if ( size < TRANSPORT_MIN_SEND_SIZE ) {
[4393]692            wl_usleep( TRANSPORT_SLEEP_TIME );
[2150]693        }
694
695        // Send as much data as possible to the address
696        size = sendto( sockets[index].handle, &buffer[length_sent], (length - length_sent), 0, 
697                      (struct sockaddr *) &socket_addr, sizeof(socket_addr) );
698
699        // Check the return value   
700        if ( size == SOCKET_ERROR )  {
[2185]701            if ( get_last_error != EWOULDBLOCK ) {
[2150]702                die_with_error("Error:  Socket Error.");
703            } else {
704                // If the socket is not ready, then we did not send any bytes
705                length_sent += 0;
706            }
707        } else {
708            // Update how many bytes we sent
709            length_sent += size;       
710        }
711
712        // TODO:  IMPLEMENT A TIMEOUT SO WE DONT GET STUCK HERE FOREVER
713        //        FOR WARPLab 7.3.0, this is not implemented and has not
714        //        been an issue during testing.
715    }
[4293]716
717    // Error checking
718    if ( length_sent != length ) {
719        printf("Size of packet sent = %d\n", length_sent);
720        printf("Size of packet      = %d\n", length);
721        die_with_error("Error:  Size of packet sent does not match size of packet.  See above.");
722    }
[2150]723   
724    return length_sent;
725}
726
727
728/*****************************************************************************/
729/**
730*  Function:  receive_socket
731*
732*  Reads data from the socket; will return 0 if no data is available
733*
734******************************************************************************/
735int receive_socket( int index, int length, char * buffer ) {
736
737    wl_trans_data_pkt  *pkt;           
738    int                 size;
739    int                 socket_addr_size = sizeof(struct sockaddr_in);
740   
741    // Allocate a packet in memory if necessary
742    if ( sockets[index].packet == NULL ) {
743        sockets[index].packet = (wl_trans_data_pkt *) malloc( sizeof(wl_trans_data_pkt) );
744       
745        make_persistent( sockets[index].packet );
746
747        if ( sockets[index].packet == NULL ) {
748            die_with_error("Error:  Cannot allocate memory for packet.");       
749        }
750
751        memset( sockets[index].packet, 0, sizeof(wl_trans_data_pkt));       
752    }
753
754    // Get the packet associcated with the index
755    pkt = sockets[index].packet;
756
757    // If we have a packet from the last recevie call, then zero out the address structure   
758    if ( pkt->length != 0 ) {
759        memset( &(pkt->address), 0, sizeof(socket_addr_size));
760    }
761
762    // Receive a response
763    size = recvfrom( sockets[index].handle, buffer, length, 0, 
[2185]764                    (struct sockaddr *) &(pkt->address), (socklen_t *) &socket_addr_size );
[2150]765
766
767    // Check on error conditions
768    if ( size == SOCKET_ERROR )  {
[2185]769        if ( get_last_error != EWOULDBLOCK ) {
[2150]770            die_with_error("Error:  Socket Error.");
771        } else {
772            // If the socket is not ready, then just return a size of 0 so the function can be
773            // called again
774            size = 0;
775        }
776    } else {
777                   
778        // Update the packet associated with the socket
779        //   NOTE:  pkt.address was updated via the function call
780        pkt->buf     = buffer;
781        pkt->offset  = 0;
782    }
783
784    // Update the packet length so we can determine when we need to zero out pkt.address
785    pkt->length  = size;
786
787    return size;
788}
789
790
791/*****************************************************************************/
792/**
793*  Function:  cleanup
794*
795*  Function called by atMexExit to close everything
796*
797******************************************************************************/
798void cleanup( void ) {
799    int i;
800
801    printf("MEX-file is terminating\n");
802
803    // Close all sockets
804    for ( i = 0; i < TRANSPORT_MAX_SOCKETS; i++ ) {
805        if ( sockets[i].handle != INVALID_SOCKET ) {  close_socket( i ); }   
806    }
807
[2185]808#ifdef WIN32
[2150]809    WSACleanup();  // Cleanup Winsock
[2185]810#endif
[2150]811
812}
813
814
815/*****************************************************************************/
816/**
817*  Function:  print_version
818*
819* This function will print the version of the wl_mex_udp_transport driver
820*
821******************************************************************************/
822void print_version( ) {
823
824    printf("WARPLab MEX UDP Transport v%s (compiled %s %s)\n", WL_MEX_UDP_TRANSPORT_VERSION, __DATE__, __TIME__);
[4698]825    printf("Copyright 2013-2015, Mango Communications. All rights reserved.\n");
[2150]826    printf("Distributed under the WARP license:  http://warpproject.org/license  \n");
827}
828
829
830/*****************************************************************************/
831/**
832*  Function:  print_usage
833*
834* This function will print the usage of the wl_mex_udp_transport driver
835*
836******************************************************************************/
837void print_usage( ) {
838
839    printf("Usage:  WARPLab MEX Transport v%s \n", WL_MEX_UDP_TRANSPORT_VERSION );
840    printf("Standard WARPLab transport functions: \n");
841    printf("    1.                  wl_mex_udp_transport('version') \n");
842    printf("    2. index          = wl_mex_udp_transport('init_socket') \n");
843    printf("    3.                  wl_mex_udp_transport('set_so_timeout', index, timeout) \n");
844    printf("    4.                  wl_mex_udp_transport('set_send_buf_size', index, size) \n");
845    printf("    5. size           = wl_mex_udp_transport('get_send_buf_size', index) \n");
846    printf("    6.                  wl_mex_udp_transport('set_rcvd_buf_size', index, size) \n");
847    printf("    7. size           = wl_mex_udp_transport('get_rcvd_buf_size', index) \n");
848    printf("    8.                  wl_mex_udp_transport('close', index) \n");
849    printf("    9. size           = wl_mex_udp_transport('send', index, buffer, length, ip_addr, port) \n");
850    printf("   10. [size, buffer] = wl_mex_udp_transport('receive', index, length ) \n");
851    printf("\n");
852    printf("Additional WARPLab MEX UDP transport functions: \n");
853    printf("    1. [num_samples, cmds_used, samples]  = wl_mex_udp_transport('read_rssi' / 'read_iq', \n");
854    printf("                                                index, buffer, length, ip_addr, port, \n");
[2880]855    printf("                                                number_samples, buffer_id, start_sample, \n");
856    printf("                                                max_length, num_pkts) \n");
[3297]857    printf("    2. [cmds_used, checksum]              = wl_mex_udp_transport('write_iq', \n");
[2150]858    printf("                                                index, cmd_buffer, max_length, ip_addr, port, \n");
859    printf("                                                number_samples, sample_buffer, buffer_id, \n");
[3297]860    printf("                                                start_sample, num_pkts, max_samples, hw_ver, \n");
861    printf("                                                check_chksum) \n");
[2880]862    printf("    3.                = wl_mex_udp_transport('write_iq_set_pkt_wait_time', wait_time) \n");
863    printf("    4.                = wl_mex_udp_transport('read_iq_set_max_request_size', size) \n");
864    printf("    5.                = wl_mex_udp_transport('suppress_iq_warnings') \n");
[2150]865    printf("\n");
866    printf("See documentation for further details.\n");
867    printf("\n");
868}
869
870
871/*****************************************************************************/
872/**
873*  Function:  die
874*
875*  Generates an error message and cause the program to halt
876*
877******************************************************************************/
878void die( ) {
879    mexErrMsgTxt("Error:  See description above.");
880}
881
882
883/*****************************************************************************/
884/**
885*  Function:  die_with_error
886*
887* This function will error out of the wl_mex_udp_transport function call
888*
889******************************************************************************/
890void die_with_error(char *errorMessage) {
[2185]891    printf("%s \n   Socket Error Code: %d\n", errorMessage, get_last_error );
[2150]892    die();
893}
894
895
[4293]896//#if _DEBUG_
[2150]897
898/*****************************************************************************/
899/**
900*  Function:  print_sockets
901*
902*  Debug function to print socket table
903*
904******************************************************************************/
905void print_sockets( void ) {
906    int i;
907   
908    printf("Sockets: \n");   
909   
910    for ( i = 0; i < TRANSPORT_MAX_SOCKETS; i++ ) {
[2880]911        printf("    socket[%d]:  handle = 0x%4x,   timeout = 0x%4x,  status = 0x%4x,  packet = 0x%8x,  rx_buf_size = %8d,  tx_buf_size = %8d\n", 
912               i, sockets[i].handle, sockets[i].timeout, sockets[i].status, sockets[i].packet, sockets[i].rx_buffer_size, sockets[i].tx_buffer_size );
[2150]913    }
914
915    printf("\n");
916}
917
918
919/*****************************************************************************/
920/**
921*  Function:  print_buffer
922*
923*  Debug function to print a buffer
924*
925******************************************************************************/
926void print_buffer(unsigned char *buf, int size) {
927    int i;
928
929    printf("Buffer: (0x%x bytes)\n", size);
930
931    for (i=0; i<size; i++) {
932        printf("%2x ", buf[i] );
933        if ( (((i + 1) % 16) == 0) && ((i + 1) != size) ) {
934            printf("\n");
935        }
936    }
937    printf("\n\n");
938}
939
940
941/*****************************************************************************/
942/**
943*  Function:  print_buffer_16
944*
945*  Debug function to print a buffer
946*
947******************************************************************************/
948void print_buffer_16(uint16 *buf, int size) {
949    int i;
950
951    printf("Buffer: (0x%x bytes)\n", (2*size));
952
953    for (i=0; i<size; i++) {
954        printf("%4x ", buf[i] );
955        if ( (((i + 1) % 16) == 0) && ((i + 1) != size) ) {
956            printf("\n");
957        }
958    }
959    printf("\n\n");
960}
961
962
963/*****************************************************************************/
964/**
965*  Function:  print_buffer_32
966*
967*  Debug function to print a buffer
968*
969******************************************************************************/
970void print_buffer_32(uint32 *buf, int size) {
971    int i;
972
973    printf("Buffer: (0x%x bytes)\n", (4*size));
974
975    for (i=0; i<size; i++) {
976        printf("%8x ", buf[i] );
977        if ( (((i + 1) % 8) == 0) && ((i + 1) != size) ) {
978            printf("\n");
979        }
980    }
981    printf("\n\n");
982}
983
[4293]984//#endif
[2150]985
986
987/*****************************************************************************/
988/**
989*  Function:  endian_swap_16
990*
991* This function will perform an byte endian swap on a 16-bit value
992*
993******************************************************************************/
994uint16 endian_swap_16(uint16 value) {
995
996    return (((value & 0xFF00) >> 8) | ((value & 0x00FF) << 8));
997}
998
999
1000/*****************************************************************************/
1001/**
1002*  Function:  endian_swap_32
1003*
1004* This function will perform an byte endian swap on a 32-bit value
1005*
1006******************************************************************************/
1007uint32 endian_swap_32(uint32 value) {
[4393]1008    uint8  byte_0 = (value & 0x000000FF);
1009    uint8  byte_1 = (value & 0x0000FF00) >>  8;
1010    uint8  byte_2 = (value & 0x00FF0000) >> 16;
1011    uint8  byte_3 = (value & 0xFF000000) >> 24;
1012   
1013    return (uint32)((byte_0 << 24) | (byte_1 << 16) | (byte_2 << 8) | byte_3);
[2150]1014}
1015
1016
1017/*****************************************************************************/
1018/**
1019*  Function:  convert_to_uppercase
1020*
1021* This function converts the input string to all uppercase letters
1022*
1023******************************************************************************/
1024void convert_to_uppercase(char *input, char *output, unsigned int len){
1025  unsigned int i;
1026 
1027  for ( i = 0; i < (len - 1); i++ ) { 
1028      output[i] = toupper( input[i] ); 
1029  }
1030
1031  output[(len - 1)] = '\0'; 
1032}
1033
1034
1035/*****************************************************************************/
1036/**
1037*  Function:  find_transport_function
1038*
1039* This function will return the wl_mex_udp_transport function number based on
1040* the input string
1041*
1042******************************************************************************/
1043unsigned int find_transport_function( char *input, unsigned int len ) {
1044    unsigned int function = 0xFFFF;
1045    char * uppercase;
1046   
1047    uppercase = (char *) mxCalloc(len, sizeof(char));
1048    convert_to_uppercase( input, uppercase, len );
1049
1050#ifdef _DEBUG_
1051    printf("Function :  %s\n", uppercase);
1052#endif   
1053
[2880]1054    if ( !strcmp( uppercase, "VERSION"                      ) && ( function == 0xFFFF ) ) { function = TRANSPORT_REVISION;                     }
1055    if ( !strcmp( uppercase, "INIT_SOCKET"                  ) && ( function == 0xFFFF ) ) { function = TRANSPORT_INIT_SOCKET;                  }
1056    if ( !strcmp( uppercase, "SET_SO_TIMEOUT"               ) && ( function == 0xFFFF ) ) { function = TRANSPORT_SET_SO_TIMEOUT;               }
1057    if ( !strcmp( uppercase, "SET_SEND_BUF_SIZE"            ) && ( function == 0xFFFF ) ) { function = TRANSPORT_SET_SEND_BUF_SIZE;            }
1058    if ( !strcmp( uppercase, "GET_SEND_BUF_SIZE"            ) && ( function == 0xFFFF ) ) { function = TRANSPORT_GET_SEND_BUF_SIZE;            }
1059    if ( !strcmp( uppercase, "SET_RCVD_BUF_SIZE"            ) && ( function == 0xFFFF ) ) { function = TRANSPORT_SET_RCVD_BUF_SIZE;            }
1060    if ( !strcmp( uppercase, "GET_RCVD_BUF_SIZE"            ) && ( function == 0xFFFF ) ) { function = TRANSPORT_GET_RCVD_BUF_SIZE;            }
1061    if ( !strcmp( uppercase, "CLOSE"                        ) && ( function == 0xFFFF ) ) { function = TRANSPORT_CLOSE;                        }
1062    if ( !strcmp( uppercase, "SEND"                         ) && ( function == 0xFFFF ) ) { function = TRANSPORT_SEND;                         }
1063    if ( !strcmp( uppercase, "RECEIVE"                      ) && ( function == 0xFFFF ) ) { function = TRANSPORT_RECEIVE;                      }
1064    if ( !strcmp( uppercase, "READ_IQ"                      ) && ( function == 0xFFFF ) ) { function = TRANSPORT_READ_IQ;                      }
1065    if ( !strcmp( uppercase, "READ_RSSI"                    ) && ( function == 0xFFFF ) ) { function = TRANSPORT_READ_RSSI;                    }
1066    if ( !strcmp( uppercase, "WRITE_IQ"                     ) && ( function == 0xFFFF ) ) { function = TRANSPORT_WRITE_IQ;                     }
1067    if ( !strcmp( uppercase, "WRITE_IQ_SET_PKT_WAIT_TIME"   ) && ( function == 0xFFFF ) ) { function = TRANSPORT_WRITE_IQ_SET_PKT_WAIT_TIME;   }
1068    if ( !strcmp( uppercase, "READ_IQ_SET_MAX_REQUEST_SIZE" ) && ( function == 0xFFFF ) ) { function = TRANSPORT_READ_IQ_SET_MAX_REQUEST_SIZE; }
1069    if ( !strcmp( uppercase, "SUPPRESS_IQ_WARNINGS"         ) && ( function == 0xFFFF ) ) { function = TRANSPORT_SUPPRESS_IQ_WARNINGS;         }
[2150]1070
1071    mxFree( uppercase );
1072    return function;
1073}
1074
1075
1076/*****************************************************************************/
1077/**
1078*
1079* This function is the mex function to implement the data transfer over sockets
1080*
1081* @param    nlhs - Number of left hand side (return) arguments
1082* @param    plhs - Pointers to left hand side (return) arguments
1083* @param    nrhs - Number of right hand side (function) arguments
1084* @param    prhs - Pointers to right hand side (function) arguments
1085*
1086* @return   None
1087*
1088* @note     This function requires the following pointers:
1089*   Input:
1090*       - Function Name
1091*       - Necessary function arguments (see individual function)
1092*
1093*   Output:
1094*       - Necessary function outputs (see individual function)
1095*
1096******************************************************************************/
1097void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[])
1098{
1099
1100    //--------------------------------------------------------------------
1101    // Declare variables
[4293]1102
[4393]1103    int            i, j, k;   
[4293]1104    char          *func                     = NULL;
1105    mwSize         func_len                 = 0;
1106    int            function                 = 0xFFFF;
1107    int            handle                   = 0xFFFF;
1108    char          *buffer                   = NULL;
[4484]1109    const void    *sample_buffer            = NULL;
[4293]1110    char          *ip_addr                  = NULL;
1111    int            size                     = 0;
1112    int            timeout                  = 0;
1113    int            wait_time                = 0;
1114    int            length                   = 0;
1115    int            port                     = 0;
1116    uint32         num_samples              = 0;
1117    uint32         max_samples              = 0;
1118    uint32         start_sample             = 0;
1119    uint32         num_pkts                 = 0;
1120    uint32         max_length               = 0;
1121    uint32         num_cmds                 = 0;
1122    int            check_chksum             = 0;
1123    uint32         checksum                 = 0;
[4393]1124    double        *checksum_array           = NULL;
[4293]1125    int            hw_ver                   = 0;
[4393]1126    uint32        *buffer_ids               = NULL;
[4484]1127    int            num_buffers              = 0;
[4293]1128    uint32         buffer_id                = 0;
1129    uint32         start_sample_to_request  = 0;
1130    uint32         num_samples_to_request   = 0;
1131    uint32         num_pkts_to_request      = 0;
1132    uint32         useful_rx_buffer_size    = 0;
1133    uint32        *command_args             = NULL;
1134
[4393]1135    uint32         data_type                = 0;
1136    uint32         data_size                = 0;
1137    uint32         mex_data_type            = 0;
[4815]1138
1139    uint32         seq_num                  = 0;
1140    uint32        *seq_num_tracker          = NULL;
1141    char          *seq_num_severity         = NULL;
[4393]1142   
[4815]1143    char          *node_id_str              = NULL;
1144   
[4393]1145    void          *output_array[2]          = {NULL, NULL};
[4484]1146    uint32         num_output_arrays        = 0;
1147    uint32         num_output_data          = 0;
[4293]1148    mwSize         ndim                     = (mwSize) 2;
1149    mwSize         dims[2];
[2150]1150   
[4293]1151   
[4557]1152   
[2150]1153    //--------------------------------------------------------------------
1154    // Initialization   
1155   
1156    if (!initialized) {
1157#ifdef _DEBUG_
1158            printf("Initialization \n");
1159#endif
1160   
1161       // Initialize driver
1162       init_wl_mex_udp_transport();           
1163
1164#ifdef _DEBUG_
1165            printf("END Initialization \n");
1166#endif
1167    }
1168
1169
1170    //--------------------------------------------------------------------
1171    // Process input
1172   
1173    // Check for proper number of arguments
1174    if( nrhs < 1 ) { print_usage(); die(); }
1175
1176    // Input must be a string
1177    if ( mxIsChar( prhs[0] ) != 1 ) {
1178        mexErrMsgTxt("Error: Input must be a string.");
1179    }
1180
1181    // Input must be a row vector
1182    if ( mxGetM( prhs[0] ) != 1 ) {
1183        mexErrMsgTxt("Error: Input must be a row vector.");
1184    }
1185   
1186    // get the length of the input string
1187    func_len = (mwSize) ( mxGetM( prhs[0] ) * mxGetN( prhs[0] ) ) + 1;
1188
1189    // copy the string data from prhs[0] into a C string func
1190    func = mxArrayToString( prhs[0] );
1191   
1192    if( func == NULL ) {
1193        mexErrMsgTxt("Error:  Could not convert input to string.");
1194    }
1195
1196
1197    //--------------------------------------------------------------------
1198    // Process commands
1199   
1200    function = find_transport_function( func, func_len );
1201
1202    switch ( function ) {
1203   
1204        //------------------------------------------------------
1205        // wl_mex_udp_transport('version')
1206        //   - Arguments:
1207        //     - none
1208        //   - Returns:
1209        //     - none
1210        case TRANSPORT_REVISION :
1211#ifdef _DEBUG_
1212            printf("Function TRANSPORT_REVISION: \n");
1213#endif
1214            // Validate arguments
1215            if( nrhs != 1 ) { print_usage(); die(); }
1216
1217            // Call function
1218            if( nlhs == 0 ) { 
1219                print_version(); 
1220            } else if( nlhs == 1 ) { 
1221                plhs[0] = mxCreateString( WL_MEX_UDP_TRANSPORT_VERSION );
1222            } else { 
1223                print_usage(); die(); 
1224            }
1225
1226#ifdef _DEBUG_
1227            printf("END TRANSPORT_REVISION \n");
1228#endif
1229        break;
1230
1231        //------------------------------------------------------
1232        // handle = wl_mex_udp_transport('init_socket')
1233        //   - Arguments:
1234        //     - none
1235        //   - Returns:
1236        //     - handle (int)     - index to the socket
1237        case TRANSPORT_INIT_SOCKET :
1238#ifdef _DEBUG_
1239            printf("Function TRANSPORT_INIT_SOCKET: \n");
1240#endif
1241            // Validate arguments
1242            if( nrhs != 1 ) { print_usage(); die(); }
1243            if( nlhs != 1 ) { print_usage(); die(); }
1244
1245            // Call function
1246            handle = init_socket();
1247           
1248            // Return value to MABLAB
1249            plhs[0] = mxCreateDoubleMatrix(1,1,mxREAL);
1250            *mxGetPr(plhs[0]) = handle;
1251
1252#ifdef _DEBUG_
1253            print_sockets();
1254            printf("END TRANSPORT_INIT_SOCKET \n");
1255#endif
1256        break;
1257
1258        //------------------------------------------------------
1259        // wl_mex_udp_transport('set_so_timeout', handle, timeout)
1260        //   - Arguments:
1261        //     - handle (int)     - index to the requested socket
1262        //     - timeout (int)    - timeout value (in ms); 0 => infinite timeout
1263        //   - Returns:
1264        //     - none
1265        case TRANSPORT_SET_SO_TIMEOUT :
1266#ifdef _DEBUG_
1267            printf("Function : TRANSPORT_SET_SO_TIMEOUT\n");
1268#endif
1269            // Validate arguments
1270            if( nrhs != 3 ) { print_usage(); die(); }
1271            if( nlhs != 0 ) { print_usage(); die(); }
1272
1273            // Get input arguments
1274            handle  = (int) mxGetScalar(prhs[1]);
1275            timeout = (int) mxGetScalar(prhs[2]);
1276
1277            // Call function
1278            set_so_timeout( handle, timeout );
1279       
1280#ifdef _DEBUG_
1281            print_sockets();
1282            printf("END TRANSPORT_SET_SO_TIMEOUT \n");
1283#endif
1284        break;
1285
1286        //------------------------------------------------------
1287        // wl_mex_udp_transport('set_send_buf_size', handle, size)
1288        //   - Arguments:
1289        //     - handle (int)     - index to the requested socket
1290        //     - size (int)       - size of buffer (in bytes)
1291        //   - Returns:
1292        //     - none
1293        case TRANSPORT_SET_SEND_BUF_SIZE :
1294#ifdef _DEBUG_
1295            printf("Function : TRANSPORT_SET_SEND_BUF_SIZE\n");
1296#endif       
1297            // Validate arguments
1298            if( nrhs != 3 ) { print_usage(); die(); }
1299            if( nlhs != 0 ) { print_usage(); die(); }
1300
1301            // Get input arguments
1302            handle  = (int) mxGetScalar(prhs[1]);
1303            size    = (int) mxGetScalar(prhs[2]);
1304
1305            // Call function
1306            set_send_buffer_size( handle, size );
1307
1308#ifdef _DEBUG_
1309            printf("END TRANSPORT_SET_SEND_BUF_SIZE \n");
1310#endif
1311        break;
1312
1313        //------------------------------------------------------
1314        // size = wl_mex_udp_transport('get_send_buf_size', handle)
1315        //   - Arguments:
1316        //     - handle (int)     - index to the requested socket
1317        //   - Returns:
1318        //     - size (int)       - size of buffer (in bytes)
1319        case TRANSPORT_GET_SEND_BUF_SIZE :
1320#ifdef _DEBUG_
1321            printf("Function : TRANSPORT_GET_SEND_BUF_SIZE\n");
1322#endif
1323            // Validate arguments
1324            if( nrhs != 2 ) { print_usage(); die(); }
1325            if( nlhs != 1 ) { print_usage(); die(); }
1326           
1327            // Get input arguments
1328            handle  = (int) mxGetScalar(prhs[1]);
1329
1330            // Call function
1331            size = get_send_buffer_size( handle );
1332           
1333            // Return value to MABLAB
1334            plhs[0] = mxCreateDoubleMatrix(1,1,mxREAL);
1335            *mxGetPr(plhs[0]) = size;
1336       
1337#ifdef _DEBUG_
1338            printf("END TRANSPORT_GET_SEND_BUF_SIZE \n");
1339#endif
1340        break;
1341       
1342        //------------------------------------------------------
1343        // wl_mex_udp_transport('set_rcvd_buf_size', handle, size)
1344        //   - Arguments:
1345        //     - handle (int)     - index to the requested socket
1346        //     - size (int)       - size of buffer (in bytes)
1347        //   - Returns:
1348        //     - none
1349        case TRANSPORT_SET_RCVD_BUF_SIZE :
1350#ifdef _DEBUG_
1351            printf("Function : TRANSPORT_SET_RCVD_BUF_SIZE\n");
1352#endif
1353            // Validate arguments
1354            if( nrhs != 3 ) { print_usage(); die(); }
1355            if( nlhs != 0 ) { print_usage(); die(); }
1356
1357            // Get input arguments
1358            handle  = (int) mxGetScalar(prhs[1]);
1359            size    = (int) mxGetScalar(prhs[2]);
1360
1361            // Call function
1362            set_receive_buffer_size( handle, size );
1363           
1364#ifdef _DEBUG_
1365            printf("END TRANSPORT_SET_RCVD_BUF_SIZE \n");
1366#endif
1367        break;
1368       
1369        //------------------------------------------------------
1370        // size = wl_mex_udp_transport('get_rcvd_buf_size', handle)
1371        //   - Arguments:
1372        //     - handle (int)     - index to the requested socket
1373        //   - Returns:
1374        //     - size (int)       - size of buffer (in bytes)
1375        case TRANSPORT_GET_RCVD_BUF_SIZE :
1376#ifdef _DEBUG_
1377            printf("Function : TRANSPORT_GET_RCVD_BUF_SIZE\n");
1378#endif
1379            // Validate arguments
1380            if( nrhs != 2 ) { print_usage(); die(); }
1381            if( nlhs != 1 ) { print_usage(); die(); }
1382           
1383            // Get input arguments
1384            handle  = (int) mxGetScalar(prhs[1]);
1385
1386            // Call function
1387            size = get_receive_buffer_size( handle );
1388           
1389            // Return value to MABLAB
1390            plhs[0] = mxCreateDoubleMatrix(1,1,mxREAL);
1391            *mxGetPr(plhs[0]) = size;
1392       
1393#ifdef _DEBUG_
1394            printf("END TRANSPORT_GET_RCVD_BUF_SIZE \n");
1395#endif
1396        break;
1397
1398        //------------------------------------------------------
1399        // wl_mex_udp_transport('close', handle)
1400        //   - Arguments:
1401        //     - handle (int)     - index to the requested socket
1402        //   - Returns:
1403        //     - none
1404        case TRANSPORT_CLOSE :
1405#ifdef _DEBUG_
1406            printf("Function : TRANSPORT_CLOSE\n");
1407#endif
1408            // Validate arguments
1409            if( nrhs != 2 ) { print_usage(); die(); }
1410            if( nlhs != 0 ) { print_usage(); die(); }
1411
1412            // Get input arguments
1413            handle  = (int) mxGetScalar(prhs[1]);
1414
1415            // Call function if socket not already closed
1416            //   NOTE:  When 'clear all' is executed in Matlab, it will
1417            //     call both the cleanup() function in the Mex that closes
1418            //     all sockets as well as the destroy function on the transport
1419            //     object that will call the wl_mex_udp_transport('close') function
1420            //     so we need to check if the socket is already closed to not
1421            //     print out an erroneous warning.
1422            if ( sockets[handle].handle != INVALID_SOCKET ) {
1423                close_socket( handle );
1424            }
1425       
1426#ifdef _DEBUG_
1427            printf("END TRANSPORT_CLOSE \n");
1428#endif
1429        break;
1430
1431        //------------------------------------------------------
1432        // size = wl_mex_udp_transport('send', handle, buffer, length, ip_addr, port)
1433        //   - Arguments:
1434        //     - handle (int)     - index to the requested socket
1435        //     - buffer (char *)  - Buffer of data to be sent
1436        //     - length (int)     - Length of data to be sent
1437        //     - ip_addr          - IP Address to send data to
1438        //     - port             - Port to send data to
1439        //   - Returns:
1440        //     - size (int)       - size of data sent (in bytes)
1441        case TRANSPORT_SEND :
1442#ifdef _DEBUG_
1443            printf("Function : TRANSPORT_SEND\n");
1444#endif
1445            // Validate arguments
1446            if( nrhs != 6 ) { print_usage(); die(); }
1447            if( nlhs != 1 ) { print_usage(); die(); }
1448           
1449            // Get input arguments
1450            handle  = (int) mxGetScalar(prhs[1]);
1451            length  = (int) mxGetScalar(prhs[3]);
1452            port    = (int) mxGetScalar(prhs[5]);
1453           
1454            // Input must be a string
1455            if ( mxIsChar( prhs[4] ) != 1 ) { mexErrMsgTxt("Error: Input must be a string."); }
1456            if ( mxGetM( prhs[4] ) != 1 ) { mexErrMsgTxt("Error: Input must be a row vector."); }
1457            ip_addr = mxArrayToString( prhs[4] );
1458            if( ip_addr == NULL ) { mexErrMsgTxt("Error:  Could not convert input to string."); }
1459
1460#ifdef _DEBUG_
1461            printf("index = %d, length = %d, port = %d, ip_addr = %s \n", handle, length, port, ip_addr);
1462#endif
1463           
1464            // Input must be an array of uint8
1465            if ( mxIsUint8( prhs[2] ) != 1 ) { mexErrMsgTxt("Error: Input must be an array of uint8"); }
1466            if ( mxGetM( prhs[2] ) != 1 ) { mexErrMsgTxt("Error: Input must be a row vector."); }
1467            buffer = (char *) mxGetData( prhs[2] );
1468            if( buffer == NULL ) { mexErrMsgTxt("Error:  Could not convert input to array of char."); }
1469           
1470            // Call function
1471            size = send_socket( handle, buffer, length, ip_addr, port );
1472           
1473            // Return value to MABLAB
1474            plhs[0] = mxCreateDoubleMatrix(1,1,mxREAL);
1475            *mxGetPr(plhs[0]) = size;
1476
1477            mxFree( ip_addr );
1478
1479#ifdef _DEBUG_
1480            print_buffer( buffer, length );
1481            printf("END TRANSPORT_SEND \n");
1482#endif
1483        break;
1484       
1485        //------------------------------------------------------
1486        // [size, buffer] = wl_mex_udp_transport('receive', handle, length )
1487        //   - Arguments:
1488        //     - handle (int)     - index to the requested socket
1489        //     - length (int)     - length of data to be received (in bytes)
1490        //   - Returns:
1491        //     - buffer (char *)  - Buffer of data received
1492        case TRANSPORT_RECEIVE :
1493                       
1494#ifdef _DEBUG_
1495            printf("Function : TRANSPORT_RECEIVE\n");
1496#endif
1497            // Validate arguments
1498            if( nrhs != 3 ) { print_usage(); die(); }
1499            if( nlhs != 2 ) { print_usage(); die(); }
1500
1501            // Get input arguments
1502            handle  = (int) mxGetScalar(prhs[1]);
1503            length  = (int) mxGetScalar(prhs[2]);
1504
1505#ifdef _DEBUG_
1506            printf("index = %d, length = %d \n", handle, length );
1507#endif
1508
[4818]1509            // Check the length input argument
1510            if(length == 0) { mexErrMsgTxt("Error:  Receive length of 0 not allowed."); }
1511
[2150]1512            // Allocate memory for the buffer
[4818]1513            buffer = (char *) malloc(sizeof(char) * length);
1514            if(buffer == NULL) { mexErrMsgTxt("Error:  Could not allocate receive buffer"); }
1515            for (i = 0; i < length; i++) { buffer[i] = 0; }
[2150]1516
1517            // Call function
[4818]1518            size = receive_socket(handle, length, buffer);
[2150]1519
1520            // Return value to MABLAB
[4818]1521            plhs[0] = mxCreateDoubleMatrix(1, 1, mxREAL);
[2150]1522            *mxGetPr(plhs[0]) = size;
1523           
1524            // Return value to MABLAB
[4818]1525            if (size != 0) {
[2150]1526                plhs[1] = mxCreateNumericMatrix(size, 1, mxUINT8_CLASS, mxREAL);
[4818]1527                if(plhs[1] == NULL) { mexErrMsgTxt("Error:  Could not allocate return buffer"); }           
1528                memcpy((char *) mxGetData(plhs[1]), buffer, size*sizeof(char));
[2150]1529            } else {
1530                plhs[1] = mxCreateNumericMatrix(0, 0, mxUINT8_CLASS, mxREAL);           
1531            }
1532
1533            // Free allocated memory
[4818]1534            free(buffer);
[2150]1535           
1536#ifdef _DEBUG_
[4818]1537            if (size != 0) {
[2150]1538                printf("Buffer size = %d \n", size);
[4818]1539                print_buffer((char *) mxGetData(plhs[1]), size);
[2150]1540            }
1541            printf("END TRANSPORT_RECEIVE \n");
1542#endif
1543        break;
1544
1545
1546        //------------------------------------------------------
1547        //[num_samples, cmds_used, samples]  = wl_mex_udp_transport('read_rssi' / 'read_iq',
1548        //                                        handle, buffer, length, ip_addr, port,
[4393]1549        //                                        number_samples, buffer_ids, start_sample,
[2880]1550        //                                        max_length, num_pkts);
[2150]1551        //   - Arguments:
[4815]1552        //     - handle           (int)          - index to the requested socket
1553        //     - buffer           (char *)       - Buffer of data to be sent
1554        //     - length           (int)          - Length of data to be sent
1555        //     - ip_addr          (char *)       - IP Address to send data to
1556        //     - port             (int)          - Port to send data to
1557        //     - num_samples      (int)          - Number of samples requested
1558        //     - buffer_ids       (int  *)       - List of WARP RF buffer to obtain samples from
1559        //     - start_sample     (int)          - Starting address in the array for the samples
1560        //     - max_length       (int)          - Max number of bytes of samples received per packet
1561        //                                         (last packet may not receive max_length bytes)
1562        //     - num_pkts         (int)          - Number of Ethernet packets it should take to receive the samples
1563        //     - data_type        (int)          - Type of the output array:
1564        //                                             IQ_DATA_TYPE_DOUBLE ==> double / mxDOUBLE_CLASS
1565        //                                             IQ_DATA_TYPE_SINGLE ==> float  / mxSINGLE_CLASS
1566        //                                             IQ_DATA_TYPE_INT16  ==> int16  / mxINT16_CLASS
1567        //                                             IQ_DATA_TYPE_RAW    ==> uint32 / mxUINT32_CLASS
1568        //     - seq_num_tracker  (int  *)       - Sequence number tracker
1569        //     - seq_num_severity (char *)       - Severity of response to sequence number match
1570        //     - node_str_id      (char *)       - Node string ID
[2150]1571        //   - Returns:
[4815]1572        //     - num_samples      (int)          - Number of samples received
1573        //     - cmds_used        (int)          - Number of transport commands used to obtain samples
1574        //     - samples          (double *)     - Array of samples received
[2150]1575        case TRANSPORT_READ_IQ:
1576        case TRANSPORT_READ_RSSI:
1577
1578#ifdef _DEBUG_
[2880]1579            printf("Function : TRANSPORT_READ_IQ / TRANSPORT_READ_RSSI\n");
[2150]1580#endif
[4293]1581
1582            // Example to profile the performance of Read IQ
1583            // printf("times = [%12d, ", wl_timestamp);
1584
[2150]1585            // Validate arguments
[4815]1586            if( nrhs != 15 ) { print_usage(); die(); }
[2150]1587            if( nlhs !=  3 ) { print_usage(); die(); }
1588
1589            // Get input arguments
1590            handle       = (int) mxGetScalar(prhs[1]);
1591            length       = (int) mxGetScalar(prhs[3]);
1592            port         = (int) mxGetScalar(prhs[5]);
1593            num_samples  = (int) mxGetScalar(prhs[6]);
1594            start_sample = (int) mxGetScalar(prhs[8]);
1595            max_length   = (int) mxGetScalar(prhs[9]);
1596            num_pkts     = (int) mxGetScalar(prhs[10]);
[4393]1597            data_type    = (int) mxGetScalar(prhs[11]);
[2150]1598           
1599            // Packet data must be an array of uint8
[4393]1600            if ( mxIsUint8( prhs[2] ) != 1 ) { mexErrMsgTxt("Error: Input buffer must be an array of uint8"); }
1601            if ( mxGetM( prhs[2] ) != 1 ) { mexErrMsgTxt("Error: Input buffer must be a row vector."); }
[2150]1602            buffer = (char *) mxGetData( prhs[2] );
[4393]1603            if( buffer == NULL ) { mexErrMsgTxt("Error:  Could not convert input buffer to array of char."); }
[2150]1604
1605            // IP address input must be a string
[4393]1606            if ( mxIsChar( prhs[4] ) != 1 ) { mexErrMsgTxt("Error: Input IP address must be a string."); }
1607            if ( mxGetM( prhs[4] ) != 1 ) { mexErrMsgTxt("Error: Input IP address must be a row vector."); }
[2150]1608            ip_addr = mxArrayToString( prhs[4] );
[4393]1609            if( ip_addr == NULL ) { mexErrMsgTxt("Error:  Could not convert input IP address to string."); }
[4815]1610
1611            // Sequence tracker must be an array of integers
1612            if ( mxIsUint32( prhs[12] ) != 1 ) { mexErrMsgTxt("Error: Sequence number tracker must be an array of uint32"); }
1613            if ( mxGetM( prhs[12] ) != 1 ) { mexErrMsgTxt("Error: Sequence number tracker must be a row vector."); }
1614            seq_num_tracker = (int *) mxGetData( prhs[12] );
1615            if( seq_num_tracker == NULL ) { mexErrMsgTxt("Error:  Could not convert sequence number tracker to array of uint32."); }
1616
1617            // Sequence number severity must be a string
1618            if ( mxIsChar( prhs[13] ) != 1 ) { mexErrMsgTxt("Error: Sequence number severity must be a string."); }
1619            if ( mxGetM( prhs[13] ) != 1 ) { mexErrMsgTxt("Error: Sequence number severity must be a row vector."); }
1620            seq_num_severity = mxArrayToString( prhs[13] );
1621            if( seq_num_severity == NULL ) { mexErrMsgTxt("Error:  Could not convert sequence number severity to string."); }
1622
1623            // Node ID string must be a string
1624            if ( mxIsChar( prhs[14] ) != 1 ) { mexErrMsgTxt("Error: Node ID string must be a string."); }
1625            if ( mxGetM( prhs[14] ) != 1 ) { mexErrMsgTxt("Error: Node ID string must be a row vector."); }
1626            node_id_str = mxArrayToString( prhs[14] );
1627            if( node_id_str == NULL ) { mexErrMsgTxt("Error:  Could not convert node ID string to string."); }
1628
[4393]1629            // Buffer IDs must be an array of singular buffer IDs
1630            if ( mxIsUint32( prhs[7] ) != 1 ) { mexErrMsgTxt("Error: Input buffer IDs must be an array of uint32"); }
1631            if ( mxGetM( prhs[7] ) != 1 ) { mexErrMsgTxt("Error: Input buffer IDs must be a row vector."); }           
1632            buffer_ids    = (int *) mxGetData( prhs[7] );
1633            if( buffer_ids == NULL ) { mexErrMsgTxt("Error:  Could not convert input buffer IDs to array of uint32."); }
1634           
[4484]1635            num_buffers = (int) mxGetN( prhs[7] );
[2150]1636
[4393]1637            for ( i = 0; i < num_buffers; i++ ) {
1638                buffer_id = buffer_ids[i];
1639               
[4815]1640                if ( !((buffer_id == BUFFER_ID_RFA) || (buffer_id == BUFFER_ID_RFB) || (buffer_id == BUFFER_ID_RFC) || (buffer_id == BUFFER_ID_RFD))) {
[4393]1641                    mexErrMsgTxt("Error:  Buffer selection must be singular.  Use vector notation for reading from multiple buffers e.g. [RFA,RFB]");
1642                }
1643            }
1644           
1645            // Determine data types and sizes based on input data_type
1646            switch (data_type) {
1647                case IQ_DATA_TYPE_DOUBLE:
1648                    mex_data_type     = mxDOUBLE_CLASS;
1649                    data_size         = sizeof(double);                   
1650                break;
1651               
1652                case IQ_DATA_TYPE_SINGLE:
1653                    mex_data_type     = mxSINGLE_CLASS;
1654                    data_size         = sizeof(float);
1655                break;
1656               
1657                case IQ_DATA_TYPE_INT16:
1658                    mex_data_type     = mxINT16_CLASS;
1659                    data_size         = sizeof(int16);
1660                break;
1661               
1662                case IQ_DATA_TYPE_RAW:
1663                    mex_data_type     = mxUINT32_CLASS;
1664                    data_size         = sizeof(uint32);
1665                break;
1666               
1667                default:
1668                    mexErrMsgTxt("Error:  Unsupported output data type");
1669                break;
1670            }
1671           
1672            // Allocate output variables based on the data type
1673            switch (data_type) {
1674                case IQ_DATA_TYPE_DOUBLE:
1675                    // Data allocation scheme uses mxGetPr / mxGetPi vs mxGetData / mxGetImagData
1676                    switch ( function ) {
1677                        case TRANSPORT_READ_IQ:
[4484]1678                            plhs[2] = mxCreateDoubleMatrix(num_samples, num_buffers, mxCOMPLEX);
[4393]1679                            if( plhs[2] == NULL ) { mexErrMsgTxt("Error:  Could not allocate return buffer"); }
[4293]1680
[4484]1681                            output_array[0]   = (void *) mxGetPr(plhs[2]);
1682                            output_array[1]   = (void *) mxGetPi(plhs[2]);
1683                            num_output_arrays = 2;
1684                            num_output_data   = num_samples;
[4393]1685                        break;
1686                       
1687                        case TRANSPORT_READ_RSSI:
[4484]1688                            plhs[2] = mxCreateDoubleMatrix((2 * num_samples), num_buffers, mxREAL);
[4393]1689                            if( plhs[2] == NULL ) { mexErrMsgTxt("Error:  Could not allocate return buffer"); }           
1690
[4484]1691                            output_array[0]   = (void *) mxGetPr(plhs[2]);
1692                            num_output_arrays = 1;
1693                            num_output_data   = 2 * num_samples;
[4393]1694                        break;
1695                    }
[4293]1696                break;
1697               
[4393]1698                case IQ_DATA_TYPE_SINGLE:
1699                case IQ_DATA_TYPE_INT16:
1700                    // Data allocation scheme is the same for 'single', and 'int16'
1701                    switch ( function ) {
1702                        case TRANSPORT_READ_IQ:
1703                            dims[0] = (mwSize) num_samples;
1704                            dims[1] = (mwSize) num_buffers;
[4293]1705
[4393]1706                            plhs[2] = mxCreateNumericArray(ndim, dims, mex_data_type, mxCOMPLEX);
1707                            if( plhs[2] == NULL ) { mexErrMsgTxt("Error:  Could not allocate return buffer"); }
[4293]1708
[4484]1709                            output_array[0]   = mxGetData(plhs[2]);
1710                            output_array[1]   = mxGetImagData(plhs[2]);
1711                            num_output_arrays = 2;
1712                            num_output_data   = num_samples;
[4393]1713                        break;
1714                       
1715                        case TRANSPORT_READ_RSSI:
1716                            dims[0] = (mwSize) (2 * num_samples);
1717                            dims[1] = (mwSize) num_buffers;
1718
1719                            plhs[2] = mxCreateNumericArray(ndim, dims, mex_data_type, mxREAL);
1720                            if( plhs[2] == NULL ) { mexErrMsgTxt("Error:  Could not allocate return buffer"); }           
1721
[4484]1722                            output_array[0]   = mxGetData(plhs[2]);
1723                            num_output_arrays = 1;
1724                            num_output_data   = 2 * num_samples;
[4393]1725                        break;
1726                    }
[4293]1727                break;
1728               
[4393]1729                case IQ_DATA_TYPE_RAW:
1730                    // Allocate a uint32 array for raw data
1731                    dims[0] = (mwSize) num_samples;
1732                    dims[1] = (mwSize) num_buffers;
[4293]1733
[4393]1734                    plhs[2] = mxCreateNumericArray(ndim, dims, mex_data_type, mxREAL);
[4293]1735                    if( plhs[2] == NULL ) { mexErrMsgTxt("Error:  Could not allocate return buffer"); }           
1736
[4484]1737                    output_array[0]   = mxGetData(plhs[2]);
1738                    num_output_arrays = 1;
1739                    num_output_data   = num_samples;
[4293]1740                break;
[4393]1741               
1742                default:
1743                    mexErrMsgTxt("Error:  Unsupported output data type");
1744                break;
[4293]1745            }
[2150]1746           
[4393]1747            plhs[0] = mxCreateDoubleMatrix(1, 1, mxREAL);
1748            plhs[1] = mxCreateDoubleMatrix(1, 1, mxREAL);
[2150]1749           
[2880]1750            // If the default implementation to limit Read IQ request size is not sufficient, then
1751            // the user can override the Read IQ max request size.
1752            //   NOTE:  The request size must be at least max_length so that we don't run in to a corner
1753            //          case of spinning forever requesting zero samples. 
1754            if ( use_user_read_iq_max_req_size == 1 ) {
1755                       
1756                if ( user_read_iq_max_req_size < max_length ) {
1757                    useful_rx_buffer_size = max_length;
1758                } else {
1759                    useful_rx_buffer_size = user_read_iq_max_req_size;
1760                }
1761            } else {
[2150]1762
[2880]1763                // Set the useful RX buffer size to 80% of the RX buffer
1764                //     NOTE:  This is integer division so the rx_buffer_size will be truncated by the divide
1765                //
1766                useful_rx_buffer_size  = 8 * ( sockets[handle].rx_buffer_size / 10 );
1767            }
1768
1769#ifdef _DEBUG_
1770            printf("Useful buffer size = %d (of %d) for %d pkt request\n", useful_rx_buffer_size, sockets[handle].rx_buffer_size, num_pkts);
1771            printf("  Num samples  = %d     Useful buffer samples = %d\n", num_samples, (useful_rx_buffer_size >> 2));
1772#endif
1773
[4393]1774            // Iterate thru all the buffers that have been requested
1775            for (k = 0; k < num_buffers; k++) {
1776           
1777                // Set the buffer ID for this Read IQ
1778                buffer_id = buffer_ids[k];
1779               
1780                // Update the buffer with the correct command arguments since it is too expensive to do in MATLAB
1781                command_args    = (uint32 *) ( buffer + sizeof( wl_transport_header ) + sizeof( wl_command_header ) );
1782                command_args[0] = endian_swap_32( buffer_id );
1783                command_args[3] = endian_swap_32( max_length );
[2150]1784
[4393]1785                // Check to see if we have enough receive buffer space for the requested packets.
1786                // If not, then break the request up in to multiple requests.           
1787                if( num_samples < ( useful_rx_buffer_size >> 2 ) ) {
[2150]1788
[4393]1789                    // Call receive function normally
1790                    command_args[1] = endian_swap_32( start_sample );
1791                    command_args[2] = endian_swap_32( num_samples );
1792                    command_args[4] = endian_swap_32( num_pkts );
[2150]1793
[4393]1794                    // Call function
1795                    size = wl_read_baseband_buffer( handle, buffer, length, ip_addr, port,
[4701]1796                                                    start_sample, num_samples, start_sample, buffer_id, function, data_type,
[4815]1797                                                    output_array, &num_cmds, &seq_num );
[2150]1798
[4393]1799                } else {
[2150]1800
[4393]1801                    // Since we are requesting more data than can fit in to the receive buffer, break this
1802                    // request in to multiple function calls, so we do not hit the timeout functions
[2150]1803
[4393]1804                    // Number of packets that can fit in the receive buffer
1805                    num_pkts_to_request     = useful_rx_buffer_size / max_length;            // RX buffer size in bytes / Max packet size in bytes
1806                   
1807                    // Number of samples in a request (number of samples in a packet * number of packets in a request)
1808                    num_samples_to_request  = (max_length >> 2) * num_pkts_to_request;
1809                   
1810                    // Starting sample
1811                    start_sample_to_request = start_sample;
[2150]1812
[3296]1813#ifdef _DEBUG_
[4393]1814                    printf("  Num pkts per req = %d     Num samples per req = %d\n", num_pkts_to_request, num_samples_to_request);
[3297]1815#endif               
[3296]1816               
[4393]1817                    // Error checking to make sure something bad did not happen
1818                    if ( num_pkts_to_request > num_pkts ) {
1819                        printf("ERROR:  Read IQ / Read RSSI - Parameter mismatch \n");
1820                        printf("    Requested %d packet(s) and %d sample(s) in function call.  \n", num_pkts, num_samples);
1821                        printf("    Receive buffer can hold %d samples (ie %d packets).  \n", num_samples_to_request, num_pkts_to_request);
1822                        printf("    Since, the number of samples requested is greater than what the receive buffer can hold, \n");
1823                        printf("    the number of packets requested should be greater than what the receive buffer can hold. \n");
1824                        die_with_error("Error:  Read IQ / Read RSSI - Parameter mismatch.  See above for debug information.");
1825                    }
[2880]1826
[4393]1827                    for( i = num_pkts; i > 0; i -= num_pkts_to_request ) {
[2150]1828
[4706]1829                        j = i - num_pkts_to_request;
[2880]1830
[4393]1831                        // If we are requesting the last set of packets, then just request the remaining samples
1832                        if ( j < 0 ) {
1833                            num_samples_to_request = num_samples - ((num_pkts - i) * (max_length >> 2));
1834                            num_pkts_to_request    = i;
1835                        }
[3297]1836
[4393]1837                        // Need to set all args here b/c if there is a timeout then the command args in the buffer will
1838                        // be changed to request the missing data.
1839                        command_args[1] = endian_swap_32( start_sample_to_request );
1840                        command_args[2] = endian_swap_32( num_samples_to_request );
1841                        command_args[4] = endian_swap_32( num_pkts_to_request );
[3297]1842
[2880]1843#ifdef _DEBUG_
[4393]1844                        printf("    Req Num pkts = %5d    Num samples = %10d    Start sample = %10d \n", num_pkts_to_request, num_samples_to_request, start_sample_to_request);
[2880]1845#endif
[4293]1846
[4393]1847                        // Call function
1848                        size = wl_read_baseband_buffer( handle, buffer, length, ip_addr, port,
[4701]1849                                                        start_sample, num_samples_to_request, start_sample_to_request, buffer_id, function, data_type,
[4815]1850                                                        output_array, &num_cmds, &seq_num );
[4293]1851
[4393]1852                        start_sample_to_request += num_samples_to_request;
1853                    }
1854                   
1855                    size = num_samples;
[2150]1856                }
1857
[4393]1858                // Do not update the pointers on the last iteration thru the loop
1859                if (k < (num_buffers - 1)) {
1860                    // Update the output array pointers to the next section of samples
[4484]1861                    for (i = 0; i < num_output_arrays; i++) {
1862                        output_array[i] = (void *)(((long long)(output_array[i])) + (long long)(num_output_data * data_size));
[4393]1863                    }
1864                }
[4815]1865               
1866                // Check the sequence number
1867                wl_check_seq_num(function, node_id_str, buffer_id, seq_num, seq_num_tracker, seq_num_severity);
1868               
1869                // Update the sequence number
1870                wl_update_seq_num(function, buffer_id, seq_num, seq_num_tracker);
1871               
[4393]1872            }  // END for each buffer_id
[4557]1873           
[4393]1874            // Return values to MABLAB
[4293]1875            *mxGetPr(plhs[0]) = size;           
[2150]1876            *mxGetPr(plhs[1]) = num_cmds;
1877
[4393]1878            // Process output array
1879            if ( size == 0 ) {
[4293]1880                // Free any allocated arrays
1881                if ( plhs[2] != NULL ) {
1882                    mxDestroyArray( plhs[2] );
1883                }
1884               
[2150]1885                // Return an empty array
1886                plhs[2] = mxCreateDoubleMatrix(0, 0, mxCOMPLEX);
1887            }
1888           
1889            // Free allocated memory
1890            free( ip_addr );
[4293]1891
[2150]1892#ifdef _DEBUG_
1893            printf("END TRANSPORT_READ_IQ \ TRANSPORT_READ_RSSI\n");
1894#endif       
1895        break;       
1896
1897        //------------------------------------------------------
[3297]1898        // [cmds_used, checksum] = wl_mex_udp_transport('write_iq', handle, cmd_buffer, max_length, ip_addr, port,
[4393]1899        //                                              number_samples, samples, buffer_ids, start_sample,
1900        //                                              num_pkts, max_samples, hw_ver, check_chksum, data_type);
[2150]1901        //
1902        //   - Arguments:
1903        //     - handle          (int)      - Index to the requested socket
1904        //     - cmd_buffer      (char *)   - Buffer of data to be used as the header for Write IQ command
1905        //     - max_length      (int)      - Length of max data packet (in bytes)
1906        //     - ip_addr         (char *)   - IP Address to send samples to
1907        //     - port            (int)      - Port to send samples to
1908        //     - num_samples     (int)      - Number of samples to send
[4393]1909        //     - samples         (void *)   - Array of IQ samples to be sent (type determined by data_type)
1910        //     - buffer_ids      (int  *)   - List of WARP RF buffers to write samples to
[2150]1911        //     - start_sample    (int)      - Starting address in the array of samples
1912        //     - num_pkts        (int)      - Number of Ethernet packets it should take to send the samples
1913        //     - max_samples     (int)      - Max number of samples transmitted per packet
1914        //                                    (last packet may not send max_sample samples)
[2166]1915        //     - hw_ver          (int)      - Hardware version;  Since different HW versions have different
[4393]1916        //                                    processing capabilities, we need to know the HW version for timing
[3297]1917        //     - check_chksum    (int)      - Perform the robustness check of the WriteIQ transmission in
1918        //                                    this function or assume the WriteIQ was successful.
[4393]1919        //     - data_type       (int)      - Type of the output array:
1920        //                                        IQ_DATA_TYPE_DOUBLE ==> double / mxDOUBLE_CLASS
1921        //                                        IQ_DATA_TYPE_SINGLE ==> float  / mxSINGLE_CLASS
1922        //                                        IQ_DATA_TYPE_INT16  ==> int16  / mxINT16_CLASS
1923        //                                        IQ_DATA_TYPE_RAW    ==> uint32 / mxUINT32_CLASS
1924        //
[2150]1925        //   - Returns:
1926        //     - cmds_used   (int)  - number of transport commands used to send samples
[3297]1927        //     - checksum    (int)  - WriteIQ checksum calculated by Mex
[4393]1928        //
[2150]1929        case TRANSPORT_WRITE_IQ:
1930
1931#ifdef _DEBUG_
1932            printf("Function : TRANSPORT_WRITE_IQ\n");
1933#endif
1934            // Validate arguments
[3297]1935            if( nrhs != 15 ) { print_usage(); die(); }
1936            if( nlhs !=  2 ) { print_usage(); die(); }
[2150]1937
1938            // Get input arguments
1939            handle       = (int) mxGetScalar(prhs[1]);
1940            max_length   = (int) mxGetScalar(prhs[3]);
1941            port         = (int) mxGetScalar(prhs[5]);
1942            num_samples  = (int) mxGetScalar(prhs[6]);
[4393]1943            start_sample = (int) mxGetScalar(prhs[9]);
1944            num_pkts     = (int) mxGetScalar(prhs[10]);
1945            max_samples  = (int) mxGetScalar(prhs[11]);
1946            hw_ver       = (int) mxGetScalar(prhs[12]);
1947            check_chksum = (int) mxGetScalar(prhs[13]);
1948            data_type    = (int) mxGetScalar(prhs[14]);
1949           
[2150]1950            // Packet data must be an array of uint8
1951            if ( mxIsUint8( prhs[2] ) != 1 ) { mexErrMsgTxt("Error: Command Buffer input must be an array of uint8"); }
1952            if ( mxGetM( prhs[2] ) != 1 ) { mexErrMsgTxt("Error: Command Buffer input must be a row vector."); }
1953            buffer = (char *) mxGetData( prhs[2] );
1954            if( buffer == NULL ) { mexErrMsgTxt("Error:  Could not convert command buffer input to array of char."); }
1955
1956            // IP address input must be a string
1957            if ( mxIsChar( prhs[4] ) != 1 ) { mexErrMsgTxt("Error: IP Address input must be a string."); }
1958            if ( mxGetM( prhs[4] ) != 1 ) { mexErrMsgTxt("Error: IP Address input must be a row vector."); }
1959            ip_addr = mxArrayToString( prhs[4] );
1960            if( ip_addr == NULL ) { mexErrMsgTxt("Error:  Could not convert ip address input to string."); }
[4393]1961
1962            // Buffer IDs must be an array of singular buffer IDs
1963            if ( mxIsUint32( prhs[8] ) != 1 ) { mexErrMsgTxt("Error: Input buffer IDs must be an array of uint32"); }
1964            if ( mxGetM( prhs[8] ) != 1 ) { mexErrMsgTxt("Error: Input buffer IDs must be a row vector."); }           
1965            buffer_ids    = (int *) mxGetData( prhs[8] );
1966            if( buffer_ids == NULL ) { mexErrMsgTxt("Error:  Could not convert input buffer IDs to array of uint32."); }
[2150]1967           
[4484]1968            num_buffers = (int) mxGetN( prhs[8] );
[2150]1969           
[4393]1970            // Sample IQ Buffer
1971            if ( mxGetN( prhs[7] ) != num_buffers ) { mexErrMsgTxt("Error: Sample buffer input must be a column vector."); }
1972            sample_buffer = prhs[7];
1973            if( sample_buffer == NULL ) { mexErrMsgTxt("Error:  Could not convert sample buffer input to array"); }
[2150]1974
[4393]1975#if 0
1976            // Print command arguments   
1977            printf("index = %d, length = %d, port = %d, ip_addr = %s \n", handle, max_length, port, ip_addr);
1978            printf("num_sample = %d, start_sample = %d\n", num_samples, start_sample);
[2150]1979            printf("num_pkts = %d, max_samples = %d \n", num_pkts, max_samples);
[4393]1980            printf("hw_ver = %d, data_type = %d \n", hw_ver, data_type);
[2150]1981#endif
[4393]1982           
1983            // Allocate return variables
[2150]1984            plhs[0] = mxCreateDoubleMatrix(1,1,mxREAL);
[4393]1985           
1986            plhs[1] = mxCreateDoubleMatrix (1, num_buffers, mxREAL);
1987            if( plhs[1] == NULL ) { mexErrMsgTxt("Error:  Could not allocate return buffer"); }           
1988            checksum_array = mxGetPr(plhs[1]);
[3297]1989
[2150]1990           
[4393]1991            // Iterate thru all the buffers that have been requested
1992            for (k = 0; k < num_buffers; k++) {
1993           
1994                // Set the buffer ID for this Write IQ
1995                buffer_id = buffer_ids[k];
1996
1997                // Call function
1998                //     NOTE:  Since we cannot decode the sample buffer in this part of the function,
1999                //        we need to pass down the iteration we are on so that the lower function can
2000                //        know the index into the sample buffer:  (k * num_samples * data_size)
2001                //
2002                size = wl_write_baseband_buffer( handle, buffer, max_length, ip_addr, port,
2003                                                 num_samples, start_sample, sample_buffer, buffer_id, 
2004                                                 num_pkts, max_samples, hw_ver, check_chksum, data_type, k,
2005                                                 &num_cmds, &checksum );
2006
2007                // Check that we actually sent some samples
2008                if ( size == 0 ) {
2009                    mexErrMsgTxt("Error:  Did not send any samples");
2010                }
2011                                                 
2012                // Update the output checksum array
2013                checksum_array[k] = checksum;
[2150]2014            }
[4393]2015
2016            // Return value to MABLAB
2017            *mxGetPr(plhs[0]) = num_cmds;
[2150]2018           
2019            // Free allocated memory
2020            free( ip_addr );
2021           
2022#ifdef _DEBUG_
2023            printf("END TRANSPORT_WRITE_IQ\n");
2024#endif       
2025        break;
2026
2027       
2028        //------------------------------------------------------
[2880]2029        // wl_mex_udp_transport('write_iq_set_pkt_wait_time', wait_time)
2030        //   - Arguments:
2031        //     - wait_time (int) - wait_time value (in us)
2032        //   - Returns:
2033        //     - none
2034        case TRANSPORT_WRITE_IQ_SET_PKT_WAIT_TIME :
2035#ifdef _DEBUG_
2036            printf("Function : TRANSPORT_WRITE_IQ_SET_PKT_WAIT_TIME\n");
2037#endif
2038            // Validate arguments
2039            if( nrhs != 2 ) { print_usage(); die(); }
2040            if( nlhs != 0 ) { print_usage(); die(); }
2041
2042            // Get input arguments
2043            wait_time = (int) mxGetScalar(prhs[1]);
2044
2045            // Set the global variables
2046            use_user_write_iq_wait_time = 1;
2047            user_write_iq_wait_time = wait_time;
2048       
2049#ifdef _DEBUG_
2050            printf("END TRANSPORT_WRITE_IQ_SET_PKT_WAIT_TIME \n");
2051#endif
2052        break;
2053
2054
2055        //------------------------------------------------------
2056        // wl_mex_udp_transport('read_iq_set_max_request_size', size)
2057        //   - Arguments:
2058        //     - size (int) - Request size value (in bytes)
2059        //   - Returns:
2060        //     - none
2061        case TRANSPORT_READ_IQ_SET_MAX_REQUEST_SIZE :
2062#ifdef _DEBUG_
2063            printf("Function : TRANSPORT_READ_IQ_SET_MAX_REQUEST_SIZE\n");
2064#endif
2065            // Validate arguments
2066            if( nrhs != 2 ) { print_usage(); die(); }
2067            if( nlhs != 0 ) { print_usage(); die(); }
2068
2069            // Get input arguments
2070            size = (int) mxGetScalar(prhs[1]);
2071
2072            // Set the global variables
2073            use_user_read_iq_max_req_size = 1;
2074            user_read_iq_max_req_size = size;
2075       
2076#ifdef _DEBUG_
2077            printf("END TRANSPORT_READ_IQ_SET_MAX_REQUEST_SIZE \n");
2078#endif
2079        break;
2080
2081
2082        //------------------------------------------------------
2083        // wl_mex_udp_transport('suppress_iq_warnings')
2084        //   - Arguments:
2085        //     - none
2086        //   - Returns:
2087        //     - none
2088        case TRANSPORT_SUPPRESS_IQ_WARNINGS :
2089#ifdef _DEBUG_
2090            printf("Function : TRANSPORT_SUPPRESS_IQ_WARNINGS\n");
2091#endif
2092            // Validate arguments
2093            if( nrhs != 1 ) { print_usage(); die(); }
2094            if( nlhs != 0 ) { print_usage(); die(); }
2095
2096            // Set the global variables
2097            suppress_iq_warnings = 1;
2098       
2099#ifdef _DEBUG_
2100            printf("END TRANSPORT_SUPPRESS_IQ_WARNINGS \n");
2101#endif
2102        break;
2103
2104
2105        //------------------------------------------------------
[2150]2106        //  Default
2107        //
2108        //  Print error message
2109        //
2110        default:
2111            printf("Error:  Function %s not supported.", func);
2112            mexErrMsgTxt("Error:  Function not supported.");
2113        break; 
2114    }
2115
2116    // Free allocated memory
2117    mxFree( func );
2118   
2119#ifdef _DEBUG_
2120    printf("DONE \n");
2121#endif
2122
[4293]2123    // if ( function == TRANSPORT_READ_IQ) {
2124    //     wl_usleep(5000000);
2125    // }
2126
[2150]2127    // Return back to MATLAB
2128    return;
2129}
2130
2131
2132
2133
2134/*****************************************************************************/
2135//           Additional WL Mex UDP Transport functionality
2136/*****************************************************************************/
2137
2138
[4293]2139/*****************************************************************************/
2140/**
2141*
2142* This function will receive a packet
2143*
2144* @param    index          - Index in to socket structure which will receive samples
2145* @param    buffer         - WARPLab command to request samples
2146* @param    length         - Length (in bytes) of buffer
2147* @param    ip_addr        - IP Address of node to retrieve samples
2148* @param    port           - Port of node to retrieve samples
2149* @param    num_samples    - Number of samples to process (should be the same as the argument in the WARPLab command)
2150* @param    start_sample   - Index of starting sample (should be the same as the agrument in the WARPLab command)
2151* @param    buffer_id      - Which buffer(s) do we need to retrieve samples from
2152* @param    output_array   - Return parameter - array of samples to return
2153* @param    num_cmds       - Return parameter - number of ethernet send commands used to request packets
2154*                                (could be > 1 if there are transmission errors)
2155*
2156* @return   size           - Number of samples processed (also size of output_array)
2157*
2158* @note     This function requires the following pointers:
2159*   Input:
2160*       - Buffer containing command to request samples (char *)
2161*       - IP Address (char *)
2162*       - Array of buffer IDs (uint32 *)
2163*       - Allocated memory to return samples (uint32 *)
2164*       - Location to return the number of send commands used to generate the samples (uint32 *)
2165*
2166*   Output:
2167*       - Number of samples processed
2168*
2169******************************************************************************/
2170int wl_receive_response( int index, 
2171                         char *resp_buffer, int resp_buffer_size, uint32 *resp_time, 
2172                         char *cmd_buffer,  int cmd_buffer_size, char *ip_addr, int port, wl_function_ptr_t cmd_update_callback
2173                       ) {
[2150]2174
[4293]2175    int                      done                = 0;
2176    uint32                   timeout             = 0;
2177    uint32                   num_retries         = 0;
2178    uint32                   num_wait_retries    = 0;
2179    int                      sent_size           = 0;
2180    int                      rcvd_size           = 0;
2181    uint32                   total_cmds          = 0;
[2150]2182
[4293]2183    wl_transport_header     *rcvd_transport_hdr;
2184   
2185
2186    // Try to process the return packet
2187    while ( !done ) {
2188
2189        // If we hit the timeout, then resend the command
2190        if ( timeout >= TRANSPORT_TIMEOUT ) {
2191       
2192            // If we hit the max number of retrys, then abort
2193            if ( num_retries >= TRANSPORT_MAX_RETRY ) {
2194
2195                printf("ERROR:  Exceeded %d retries for current command request\n", TRANSPORT_MAX_RETRY);
2196                die_with_error("Error:  Reached maximum number of retries without a response... aborting.");
2197               
2198            } else {           
2199                // Retransmit the command packet
2200
2201                // See if there are any updates required               
2202                cmd_update_callback( cmd_buffer, cmd_buffer_size, ip_addr, port );
2203               
2204                // Send the packet
2205                sent_size    = send_socket( index, cmd_buffer, cmd_buffer_size, ip_addr, port );
2206                total_cmds  += 1;
2207               
2208                // Update control variables
2209                timeout      = 0;
2210                num_retries += 1;
2211            }
2212        }
2213       
2214        // Receive packet
2215        rcvd_size = receive_socket( index, resp_buffer_size, resp_buffer );
2216
2217        // receive_socket() handles all socket related errors and will only return:
2218        //   - zero if no packet is available
2219        //   - non-zero if packet is available
2220        if ( rcvd_size > 0 ) {
2221            // Decode the ethernet packet
2222            rcvd_transport_hdr  = (wl_transport_header *) resp_buffer;
2223           
2224            // Record the response timeout value
2225            *resp_time   = timeout;
2226
2227            // Reset the timeout regardless of the contents of the packet
2228            timeout      = 0;
2229
2230            // Check the transport header to see if we should wait
2231            if ( (rcvd_transport_hdr->flags & TRANSPORT_HDR_NODE_NOT_READY_FLAG) == TRANSPORT_HDR_NODE_NOT_READY_FLAG ) {
2232           
2233                // Node is not ready; Wait and try again
2234                wl_usleep( TRANSPORT_NOT_READY_WAIT_TIME );               
2235                num_wait_retries += 1;
2236
2237                // Send packet to request samples
2238                sent_size   = send_socket( index, cmd_buffer, cmd_buffer_size, ip_addr, port );
2239                total_cmds += 1;
2240
2241                // Check that we have not spent a "long time" waiting for samples to be ready               
2242                if ( num_wait_retries > TRANSPORT_NOT_READY_MAX_RETRY ) {
2243                    die_with_error("Error:  Timeout waiting for node to be ready.  Please check the node operation.");
2244                }           
2245            } else {
2246                done = 1;
2247            }
2248        }
2249    }
2250   
2251    return total_cmds;
2252}
2253
2254
2255
[2150]2256/*****************************************************************************/
2257/**
2258*
2259* This function will read the baseband buffers and construct the sample array
2260*
2261* @param    index          - Index in to socket structure which will receive samples
2262* @param    buffer         - WARPLab command to request samples
2263* @param    length         - Length (in bytes) of buffer
2264* @param    ip_addr        - IP Address of node to retrieve samples
2265* @param    port           - Port of node to retrieve samples
[4701]2266* @param    initial_offset - Initial offset of the Read IQ request
[2150]2267* @param    num_samples    - Number of samples to process (should be the same as the argument in the WARPLab command)
[4701]2268* @param    start_sample   - Index of starting sample (this changes when "chunking")
[2150]2269* @param    buffer_id      - Which buffer(s) do we need to retrieve samples from
[4393]2270* @param    function       - Function that we are reading data for:
2271*                                Values = [TRANSPORT_READ_IQ, TRANSPORT_READ_RSSI]
2272* @param    data_type      - Type of the output array:
2273*                                IQ_DATA_TYPE_DOUBLE ==> double / mxDOUBLE_CLASS
2274                                 IQ_DATA_TYPE_SINGLE ==> float  / mxSINGLE_CLASS
2275                                 IQ_DATA_TYPE_INT16  ==> short  / mxINT16_CLASS
2276                                 IQ_DATA_TYPE_RAW    ==> uint32 / mxUINT32_CLASS
[2150]2277* @param    output_array   - Return parameter - array of samples to return
2278* @param    num_cmds       - Return parameter - number of ethernet send commands used to request packets
2279*                                (could be > 1 if there are transmission errors)
2280*
2281* @return   size           - Number of samples processed (also size of output_array)
2282*
2283* @note     This function requires the following pointers:
2284*   Input:
2285*       - Buffer containing command to request samples (char *)
2286*       - IP Address (char *)
2287*       - Array of buffer IDs (uint32 *)
2288*       - Allocated memory to return samples (uint32 *)
2289*       - Location to return the number of send commands used to generate the samples (uint32 *)
2290*
2291*   Output:
2292*       - Number of samples processed
2293*
[4416]2294*
2295* @note    BB_READ_IQ / BB_READ_RSSI Packet Format:
2296*
2297* Command Packet
2298*    - cmd_args_32[0]      - Buffer selection (guaranteed to be singular) (uint16)
2299*    - cmd_args_32[1]      - Start sample
2300*    - cmd_args_32[2]      - Total samples in transfer
2301*    - cmd_args_32[3]      - Maximum number of samples per packet
2302*    - cmd_args_32[4]      - Number of packets in transfer
2303*    - cmd_args_32[5]      - IQ ID (uint 8) - Populated at the lower level
2304*
2305* Response Packet
2306*    - resp_args           - Samples:  wl_bb_samp_hdr followed by appropriate samples
2307*
2308*    NOTE:  If the sample header flags == SAMPLE_HDR_FLAG_IQ_NOT_READY, then the "samples"
2309*        after the sample header need to be interpreted in the following manner:
2310*
2311*        - sample[0]       - Tx status
2312*        - sample[1]       - Current Tx read pointer
2313*        - sample[2]       - Tx length
2314*        - sample[3]       - Rx status
2315*        - sample[4]       - Current Rx write pointer
2316*        - sample[5]       - Rx length
2317*
[2150]2318******************************************************************************/
[4393]2319int wl_read_baseband_buffer( int index, char *buffer, int length, char *ip_addr, int port,
[4701]2320                             uint32 initial_offset, uint32 num_samples, uint32 start_sample, uint32 buffer_id, 
[4393]2321                             uint32 function, uint32 data_type,
[4815]2322                             void **output_array, uint32 *num_cmds, uint32 *seq_num) {
[2150]2323
2324    // Variable declaration
[4293]2325    uint32                   i, tmp;
2326    int                      done                = 0;
[2150]2327   
[4293]2328    uint32                   buffer_id_cmd       = 0;
2329    uint32                   start_sample_cmd    = 0;
2330    uint32                   total_sample_cmd    = 0;
2331    uint32                   bytes_per_pkt       = 0;
2332    uint32                   num_pkts            = 0;
[2150]2333   
[4293]2334    uint32                   tmp_eth_buffer_size = 0;
2335    uint32                   samples_per_pkt     = 0;
[2150]2336
[4293]2337    int                      sent_size           = 0;
[2150]2338
[4293]2339    uint32                   rcvd_pkts           = 0;
2340    int                      rcvd_size           = 0;
2341    uint32                   num_rcvd_samples    = 0;
2342    uint32                   sample_num          = 0;
2343    uint32                   sample_size         = 0;
2344    uint8                    sample_flags        = 0;
[4472]2345    uint8                    sample_iq_id        = 0;
[2150]2346   
[4293]2347    uint32                   timeout             = 0;
2348    uint32                   num_retrys          = 0;
2349    uint32                   num_iq_retrys       = 0;
[2150]2350
[4293]2351    uint32                   total_cmds          = 0;
[2150]2352   
[4293]2353    uint32                   err_start_sample    = 0;
2354    uint32                   err_num_samples     = 0;
2355    uint32                   err_num_pkts        = 0;
[2150]2356   
[4370]2357    uint32                   iq_busy_warn        = 1;
[4416]2358    uint32                   wait_time           = 0;
[4370]2359   
[4293]2360    char                    *tmp_eth_buffer;
2361    uint8                   *samples;
[4393]2362
2363    // Variables for the different output types   
2364    double                  *tmp_double_array_0;
2365    double                  *tmp_double_array_1;
2366    double                   tmp_double_val;
2367
2368    float                   *tmp_single_array_0;
2369    float                   *tmp_single_array_1;
2370    float                    tmp_single_val;
[4293]2371   
[4393]2372    int16                   *tmp_int16_array_0;
2373    int16                   *tmp_int16_array_1;
2374   
2375    uint32                  *tmp_uint32_array_0;
[2150]2376
[4293]2377    wl_transport_header     *transport_hdr;
2378    wl_transport_header     *rcvd_transport_hdr;
2379    wl_command_header       *command_hdr;
2380    uint32                  *command_args;
2381    wl_sample_header        *sample_hdr;
2382    wl_sample_tracker       *sample_tracker;
[2150]2383
[4293]2384    // Read statistics
2385    uint32                   total_timeout     = 0;
2386    uint32                   init_timeout      = 0;
2387    uint32                   avg_timeout       = 0;
2388   
[2150]2389    // Compute some constants to be used later
[4293]2390    uint32                   tport_hdr_size    = sizeof( wl_transport_header );
2391    uint32                   cmd_hdr_size      = sizeof( wl_transport_header ) + sizeof( wl_command_header );
2392    uint32                   all_hdr_size      = sizeof( wl_transport_header ) + sizeof( wl_command_header ) + sizeof( wl_sample_header );
[2150]2393
2394    // Initialization
[4416]2395    transport_hdr       = (wl_transport_header *) buffer;
2396    command_hdr         = (wl_command_header   *) ( buffer + tport_hdr_size );
2397    command_args        = (uint32              *) ( buffer + cmd_hdr_size );
[2150]2398   
[4416]2399    buffer_id_cmd       = endian_swap_32( command_args[0] );
2400    start_sample_cmd    = endian_swap_32( command_args[1] );
2401    total_sample_cmd    = endian_swap_32( command_args[2] );
2402    bytes_per_pkt       = endian_swap_32( command_args[3] );         // Command contains payload size; add room for header
2403    num_pkts            = endian_swap_32( command_args[4] );
2404   
2405    tmp_eth_buffer_size = bytes_per_pkt + 100;                       // Command contains payload size; add room for header
2406    samples_per_pkt     = ( bytes_per_pkt >> 2 );                    // Each WARPLab sample is 4 bytes
2407   
2408    // Replace IQ ID with value maintained by the transport
2409    sample_iq_id        = (sample_read_iq_id & 0xFF);
[4472]2410    command_args[5]     = endian_swap_32( sample_iq_id );
[4416]2411   
2412    // Increment read IQ ID (explicitly maintain as a uint8)
2413    sample_read_iq_id   = (sample_read_iq_id + 1) % 0x100;
2414   
[2150]2415#ifdef _DEBUG_
2416    // Print command arguments   
[4293]2417    printf("Read IQ / Read RSSI command\n");
2418    printf("    index = %d, length = %d, port = %d, ip_addr = %s \n", index, length, port, ip_addr);
2419    printf("    num_sample = %d, start_sample = %d, buffer_id = %d \n", num_samples, start_sample, buffer_id);
2420    printf("    bytes_per_pkt = %d;  num_pkts = %d \n", bytes_per_pkt, num_pkts );
2421    // print_buffer( buffer, length );
[2150]2422#endif
2423
2424    // Perform a consistency check to make sure parameters are correct
2425    if ( buffer_id_cmd != buffer_id ) {
2426        printf("WARNING:  Buffer ID in command (%d) does not match function parameter (%d)\n", buffer_id_cmd, buffer_id);
2427    }
2428    if ( start_sample_cmd != start_sample ) {
2429        printf("WARNING:  Starting sample in command (%d) does not match function parameter (%d)\n", start_sample_cmd, start_sample);
2430    }
2431    if ( total_sample_cmd != num_samples ) {
2432        printf("WARNING:  Number of samples requested in command (%d) does not match function parameter (%d)\n", total_sample_cmd, num_samples);
2433    }
2434   
2435    // Malloc temporary buffer to process ethernet packets
[4293]2436    tmp_eth_buffer  = (char *) malloc( sizeof( char ) * tmp_eth_buffer_size );
2437    if( tmp_eth_buffer == NULL ) { die_with_error("Error:  Could not allocate temporary Ethernet packet buffer"); }
[2150]2438   
2439    // Malloc temporary array to track samples that have been received and initialize
2440    sample_tracker = (wl_sample_tracker *) malloc( sizeof( wl_sample_tracker ) * num_pkts );
2441    if( sample_tracker == NULL ) { die_with_error("Error:  Could not allocate sample tracker buffer"); }
2442    for ( i = 0; i < num_pkts; i++ ) { sample_tracker[i].start_sample = 0;  sample_tracker[i].num_samples = 0; }
2443   
2444    // Send packet to request samples
2445    sent_size   = send_socket( index, buffer, length, ip_addr, port );
2446    total_cmds += 1;
2447
2448    // Initialize loop variables
2449    rcvd_pkts = 0;
2450    timeout   = 0;
2451   
2452    // Process each return packet
2453    while ( !done ) {
2454       
2455        // If we hit the timeout, then try to re-request the remaining samples
2456        if ( timeout >= TRANSPORT_TIMEOUT ) {
2457       
2458            // If we hit the max number of retrys, then abort
2459            if ( num_retrys >= TRANSPORT_MAX_RETRY ) {
2460
2461                printf("ERROR:  Exceeded %d retrys for current Read IQ / Read RSSI request \n", TRANSPORT_MAX_RETRY);
2462                printf("    Requested %d samples from buffer %d starting from sample number %d \n", num_samples, buffer_id, start_sample);
2463                printf("    Received %d out of %d packets from node before timeout.\n", rcvd_pkts, num_pkts);
2464                printf("    Please check the node and look at the ethernet traffic to isolate the issue. \n");               
2465           
2466                die_with_error("Error:  Reached maximum number of retrys without a response... aborting.");
2467               
2468            } else {
2469
2470                // NOTE:  We print a warning here because the Read IQ / Read RSSI case in the mex function above
2471                //        will split Read IQ / Read RSSI requests based on the receive buffer size.  Therefore,
[4293]2472                //        any timeouts we receive here should be legitimate issues that should be explored.
[2150]2473                //
[2880]2474                if ( suppress_iq_warnings == 0 ) {
2475                    printf("WARNING:  Read IQ / Read RSSI request timed out.  Retrying remaining samples. \n");
2476                    printf("          If this message occurs frequently, please adjust the Read IQ \n");
2477                    printf("          maximum request size (in bytes) for the transport using the \n");
2478                    printf("          M code function:  \n");
2479                    printf("              wl_mex_udp_transport('read_iq_set_max_request_size', size)  \n");
[3296]2480                    printf("          Defaults to 80 percent of the receive buffer allocated by the OS. \n");
[2880]2481                    printf("\n          To suppress all IQ warnings for the transport use the M code function: \n");
2482                    printf("              wl_mex_udp_transport('suppress_iq_warnings')\n");
2483                }
[2150]2484           
2485                // Find the first packet error and request the remaining samples
2486                if ( wl_read_iq_find_error( sample_tracker, num_samples, start_sample, rcvd_pkts, samples_per_pkt,
2487                                            &err_num_samples, &err_start_sample, &err_num_pkts ) ) {
2488                   
2489                    command_args[1] = endian_swap_32( err_start_sample );
2490                    command_args[2] = endian_swap_32( err_num_samples );
2491                    command_args[4] = endian_swap_32( num_pkts - ( rcvd_pkts - err_num_pkts ) );
2492
2493                    // Since there was an error in the packets we have already received, then we need to adjust rcvd_pkts
2494                    rcvd_pkts        -= err_num_pkts;
2495                    num_rcvd_samples  = num_samples - err_num_samples;
[4293]2496
[2150]2497                } else {
2498                    // If we did not find an error, then the first rcvd_pkts are correct and we should request
2499                    //   the remaining packets
2500                    command_args[1] = endian_swap_32( err_start_sample );
2501                    command_args[2] = endian_swap_32( err_num_samples );
2502                    command_args[4] = endian_swap_32( num_pkts - rcvd_pkts );
2503                }
2504
2505                // Retransmit the read IQ request packet
2506                sent_size   = send_socket( index, buffer, length, ip_addr, port );
[4293]2507                total_cmds += 1;
[2150]2508               
2509                // Update control variables
2510                timeout     = 0;
2511                num_retrys += 1;
2512            }
2513        }
2514       
[4293]2515        // Receive packet
2516        rcvd_size = receive_socket( index, tmp_eth_buffer_size, tmp_eth_buffer );
[2150]2517
[4293]2518        // receive_socket() handles all socket related errors and will only return:
[2150]2519        //   - zero if no packet is available
2520        //   - non-zero if packet is available
2521        if ( rcvd_size > 0 ) {
[4293]2522            // Decode the Ethernet packet
2523            rcvd_transport_hdr  = (wl_transport_header *) tmp_eth_buffer;
2524            sample_hdr          = (wl_sample_header    *) (tmp_eth_buffer + cmd_hdr_size);
[2150]2525
[4293]2526            // Decode the sample header
[4701]2527            sample_num          = endian_swap_32( sample_hdr->start ) - initial_offset;
[4293]2528            sample_size         = endian_swap_32( sample_hdr->num_samples );
2529            sample_flags        = sample_hdr->flags;
2530           
[2150]2531#ifdef _DEBUG_
[4293]2532            // Record the timeout value for statistics
2533            if ( rcvd_pkts == 0 ) {
2534                init_timeout   = timeout;
2535            } else {
2536                total_timeout += timeout;
2537            }
[2150]2538           
[4293]2539            // Print information about the last 10 packets
2540            if ((rcvd_pkts > (num_pkts - 10)) || (num_pkts < 10 )) {
2541                printf("num_sample = %d, start_sample = %d   %08x  %08x %08x %08x\n", sample_size, sample_num, sample_hdr->num_samples, sample_size, sample_hdr->start, sample_num);
[2150]2542            }
[4293]2543#endif
[2150]2544
[4293]2545            // Reset the timeout regardless of the contents of the packet
2546            timeout = 0;
2547
2548            // Check the sample header flags
2549            if ((sample_flags & SAMPLE_IQ_ERROR) == SAMPLE_IQ_ERROR) {
2550                // Error in samples print error message and return
[4416]2551                die_with_error("Error:  Node returned 'SAMPLE_IQ_ERROR'.  Check that node is not currently transmitting in continuous TX mode.");
[2150]2552           
[4293]2553            } else if ((sample_flags & SAMPLE_IQ_NOT_READY) == SAMPLE_IQ_NOT_READY) {
[4370]2554                if ( iq_busy_warn ) {
[4688]2555                    printf("WARNING:  Node was not ready to process Read IQ request.  Waiting to request again.\n");
[4706]2556                    printf("    This warning can be removed by waiting until the node is not busy with a TX or RX \n");
2557                    printf("    operation.  To do this, please add 'pause(1.5 * NUM_SAMPLES * 1/(40e6));' after\n");
2558                    printf("    any triggers and before the Read IQ request.\n\n");
[4370]2559                    iq_busy_warn = 0;
2560                }
[4416]2561               
2562                wait_time = wl_compute_sample_wait_time((uint32 *)(tmp_eth_buffer + all_hdr_size));
2563               
2564                // Wait until the samples should be done
2565                if ( wait_time != 0 ) {
2566                    wl_usleep( wait_time + 100 );
2567                }
2568               
[4293]2569                num_iq_retrys += 1;
2570               
2571                // Send packet to request samples
2572                sent_size   = send_socket( index, buffer, length, ip_addr, port );
2573                total_cmds += 1;
[2150]2574
[4293]2575                if ( sent_size != length ) {
2576                    die_with_error("Error:  Size of packet sent to request samples does not match length of packet.");
2577                }
2578
2579                // Check that we have not spent a "long time" waiting for samples to be ready               
2580                if ( num_iq_retrys > SAMPLE_IQ_MAX_RETRY ) {
2581                    die_with_error("Error:  Timeout waiting for node to return samples.  Please check the node operation.");
2582                }
2583            } else {
2584                // Normal IQ data
2585               
2586                // Set a pointer to the sample data
2587                samples      = (uint8 *) ( tmp_eth_buffer + all_hdr_size );
2588               
2589                // If we are tracking packets, record which samples have been received
[4701]2590                sample_tracker[rcvd_pkts].start_sample = sample_num + initial_offset;
[4293]2591                sample_tracker[rcvd_pkts].num_samples  = sample_size;
2592               
[4393]2593                // Place samples in the array (Ethernet packet is uint8 big endian, output array is various types little endian)
2594                //   NOTE: Need to process samples in the correct order
2595                //   NOTE: Need to process differently based on the data type
[4293]2596               
[4393]2597                switch (data_type) {
2598                    // ------------------------------------------------------------------------------------------------
2599                    case IQ_DATA_TYPE_DOUBLE:
2600                        // Need to process the Ethernet packet into a double precision floating point array
2601                        //    NOTE:  This performs an endian swap (big to little) on both IQ and RSSI data
2602                        //    NOTE:  This will convert IQ data from a UFix_16_0 to a Fix_16_15
2603                        //    NOTE:  This will unpack the RSSI sample
2604                        //
2605                        switch ( function ) {
2606                            case TRANSPORT_READ_IQ:
2607                                tmp_double_array_0 = (double *) output_array[0];
2608                                tmp_double_array_1 = (double *) output_array[1];
2609                               
2610                                for( i = 0; i < (4 * sample_size); i += 4 ) {
2611                                    tmp = sample_num + (i / 4);
2612                                   
2613                                    // Unpack the WARPLab IQ sample
2614                                    //   NOTE:  This performs a conversion from an UFix_16_0 to a Fix_16_15
2615                                    //      Process:
2616                                    //          1) Treat the 16 bit unsigned value as a 16 bit two's compliment signed value
2617                                    //          2) Divide by range / 2 to move the decimal point so resulting value is between +/- 1
2618                                   
2619                                    // I samples
2620                                    tmp_double_val = (double) ((int16)((samples[i    ] << 8) | (samples[i + 1])));
2621                                    tmp_double_array_0[tmp] = ( tmp_double_val / 0x8000 );
2622                                   
2623                                    // Q samples
2624                                    tmp_double_val = (double) ((int16)((samples[i + 2] << 8) | (samples[i + 3])));
2625                                    tmp_double_array_1[tmp] = ( tmp_double_val / 0x8000 );
2626                                }
2627                            break;
2628                           
2629                            case TRANSPORT_READ_RSSI:
2630                                tmp_double_array_0 = (double *) output_array[0];
2631                               
2632                                for( i = 0; i < (4 * sample_size); i += 4 ) {
2633                                    tmp = (sample_num + (i / 4)) * 2;
2634                                   
2635                                    // Unpack the WARPLab RSSI sample
2636                                    //   NOTE:  This will place the packed 12 bit RSSI samples in the output array
2637                                    tmp_double_array_0[tmp    ] = (double)(((samples[i    ] << 8) | (samples[i + 1])) & 0x03FF);
2638                                    tmp_double_array_0[tmp + 1] = (double)(((samples[i + 2] << 8) | (samples[i + 3])) & 0x03FF);
2639                                }
2640                            break;
2641                           
2642                            default:
2643                                printf("ERROR:  Unsupported function for read_buffers in MEX transport\n");
2644                            break;
2645                        }
2646                    break;
[2150]2647                   
[4393]2648                    // ------------------------------------------------------------------------------------------------
2649                    case IQ_DATA_TYPE_SINGLE:
2650                        // Need to process the Ethernet packet into a single precision floating point array
2651                        //     NOTE:  This is exactly the same processing as the 'double' case but using float
2652                        //            instead of double for the output type
2653                        //
2654                        switch ( function ) {
2655                            case TRANSPORT_READ_IQ:
2656                                tmp_single_array_0 = (float *) output_array[0];
2657                                tmp_single_array_1 = (float *) output_array[1];
2658                               
2659                                for( i = 0; i < (4 * sample_size); i += 4 ) {
2660                                    tmp = sample_num + (i / 4);
2661                                   
2662                                    // Unpack the WARPLab IQ sample
2663                                    //   NOTE:  This performs a conversion from an UFix_16_0 to a Fix_16_15
2664                                    //      Process:
2665                                    //          1) Treat the 16 bit unsigned value as a 16 bit two's compliment signed value
2666                                    //          2) Divide by range / 2 to move the decimal point so resulting value is between +/- 1
2667                                   
2668                                    // I samples
2669                                    tmp_single_val = (float) ((int16)((samples[i    ] << 8) | (samples[i + 1])));
2670                                    tmp_single_array_0[tmp] = ( tmp_single_val / 0x8000 );
2671                                   
2672                                    // Q samples
2673                                    tmp_single_val = (float) ((int16)((samples[i + 2] << 8) | (samples[i + 3])));
2674                                    tmp_single_array_1[tmp] = ( tmp_single_val / 0x8000 );
2675                                }
2676                            break;
2677                           
2678                            case TRANSPORT_READ_RSSI:
2679                                tmp_single_array_0 = (float *) output_array[0];
2680                               
2681                                for( i = 0; i < (4 * sample_size); i += 4 ) {
2682                                    tmp = (sample_num + (i / 4)) * 2;
2683                                   
2684                                    // Unpack the WARPLab RSSI sample
2685                                    //   NOTE:  This will place the packed 12 bit RSSI samples in the output array
2686                                    tmp_single_array_0[tmp    ] = (float)(((samples[i    ] << 8) | (samples[i + 1])) & 0x03FF);
2687                                    tmp_single_array_0[tmp + 1] = (float)(((samples[i + 2] << 8) | (samples[i + 3])) & 0x03FF);
2688                                }
2689                            break;
2690                           
2691                            default:
2692                                printf("ERROR:  Unsupported function for read_buffers in MEX transport\n");
2693                            break;
[4293]2694                        }
2695                    break;
[2150]2696                   
[4393]2697                    // ------------------------------------------------------------------------------------------------
2698                    case IQ_DATA_TYPE_INT16:
2699                        // Need to process the Ethernet packet into a int16 array
2700                        //    NOTE:  This performs an endian swap (big to little) on both IQ and RSSI data
2701                        //    NOTE:  This will convert IQ data from a UFix_16_0 to a Fix_16_0
2702                        //    NOTE:  This will unpack the RSSI sample
2703                        //
2704                        switch ( function ) {
2705                            case TRANSPORT_READ_IQ:
2706                                tmp_int16_array_0 = (int16 *) output_array[0];
2707                                tmp_int16_array_1 = (int16 *) output_array[1];
2708                               
2709                                for( i = 0; i < (4 * sample_size); i += 4 ) {
2710                                    tmp = sample_num + (i / 4);
2711                                   
2712                                    // Unpack the WARPLab IQ sample
2713                                    //   NOTE:  This performs a conversion from an UFix_16_0 to a Fix_16_0
2714                                    //      Process:
2715                                    //          1) Treat the 16 bit unsigned value as a 16 bit two's compliment signed value
2716                                   
2717                                    // I samples
2718                                    tmp_int16_array_0[tmp] = (int16)((samples[i    ] << 8) | (samples[i + 1]));
2719                                   
2720                                    // Q samples
2721                                    tmp_int16_array_1[tmp] = (int16)((samples[i + 2] << 8) | (samples[i + 3]));
2722                                }
2723                            break;
[4293]2724                           
[4393]2725                            case TRANSPORT_READ_RSSI:
2726                                tmp_int16_array_0 = (int16 *) output_array[0];
2727                               
2728                                for( i = 0; i < (4 * sample_size); i += 4 ) {
2729                                    tmp = (sample_num + (i / 4)) * 2;
2730                                   
2731                                    // Unpack the WARPLab RSSI sample
2732                                    //   NOTE:  This will place the packed 12 bit RSSI samples in the output array
2733                                    tmp_int16_array_0[tmp    ] = (int16)(((samples[i    ] << 8) | (samples[i + 1])) & 0x03FF);
2734                                    tmp_int16_array_0[tmp + 1] = (int16)(((samples[i + 2] << 8) | (samples[i + 3])) & 0x03FF);
2735                                }
2736                            break;
[4293]2737                           
[4393]2738                            default:
2739                                printf("ERROR:  Unsupported function for read_buffers in MEX transport\n");
2740                            break;
[4293]2741                        }
2742                    break;
2743                   
[4393]2744                    // ------------------------------------------------------------------------------------------------
2745                    case IQ_DATA_TYPE_RAW:
2746                        // Need to process the Ethernet packet into a uint32 array
2747                        //    NOTE:  This performs an endian swap (big to little) on both IQ and RSSI data
2748                        //    NOTE:  No other processing is done on the data
2749                        //
2750                        switch ( function ) {
2751                            case TRANSPORT_READ_IQ:
2752                            case TRANSPORT_READ_RSSI:
2753                                tmp_uint32_array_0 = (uint32 *) output_array[0];
2754                               
2755                                for( i = 0; i < (4 * sample_size); i += 4 ) {
2756                                    tmp_uint32_array_0[ sample_num + (i / 4) ] = (uint32) ( (samples[i] << 24) | (samples[i + 1] << 16) | (samples[i + 2] << 8) | (samples[i + 3]) );
2757                                }
2758                            break;
2759                           
2760                            default:
2761                                printf("ERROR:  Unsupported function for read_buffers in MEX transport\n");
2762                            break;
[4293]2763                        }
2764                    break;
2765                   
[4393]2766                    // ------------------------------------------------------------------------------------------------
[4293]2767                    default:
[4393]2768                        mexErrMsgTxt("Error:  Unsupported output data type");
[4293]2769                    break;
2770                }
[4393]2771   
[4293]2772                num_rcvd_samples += sample_size;
2773                rcvd_pkts        += 1;
2774                num_iq_retrys     = 0;
2775               
2776                // Exit the loop when we have enough packets
2777                if ( rcvd_pkts == num_pkts ) {
2778               
2779#ifdef _DEBUG_
2780                    // Calculate statistics
2781                    if (num_pkts > 1) {
2782                        avg_timeout = total_timeout / (num_pkts - 1);
[2150]2783                    } else {
[4293]2784                        avg_timeout = init_timeout;
2785                    }
2786                   
2787                    // Print statistics
2788                    printf("Initial Timeout = %10d\n", init_timeout);
2789                    printf("Avg Timeout     = %10d  (%10d packets)\n", avg_timeout, (num_pkts - 1));
2790#endif
2791               
2792                    // Check to see if we have any packet errors
2793                    //     NOTE:  This check will detect duplicate packets or sample indexing errors
2794                    if ( wl_read_iq_sample_error( sample_tracker, num_samples, start_sample, rcvd_pkts, samples_per_pkt ) ) {
[2150]2795
[4293]2796                        // In this case, there is probably some issue in the transmitting node not getting the
2797                        // correct number of samples or messing up the indexing of the transmit packets. 
2798                        // The wl_read_iq_sample_error() printed debug information, so we need to retry the packets
2799                       
2800                        if ( num_retrys >= TRANSPORT_MAX_RETRY ) {
2801                       
2802                            die_with_error("Error:  Errors in sample request from board.  Max number of re-transmissions reached.  See above for debug information.");
2803                           
2804                        } else {
[2150]2805
[4293]2806                            // Find the first packet error and request the remaining samples
2807                            if ( wl_read_iq_find_error( sample_tracker, num_samples, start_sample, rcvd_pkts, samples_per_pkt,
2808                                                        &err_num_samples, &err_start_sample, &err_num_pkts ) ) {
[2150]2809
[4293]2810                                command_args[1] = endian_swap_32( err_start_sample );
2811                                command_args[2] = endian_swap_32( err_num_samples );
2812                                command_args[4] = endian_swap_32( num_pkts - ( rcvd_pkts - err_num_pkts ) );
2813                                // command_args[4] = endian_swap_32( err_num_pkts );
2814
2815                                // Retransmit the read IQ request packet
2816                                sent_size   = send_socket( index, buffer, length, ip_addr, port );
2817                               
2818                                if ( sent_size != length ) {
2819                                    die_with_error("Error:  Size of packet sent to request samples does not match length of packet.");
2820                                }
2821                               
2822                                // We are re-requesting err_num_pkts, so we need to subtract err_num_pkts from what we have already recieved
2823                                rcvd_pkts        -= err_num_pkts;
2824                                num_rcvd_samples  = num_samples - err_num_samples;
2825
2826                                // Update control variables
2827                                timeout     = 0;
2828                                total_cmds += 1;
2829                                num_retrys += 1;
2830                               
2831                            } else {
2832                                // Die since we could not find the error
2833                                die_with_error("Error:  Encountered error in sample packets but could not determine the error.  See above for debug information.");
[2150]2834                            }
2835                        }
[4293]2836                    } else {
2837                        // There are no errors, so we are done
[4815]2838                        //     Record sequence number and exit the function
2839                        *seq_num = sample_hdr->sample_iq_id;
2840                       
[4293]2841                        done = 1;
[2150]2842                    }
2843                }
[4293]2844            }  // END if (sample_flags)
2845        } else {       
2846            // Increment the timeout counter; Note this counter does not reflect real-time
[2150]2847            timeout += 1;           
2848           
2849        }  // END if ( rcvd_size > 0 )
2850       
2851    }  // END while( !done )
2852
2853   
2854    // Free locally allocated memory   
[4293]2855    free( tmp_eth_buffer );   
[2150]2856    free( sample_tracker ); 
2857
2858    // Finalize outputs   
2859    *num_cmds  += total_cmds;
2860   
2861    return num_rcvd_samples;
2862}
2863
2864
2865
2866/*****************************************************************************/
2867/**
2868*  Function:  Read IQ sample check
2869*
[4416]2870*  Function to check if we received all the samples at the correct indexes
[2150]2871*
2872*  Returns:  0 if no errors
2873*            1 if if there is an error and prints debug information
2874*
2875******************************************************************************/
[2185]2876int wl_read_iq_sample_error( wl_sample_tracker *tracker, uint32 num_samples, uint32 start_sample, uint32 num_pkts, uint32 max_sample_size ) {
[2150]2877
[2880]2878    uint32 i;
2879    uint32 start_sample_total;
[2150]2880
[2880]2881    uint32 num_samples_sum  = 0;
2882    uint32 start_sample_sum = 0;
[2150]2883
2884    // Compute the value of the start samples:
2885    //   We know that the start samples should follow the pattern:
2886    //       [ x, (x + y), (x + 2y), (x + 3y), ... , (x + (N - 1)y) ]
2887    //   where x = start_sample, y = max_sample_size, and N = num_pkts.  This is due
2888    //   to the fact that the node will fill all packets completely except the last packet.
2889    //   Therefore, the sum of all element in that array is:
[4416]2890    //       (N * x) + ((N * (N - 1) * Y) / 2
[4701]2891    //
[6295]2892    start_sample_total = ((uint64_t)num_pkts * start_sample) + ((((uint64_t)num_pkts * (num_pkts - 1)) * max_sample_size) >> 1);
[2150]2893   
2894    // Compute the totals using the sample tracker
2895    for( i = 0; i < num_pkts; i++ ) {
2896   
2897        num_samples_sum  += tracker[i].num_samples;
2898        start_sample_sum += tracker[i].start_sample;
2899    }
2900
2901    // Check the totals
2902    if ( ( num_samples_sum != num_samples ) || ( start_sample_sum != start_sample_total ) ) {
2903   
[2880]2904        if ( suppress_iq_warnings == 0 ) {
2905            // Print warning debug information if there is an error
2906            if ( num_samples_sum != num_samples ) { 
2907                printf("WARNING:  Number of samples received (%d) does not equal number of samples requested (%d).  \n", num_samples_sum, num_samples);
2908            } else {
[4293]2909                printf("WARNING:  Sample packet indexes not correct.  Expected the sum of sample indexes to be \n");
[2880]2910                printf("          (%d) but received a sum of (%d).  Retrying ...\n", start_sample_total, start_sample_sum);
2911            }
[4293]2912            // printf("\n          To suppress all IQ warnings for the transport use the M code function: \n");
2913            // printf("              wl_mex_udp_transport('suppress_iq_warnings')\n");
[2880]2914           
2915            // Print debug information
2916            printf("Packet Tracking Information: \n");
2917            printf("    Requested Samples:  Number: %8d    Start Sample: %8d  \n", num_samples, start_sample);
2918            printf("    Received  Samples:  Number: %8d  \n", num_samples_sum);
2919           
[4293]2920            // for ( i = 0; i < num_pkts; i++ ) {
2921            //     printf("         Packet[%4d]:   Number: %8d    Start Sample: %8d  \n", i, tracker[i].num_samples, tracker[i].start_sample );
2922            // }
[2150]2923        }
2924
2925        return 1;
2926    } else {
2927        return 0;
2928    }
2929}
2930
2931
2932
2933/*****************************************************************************/
2934/**
2935*  Function:  Read IQ find first error
2936*
2937*  Function to check if we received all the samples at the correct indecies.
2938*  This will also update the ret_* parameters to indicate values to use when
2939*  requesting a new set of packets.
2940*
2941*  Returns:  0 if no error was found
2942*            1 if an error was found
2943*
2944******************************************************************************/
[2185]2945int wl_read_iq_find_error( wl_sample_tracker *tracker, uint32 num_samples, uint32 start_sample, uint32 num_pkts, uint32 max_sample_size,
2946                           uint32 *ret_num_samples, uint32 *ret_start_sample, uint32 *ret_num_pkts ) {
[2150]2947
[2880]2948    uint32 i, j;
[2150]2949
[2880]2950    uint32 value_found;
2951    uint32 return_value            = 1;
2952    uint32 start_sample_to_request = start_sample;
2953    uint32 num_samples_left        = num_samples;
2954    uint32 num_pkts_left           = num_pkts;
[2150]2955
2956    // Iterate thru the array to find the first index that does not match the expected value
2957    //     NOTE:  This is performing a naive search and could be optimized.  Since we are
2958    //         already in an error condition, we chose simplicity over performance.
2959   
2960    for( i = 0; i < num_pkts; i++ ) {
2961   
2962        value_found = 0;
2963   
2964        // Find element in the array   
2965        for ( j = 0; j < num_pkts; j ++ ) {
2966            if ( start_sample_to_request == tracker[i].start_sample ) {
2967                value_found = 1;
2968            }           
2969        }
2970
[4293]2971        // If we did not find the value, then exit the loop
[2150]2972        if ( !value_found ) {
2973            break;
2974        }
2975       
2976        // Update element to search for
2977        start_sample_to_request += max_sample_size;
2978        num_samples_left        -= max_sample_size;
2979        num_pkts_left           -= 1;
2980    }
2981
2982    // Set the return value
2983    if ( num_pkts_left == 0 ) {
2984        return_value  = 0;
2985    } 
2986       
2987    // Return parameters
2988    *ret_start_sample = start_sample_to_request;
2989    *ret_num_samples  = num_samples_left;
2990    *ret_num_pkts     = num_pkts_left;
2991   
2992    return return_value;
2993}
2994
2995
2996
2997/*****************************************************************************/
2998/**
[4815]2999*  Function:  Sequence Number tracker
3000*
3001*  Functions to update and check the sequence numbers.
3002*
3003*  Returns:   None
3004*
3005******************************************************************************/
3006void wl_update_seq_num(uint32 function, uint32 buffer_id, uint32 seq_num, uint32 *seq_num_tracker) {
3007
3008    switch ( function ) {
3009        case TRANSPORT_READ_IQ:
3010            if (buffer_id == BUFFER_ID_RFA) { seq_num_tracker[0] = seq_num; }
3011            if (buffer_id == BUFFER_ID_RFB) { seq_num_tracker[2] = seq_num; }
3012            if (buffer_id == BUFFER_ID_RFC) { seq_num_tracker[4] = seq_num; }
3013            if (buffer_id == BUFFER_ID_RFD) { seq_num_tracker[6] = seq_num; }
3014        break;
3015       
3016        case TRANSPORT_READ_RSSI:
3017            if (buffer_id == BUFFER_ID_RFA) { seq_num_tracker[1] = seq_num; }
3018            if (buffer_id == BUFFER_ID_RFB) { seq_num_tracker[3] = seq_num; }
3019            if (buffer_id == BUFFER_ID_RFC) { seq_num_tracker[5] = seq_num; }
3020            if (buffer_id == BUFFER_ID_RFD) { seq_num_tracker[7] = seq_num; }
3021        break;
3022       
3023        default:
3024            printf("ERROR:  Unsupported function for wl_update_seq_num in MEX transport\n");
3025        break;
3026    }
3027   
3028    // printf("Seq Num: %5d %5d %5d %5d %5d %5d %5d %5d\n", seq_num_tracker[0], seq_num_tracker[1], seq_num_tracker[2], seq_num_tracker[3],
3029    //                                                      seq_num_tracker[4], seq_num_tracker[5], seq_num_tracker[6], seq_num_tracker[7]);
3030   
3031}
3032
3033
3034
3035void wl_check_seq_num(uint32 function, char * node_id_str, uint32 buffer_id, uint32 seq_num, uint32 *seq_num_tracker, char *seq_num_severity) {
3036    uint32 seq_num_matches   = 0;
3037    uint32 severity          = 0xFFFF;
3038    char  *function_name     = NULL;
3039    char   iq_function[10]   = "read_iq\0";
3040    char   rssi_function[10] = "read_rssi\0";
3041   
3042   
3043    switch (function) {
3044        case TRANSPORT_READ_IQ:
3045            if ((buffer_id == BUFFER_ID_RFA) && (seq_num_tracker[0] == seq_num)) { seq_num_matches = 1; }
3046            if ((buffer_id == BUFFER_ID_RFB) && (seq_num_tracker[2] == seq_num)) { seq_num_matches = 1; }
3047            if ((buffer_id == BUFFER_ID_RFC) && (seq_num_tracker[4] == seq_num)) { seq_num_matches = 1; }
3048            if ((buffer_id == BUFFER_ID_RFD) && (seq_num_tracker[6] == seq_num)) { seq_num_matches = 1; }
3049           
3050            function_name = iq_function;
3051        break;
3052       
3053        case TRANSPORT_READ_RSSI:
3054            if ((buffer_id == BUFFER_ID_RFA) && (seq_num_tracker[1] == seq_num)) { seq_num_matches = 1; }
3055            if ((buffer_id == BUFFER_ID_RFB) && (seq_num_tracker[3] == seq_num)) { seq_num_matches = 1; }
3056            if ((buffer_id == BUFFER_ID_RFC) && (seq_num_tracker[5] == seq_num)) { seq_num_matches = 1; }
3057            if ((buffer_id == BUFFER_ID_RFD) && (seq_num_tracker[7] == seq_num)) { seq_num_matches = 1; }
3058           
3059            function_name = rssi_function;
3060        break;
3061       
3062        default:
3063            printf("ERROR:  Unsupported function for wl_check_seq_num in MEX transport\n");
3064        break;
3065    }
3066   
3067    // printf("%d:  Buffer %d:  Seq Num = %d matches %d\n", function, buffer_id, seq_num, seq_num_matches);
3068   
3069    // If the current sequence number matches the recorded sequence number, this means
3070    // that the buffer has already been read and the appropriate message should be sent
3071    if (seq_num_matches) {
3072        if (!strcmp(seq_num_severity, SEQ_NUM_MATCH_IGNORE ) && (severity == 0xFFFF)) { severity = 0; }
3073        if (!strcmp(seq_num_severity, SEQ_NUM_MATCH_WARNING) && (severity == 0xFFFF)) { severity = 1; }
3074        if (!strcmp(seq_num_severity, SEQ_NUM_MATCH_ERROR  ) && (severity == 0xFFFF)) { severity = 2; }
3075       
3076        switch (severity) {
3077            case 0:    // Do nothing
3078            break;
3079           
3080            case 1:    // Warning
3081                mexWarnMsgIdAndTxt("WARPLab:MEX_UDP_TRANSPORT", "%s Detected multiple reads of same %s waveform.  If this is unintentional, ensure Rx node triggers are configured correctly.", node_id_str, function_name);
3082            break;
3083           
3084            case 2:    // Error
3085                mexErrMsgIdAndTxt("WARPLab:MEX_UDP_TRANSPORT", "ERROR:  %s Detected multiple reads of same %s waveform.", node_id_str, function_name);
3086            break;
3087           
3088            default:
3089                mexErrMsgIdAndTxt("WARPLab:MEX_UDP_TRANSPORT", "ERROR:  %s Unknown sequence number error severity = %s", node_id_str, seq_num_severity);
3090            break;
3091        }
3092    }
3093}
3094
3095
3096
3097/*****************************************************************************/
3098/**
[2150]3099* This function will write the baseband buffers
3100*
3101* @param    index          - Index in to socket structure which will receive samples
3102* @param    buffer         - WARPLab command (includes transport header and command header)
3103* @param    length         - Length (in bytes) max data packet to send (Ethernet MTU size - Ethernet header)
3104* @param    ip_addr        - IP Address of node to retrieve samples
3105* @param    port           - Port of node to retrieve samples
3106* @param    num_samples    - Number of samples to process (should be the same as the argument in the WARPLab command)
3107* @param    start_sample   - Index of starting sample (should be the same as the agrument in the WARPLab command)
[4393]3108* @param    samples        - Array of IQ samples to be sent
[2150]3109* @param    buffer_id      - Which buffer(s) do we need to send samples to (all dimensionality of buffer_ids is handled by Matlab)
3110* @param    num_pkts       - Number of packets to transfer (precomputed by calling SW)
3111* @param    max_samples    - Max samples to send per packet (precomputed by calling SW)
[2166]3112* @param    hw_ver         - Hardware version of node
[3297]3113* @param    check_chksum   - Perform the robustness check of transmission in this function or assume the WriteIQ was successful.
[4393]3114* @param    data_type      - Data type of IQ samples to be sent
3115* @param    iteration      - Variable to help with indexing into samples array
[2150]3116* @param    num_cmds       - Return parameter - number of ethernet send commands used to request packets
3117*                                (could be > 1 if there are transmission errors)
[3297]3118* @param    checksum       - Return parameter - WriteIQ checksum that was calculated
[2150]3119*
3120* @return   samples_sent   - Number of samples processed
3121*
3122* @note     This function requires the following pointers:
3123*   Input:
3124*       - Buffer containing command header to send samples (char *)
3125*       - IP Address (char *)
3126*       - Buffer containing the samples to be sent (uint32 *)
3127*       - Location to return the number of send commands used to generate the samples (uint32 *)
3128*
3129*   Output:
3130*       - Number of samples processed
3131*
[4416]3132* @note    BB_WRITE_IQ Packet Format:
3133*
3134*       - cmd_args            - Samples:  wl_bb_samp_hdr followed by the
3135*                                   defined number of samples.
3136*
3137*       - resp_args_32[0]     - Status
3138*                                 - CMD_PARAM_SUCCESS
3139*                                 - SAMPLE_HDR_FLAG_IQ_NOT_READY
3140*                                 - SAMPLE_HDR_FLAG_IQ_ERROR
3141*       - resp_args_32[1]     - Current checksum            (ignore if Status != CMD_PARAM_SUCCESS)
3142*       - resp_args_32[2]     - IQ ID                       (ignore if Status != SAMPLE_HDR_FLAG_IQ_NOT_READY)
3143*       - resp_args_32[2]     - Tx status                   (ignore if Status != SAMPLE_HDR_FLAG_IQ_NOT_READY)
3144*       - resp_args_32[3]     - Current Tx read pointer     (ignore if Status != SAMPLE_HDR_FLAG_IQ_NOT_READY)
3145*       - resp_args_32[4]     - Tx length                   (ignore if Status != SAMPLE_HDR_FLAG_IQ_NOT_READY)
3146*       - resp_args_32[5]     - Rx status                   (ignore if Status != SAMPLE_HDR_FLAG_IQ_NOT_READY)
3147*       - resp_args_32[6]     - Current Rx write pointer    (ignore if Status != SAMPLE_HDR_FLAG_IQ_NOT_READY)
3148*       - resp_args_32[7]     - Rx length                   (ignore if Status != SAMPLE_HDR_FLAG_IQ_NOT_READY)
3149*
3150*       NOTE:  When SAMPLE_HDR_FLAG_IQ_NOT_READY is returned, then the host
3151*              can compute the wait time by the following calculation:
3152*                  max(((tx_status) ? (tx_length - tx_read_pointer) : 0), ((rx_status) ? (rx_length - rx_write_pointer) : 0));
3153*
3154*       NOTE:  Node will return SAMPLE_HDR_FLAG_IQ_ERROR if in continuous Tx
3155*              mode since the node will never be ready.
3156*
[2150]3157******************************************************************************/
[4393]3158int wl_write_baseband_buffer( int index, char *buffer, int max_length, char *ip_addr, int port,
[4484]3159                              uint32 num_samples, uint32 start_sample, const void *samples, uint32 buffer_id, uint32 num_pkts, 
[4393]3160                              uint32 max_samples, uint32 hw_ver, uint32 check_chksum, uint32 data_type, uint32 iteration,
[3297]3161                              uint32 *num_cmds, uint32 *checksum ) {
[2150]3162
3163    // Variable declaration
[2880]3164    uint32                i, j;
[4416]3165    int                   done                   = 0;
3166    int                   length                 = 0;
3167    int                   sent_size              = 0;
[4484]3168    uint32                sample_num             = 0;
[4416]3169    int                   offset                 = 0;
3170    int                   need_resp              = 0;
3171    int                   slow_write             = 0;
3172    uint16                transport_flags        = 0;
3173    uint32                timeout                = 0;
3174    int                   buffer_count           = 0;
3175    uint32                mex_data_type          = 0;
3176    uint32                data_size              = 0;
3177    uint8                 sample_iq_id           = 0;
3178    uint32                write_iq_ready_warn    = 1;
[4393]3179   
3180    // Variables to handle different input types
3181    double               *tmp_double_array_real;
3182    double               *tmp_double_array_imag;
[4416]3183    double                tmp_double_real        = 0.0;
3184    double                tmp_double_imag        = 0.0;
[2150]3185
[4393]3186    float                *tmp_single_array_real;
3187    float                *tmp_single_array_imag;
[4416]3188    float                 tmp_single_real        = 0.0;
3189    float                 tmp_single_imag        = 0.0;
[4393]3190   
3191    int16                *tmp_int16_array_real;
3192    int16                *tmp_int16_array_imag;
[4416]3193    int16                 tmp_int16_real         = 0;
3194    int16                 tmp_int16_imag         = 0;
[4393]3195   
3196    uint32               *tmp_uint32_array;
3197   
[4416]3198    uint32                process_imag           = 1;
[4393]3199   
[4416]3200    // Response variables
3201    uint32                write_iq_response      = 0;
3202   
[2150]3203    // Packet checksum tracking
[4416]3204    uint32                local_checksum         = 0;
[2150]3205
3206    // Keep track of packet sequence number
[4393]3207    //   NOTE: these are uint32 even though the actual seq_num is uint16 so we can keep track of
3208    //       sending greater than 2^16 commands in one write IQ.
[4416]3209    uint32                seq_num                = 0;
3210    uint32                seq_start_num          = 0;
[2150]3211
3212    // Receive Buffer variables
[4416]3213    int                   rcvd_size              = 0;
3214    int                   rcvd_max_size          = 100;
3215    int                   num_retrys             = 0;
[2150]3216    unsigned char        *rcvd_buffer;
[4416]3217    uint32               *resp_args;
[2150]3218
3219    // Buffer of data for ethernet packet and pointers to components of the packet
3220    unsigned char        *send_buffer;
3221    wl_transport_header  *transport_hdr;
3222    wl_command_header    *command_hdr;
3223    wl_sample_header     *sample_hdr;
3224    uint32               *sample_payload;
3225
3226    // Sleep timer
3227    uint32                wait_time;
3228       
3229    // Compute some constants to be used later
[4416]3230    uint32                tport_hdr_size         = sizeof( wl_transport_header );
3231    uint32                tport_hdr_size_np      = sizeof( wl_transport_header ) - TRANSPORT_PADDING_SIZE;
3232    uint32                cmd_hdr_size           = sizeof( wl_transport_header ) + sizeof( wl_command_header );
3233    uint32                cmd_hdr_size_np        = sizeof( wl_transport_header ) + sizeof( wl_command_header ) - TRANSPORT_PADDING_SIZE;
3234    uint32                all_hdr_size           = sizeof( wl_transport_header ) + sizeof( wl_command_header ) + sizeof( wl_sample_header );
3235    uint32                all_hdr_size_np        = sizeof( wl_transport_header ) + sizeof( wl_command_header ) + sizeof( wl_sample_header ) - TRANSPORT_PADDING_SIZE;
[2150]3236
3237
3238#ifdef _DEBUG_
3239    // Print command arguments   
3240    printf("index = %d, length = %d, port = %d, ip_addr = %s \n", index, length, port, ip_addr);
3241    printf("num_sample = %d, start_sample = %d, buffer_id = %d \n", num_samples, start_sample, buffer_id);
3242    printf("num_pkts = %d, max_samples = %d \n", num_pkts, max_samples);
3243#endif
3244
3245    // Initialization
3246
3247    // Malloc temporary buffer to receive ethernet packets   
3248    rcvd_buffer  = (unsigned char *) malloc( sizeof( char ) * rcvd_max_size );
3249    if( rcvd_buffer == NULL ) { die_with_error("Error:  Could not allocate temp receive buffer"); }
3250
3251    // Malloc temporary buffer to process ethernet packets   
3252    send_buffer  = (unsigned char *) malloc( sizeof( char ) * max_length );
3253    if( send_buffer == NULL ) { die_with_error("Error:  Could not allocate temp send buffer"); }
3254    for( i = 0; i < cmd_hdr_size; i++ ) { send_buffer[i] = buffer[i]; }     // Copy current header to send buffer
3255
3256    // Set up pointers to all the pieces of the ethernet packet   
3257    transport_hdr  = (wl_transport_header *) send_buffer;
3258    command_hdr    = (wl_command_header   *) ( send_buffer + tport_hdr_size );
3259    sample_hdr     = (wl_sample_header    *) ( send_buffer + cmd_hdr_size   );
3260    sample_payload = (uint32              *) ( send_buffer + all_hdr_size   );
3261
3262    // Get necessary values from the packet buffer so we can send multiple packets
3263    seq_num         = endian_swap_16( transport_hdr->seq_num ) + 1;    // Current sequence number is from the last packet
3264    transport_flags = endian_swap_16( transport_hdr->flags );
3265
[4393]3266    // Determine data types and sizes based on input data_type
3267    switch (data_type) {
3268        case IQ_DATA_TYPE_DOUBLE:
3269            mex_data_type     = mxDOUBLE_CLASS;
3270            data_size         = sizeof(double);
3271
3272            // Check that we have complex doubles
3273            if ( mxIsDouble(samples) != 1 ) { mexErrMsgTxt("Error: Data type of samples does not match input type 'double'"); }
3274            if ( mxIsComplex(samples) != 1 ) { process_imag = 0; }  // Do not process the imaginary part of the array
3275           
3276            // Get the real pointer and update based on the iteration
3277            tmp_double_array_real = mxGetPr(samples);
3278            tmp_double_array_real = &(tmp_double_array_real[iteration * num_samples]);
3279
3280            // Get the imag pointer and update based on the iteration
3281            if (process_imag) {
3282                tmp_double_array_imag = mxGetPi(samples);           
3283                tmp_double_array_imag = &(tmp_double_array_imag[iteration * num_samples]);
3284            }
3285        break;
3286       
3287        case IQ_DATA_TYPE_SINGLE:
3288            mex_data_type     = mxSINGLE_CLASS;
3289            data_size         = sizeof(float);
3290           
3291            // Check that we have complex singles
3292            if ( mxIsSingle(samples) != 1 ) { mexErrMsgTxt("Error: Data type of samples does not match input type 'single'"); }
3293            if ( mxIsComplex(samples) != 1 ) { process_imag = 0; }  // Do not process the imaginary part of the array
3294           
3295            // Get the real pointer and update based on the iteration
3296            tmp_single_array_real = (float *) mxGetData(samples);
3297            tmp_single_array_real = &(tmp_single_array_real[iteration * num_samples]);
3298
3299            // Get the imag pointer and update based on the iteration
3300            if (process_imag) {
3301                tmp_single_array_imag = (float *) mxGetImagData(samples);
3302                tmp_single_array_imag = &(tmp_single_array_imag[iteration * num_samples]);
3303            }
3304        break;
3305       
3306        case IQ_DATA_TYPE_INT16:
3307            mex_data_type     = mxINT16_CLASS;
3308            data_size         = sizeof(int16);
3309           
3310            // Check that we have complex int16
3311            if ( mxIsInt16(samples) != 1 ) { mexErrMsgTxt("Error: Data type of samples does not match input type 'int16'"); }
3312            if ( mxIsComplex(samples) != 1 ) { process_imag = 0; }  // Do not process the imaginary part of the array
3313           
3314            // Get the real pointer and update based on the iteration
3315            tmp_int16_array_real = (int16 *) mxGetData(samples);
3316            tmp_int16_array_real = &(tmp_int16_array_real[iteration * num_samples]);
3317                       
3318            // Get the imag pointer and update based on the iteration
3319            if (process_imag) {
3320                tmp_int16_array_imag = (int16 *) mxGetImagData(samples);
3321                tmp_int16_array_imag = &(tmp_int16_array_imag[iteration * num_samples]);
3322            }
3323        break;
3324       
3325        case IQ_DATA_TYPE_RAW:
3326            mex_data_type     = mxUINT32_CLASS;
3327            data_size         = sizeof(uint32);
3328           
3329            // Check that we have real uint32
3330            if ( mxIsUint32(samples) != 1 ) { mexErrMsgTxt("Error: Data type of samples does not match input type 'raw'"); }
3331            if ( mxIsComplex(samples) != 0 ) { mexErrMsgTxt("Error: Sample data of type 'raw' must be real"); }
3332           
3333            // Get the real and imag pointers
3334            tmp_uint32_array = (uint32 *) mxGetData(samples);
3335           
3336            // Update the pointers based on the iteration
3337            tmp_uint32_array = &(tmp_uint32_array[iteration * num_samples]);
3338        break;
3339       
3340        default:
3341            mexErrMsgTxt("Error:  Unsupported output data type");
3342        break;
3343    }
[4401]3344
[4393]3345   
[4401]3346    // Computer the intra-packet wait time
3347    wait_time = wl_compute_write_wait_time(hw_ver, buffer_id, max_samples);
3348
3349   
[4393]3350    // Set up the one-time packet values
3351
3352    // Command header
3353    //   NOTE:  Since there is one sample packet per command, we set the number number of command arguments to be 1.
[4416]3354    command_hdr->num_args    = endian_swap_16( 0x0001 );
[4393]3355
3356    // Sample header
3357    //   NOTE:  Iteration over buffer_ids occurs above this function
[4416]3358    sample_hdr->buffer_id    = endian_swap_16( buffer_id );   
3359    sample_iq_id             = (sample_write_iq_id & 0xFF);
3360    sample_hdr->sample_iq_id = sample_iq_id;
[4393]3361   
[4416]3362    // Increment write IQ ID (explicitly maintain as a uint8)
3363    sample_write_iq_id = (sample_write_iq_id + 1) % 0x100;
3364   
[2150]3365    // Initialize loop variables
3366    slow_write    = 0;
3367    need_resp     = 0;
3368    offset        = start_sample;
3369    seq_start_num = seq_num;
[4401]3370       
3371    // Based on some analysis, the loop to transmit packets rarely takes longer than 72us
3372    // (ie the time that it takes to transmit a jumbo frame on a 1 Gbps Ethernet link)
3373    // on our test setup:  See http://warpproject.org/trac/wiki/WARPLab/Benchmarks/WARPLAB_7_5_0
3374    // for a description of the hardware and software.
3375    //
3376    // Therefore, while further optimizations do exist within the code loop below, we are going
3377    // to leave it 'as is' for now.
3378    //
[2150]3379   
3380    // For each packet
3381    for( i = 0; i < num_pkts; i++ ) {
3382   
3383        // Determine how many samples we need to send in the packet
3384        if ( ( offset + max_samples ) <= num_samples ) {
3385            sample_num = max_samples;
3386        } else {
3387            sample_num = num_samples - offset;
3388        }
3389
3390        // Determine the length of the packet (All WARPLab payload minus the padding for word alignment)
3391        length = all_hdr_size_np + (sample_num * sizeof( uint32 ));
3392
[3297]3393        // Request that the board responds:
3394        //    - For slow_write == 1, all packets should be acked
3395        //    - For the last packet, if we are checking the checksum, then it needs to be acked
3396        //
3397        if ( slow_write == 1 ) {
[2150]3398            need_resp       = 1;
[3297]3399            transport_flags = transport_flags | TRANSPORT_FLAG_ROBUST;       
3400        } else if ( ( i == ( num_pkts - 1 ) ) && ( check_chksum == 1 ) ) {
3401            need_resp       = 1;
[2150]3402            transport_flags = transport_flags | TRANSPORT_FLAG_ROBUST;
3403        } else {
3404            need_resp       = 0;
3405            transport_flags = transport_flags & ~TRANSPORT_FLAG_ROBUST;       
3406        }
3407
3408        // Prepare transport
3409        //   NOTE:  The length of the packet is the maximum payload size supported by the transport.  However, the
3410        //       maximum payload size returned by the node has the two byte Ethernet padding already subtracted out, so the
3411        //       length of the transport command is the length minus the transport header plus the padding size, since
3412        //       we do not want to double count the padding.
3413        //
3414        transport_hdr->length   = endian_swap_16( length - tport_hdr_size_np );
[4393]3415        transport_hdr->seq_num  = endian_swap_16( (seq_num & 0xFFFF) );
[2150]3416        transport_hdr->flags    = endian_swap_16( transport_flags );
3417       
3418        // Prepare command
[4393]3419        //   NOTE:  See above comment about length.  Since there is one sample packet per command, we set the number
[2150]3420        //       number of command arguments to be 1.
3421        //
3422        command_hdr->length     = endian_swap_16( length - cmd_hdr_size_np );
3423       
3424        // Prepare sample packet
3425        if ( i == 0 ) {
[4236]3426            // First packet
3427            if ( num_pkts > 1 ) {
3428                sample_hdr->flags   = SAMPLE_CHKSUM_RESET;
3429            } else {
3430                // Only one packet so mark both flag bits
3431                sample_hdr->flags   = SAMPLE_CHKSUM_RESET | SAMPLE_LAST_WRITE;
3432            }
3433        } else if ( i == (num_pkts - 1) ) {
3434            // Last packet
3435            sample_hdr->flags       = SAMPLE_LAST_WRITE;
[2150]3436        } else {
[4236]3437            // All other packets
3438            sample_hdr->flags       = 0x0;
[2150]3439        }
[4393]3440       
[2150]3441        sample_hdr->start       = endian_swap_32( offset );
3442        sample_hdr->num_samples = endian_swap_32( sample_num );
[4393]3443       
[4401]3444       
[4393]3445        // NOTE:  In C when converting from double to Fix_16_15, the naive implementation:
3446        //
3447        //       output = (int16)(double_value * (1 << 15));
3448        //
3449        //   will have conversion errors when the double input exceeds the range of the Fix_16_15 variable.  For
3450        //   example, a Fix_16_15 can only represent a value of [32767 .. -32768] or in terms of doubles
3451        //   [0.999969482421875 .. -1].  However, in Write IQ, we allow a value of [1 .. -1] which unfortunately
3452        //   exceeds the range of the Fix_16_15 representation.  Therefore, if we have a Write IQ value of 1, then
3453        //   this will result in an output value of 0x8000 which is -32768 and is not correct.
3454        //
3455        //   Interestingly, the naive implementation in M:
3456        //
3457        //       output = int16(double_value * 2^15);
3458        //
3459        //   actually handles the conversion correctly and will cause the value of 1 (and presumably any values
3460        //   greater than 1) to have a value of 32767 (ie it caps the value at the largest positive integer
3461        //   representable by a Fix_16_15).
3462        //
3463        //   Hence, we need to adjust our implementation of the double to Fix_16_15 conversion so that it behaves
3464        //   like the naive Matlab implementation (ie correctly):
3465        //
3466        //       tmp_int32 = (double_value) * (1 << 15);                   // Convert but store in a larger variable
3467        //       tmp_int32 = ( tmp_int32 >= 0x8000 ) ? 0x7FFF : tmp_int32; // Adjust any values that greater than Fix_16_15
3468        //       tmp_int32 = ( tmp_int32 < -0x8000 ) ? 0x8000 : tmp_int32; // Adjust any values that are less than Fix_16_15
3469        //       output    = (int16)(tmp_int32);                           // Truncate to a 16-bit value
3470        //
3471        //   or
3472        //
3473        //       tmp_int16 = (int16)(double_value * (1 << 15));            // Convert naively from double to Fix_16_15
3474        //       if (double_value >= 1.0) { tmp_int16 = (int16)(0x7FFF); } // Adjust any values that greater than Fix_16_15
3475        //       if (double_value < -1.0) { tmp_int16 = (int16)(0x8000); } // Adjust any values that are less than Fix_16_15
3476        //       output = tmp_int16;
3477        //
3478        //   This will correctly handle the error condition described above.
3479        //
3480        switch (data_type) {
3481            // ------------------------------------------------------------------------------------------------
3482            case IQ_DATA_TYPE_DOUBLE:
3483                // Need to process double precision floating point array
3484                //    NOTE:  This performs an endian swap (little big) on IQ data
3485                //    NOTE:  This will convert IQ data from a complex double to two Fix_16_15 packed into a UFix_32_0
3486                //
3487                for( j = 0; j < sample_num; j++ ) {
3488                    tmp_double_real = tmp_double_array_real[j + offset];
3489                   
3490                    // Convert the real value
3491                    tmp_int16_real = (int16)(tmp_double_real * (1 << 15));            // Convert naively from double to Fix_16_15
3492                    if (tmp_double_real >= 1.0) { tmp_int16_real = (int16)(0x7FFF); } // Adjust any values that greater than Fix_16_15
3493                    if (tmp_double_real < -1.0) { tmp_int16_real = (int16)(0x8000); } // Adjust any values that are less than Fix_16_15
3494                     
3495                    if (process_imag) {
3496                        tmp_double_imag = tmp_double_array_imag[j + offset];
3497                                   
3498                        // Convert the imag value
3499                        tmp_int16_imag = (int16)(tmp_double_imag * (1 << 15));            // Convert naively from double to Fix_16_15
3500                        if (tmp_double_imag >= 1.0) { tmp_int16_imag = (int16)(0x7FFF); } // Adjust any values that greater than Fix_16_15
3501                        if (tmp_double_imag < -1.0) { tmp_int16_imag = (int16)(0x8000); } // Adjust any values that are less than Fix_16_15
3502                   
3503                        // Populate the sample payload
3504                        sample_payload[j] = endian_swap_32((tmp_int16_real << 16) | (tmp_int16_imag & 0xFFFF));
3505                    } else {
3506                        // Populate the sample payload
3507                        sample_payload[j] = endian_swap_32((tmp_int16_real << 16));
3508                    }
[4401]3509                }               
[4393]3510            break;
3511           
3512            // ------------------------------------------------------------------------------------------------
3513            case IQ_DATA_TYPE_SINGLE:
3514                // Need to process single precision floating point array
3515                //     NOTE:  This is exactly the same processing as the 'double' case but using float
3516                //            instead of double for the output type
3517                //
3518                for( j = 0; j < sample_num; j++ ) {
3519                    tmp_single_real = tmp_single_array_real[j + offset];
3520                                   
3521                    // Convert the real value
3522                    tmp_int16_real = (int16)(tmp_single_real * (1 << 15));            // Convert naively from double to Fix_16_15
3523                    if (tmp_single_real >= 1.0) { tmp_int16_real = (int16)(0x7FFF); } // Adjust any values that greater than Fix_16_15
3524                    if (tmp_single_real < -1.0) { tmp_int16_real = (int16)(0x8000); } // Adjust any values that are less than Fix_16_15
3525 
3526                    if (process_imag) {
3527                        tmp_single_imag = tmp_single_array_imag[j + offset];
3528                       
3529                        // Convert the imag value
3530                        tmp_int16_imag = (int16)(tmp_single_imag * (1 << 15));            // Convert naively from double to Fix_16_15
3531                        if (tmp_single_imag >= 1.0) { tmp_int16_imag = (int16)(0x7FFF); } // Adjust any values that greater than Fix_16_15
3532                        if (tmp_single_imag < -1.0) { tmp_int16_imag = (int16)(0x8000); } // Adjust any values that are less than Fix_16_15
3533                       
3534                        // Populate the sample payload
3535                        sample_payload[j] = endian_swap_32((tmp_int16_real << 16) | (tmp_int16_imag & 0xFFFF));
3536                    } else {
3537                        // Populate the sample payload
3538                        sample_payload[j] = endian_swap_32((tmp_int16_real << 16));                   
3539                    }
3540                }
3541            break;
3542           
3543            // ------------------------------------------------------------------------------------------------
3544            case IQ_DATA_TYPE_INT16:
3545                // Need to process int16 array
3546                //    NOTE:  This performs an endian swap (little big) on IQ data
3547                //    NOTE:  This will convert IQ data from a complex Fix_16_15 to two Fix_16_15 packed into a UFix_32_0
3548                //
3549                for( j = 0; j < sample_num; j++ ) {
3550                    tmp_int16_real = tmp_int16_array_real[j + offset];
3551
3552                    if (process_imag) {
3553                        tmp_int16_imag = tmp_int16_array_imag[j + offset];
3554                   
3555                        // Populate the sample payload
3556                        sample_payload[j] = endian_swap_32((tmp_int16_real << 16) | (tmp_int16_imag & 0xFFFF));
3557                    } else {
3558                        // Populate the sample payload
3559                        sample_payload[j] = endian_swap_32((tmp_int16_real << 16));
3560                    }
3561                }
3562            break;
3563           
3564            // ------------------------------------------------------------------------------------------------
3565            case IQ_DATA_TYPE_RAW:
3566                // Need to process the Ethernet packet into a uint32 array
3567                //    NOTE:  This performs an endian swap (little big) on IQ data
3568                //    NOTE:  No other processing is done on the data
3569                //
3570                for( j = 0; j < sample_num; j++ ) {
3571                    sample_payload[j] = endian_swap_32(tmp_uint32_array[j + offset]);
3572                   
3573                    // Populate variables for the checksum
3574                    if (j == (sample_num - 1)) {
3575                        tmp_int16_real = (tmp_uint32_array[j + offset] >> 16);
3576                        tmp_int16_imag = (tmp_uint32_array[j + offset] & 0xFFFF);
3577                    }
3578                }
3579            break;
3580           
3581            // ------------------------------------------------------------------------------------------------
3582            default:
3583                mexErrMsgTxt("Error:  Unsupported output data type");
3584            break;
[2150]3585        }
[4393]3586       
[2150]3587        // Add back in the padding so we can send the packet
3588        length += TRANSPORT_PADDING_SIZE;
3589
3590        // Send packet
[2185]3591        sent_size = send_socket( index, (char *) send_buffer, length, ip_addr, port );
[2150]3592
3593        if ( sent_size != length ) {
3594            die_with_error("Error:  Size of packet sent to with samples does not match length of packet.");
3595        }
3596       
3597        // Update loop variables
3598        offset   += sample_num;
3599        seq_num  += 1;
3600
3601        // Compute checksum
[2166]3602        // NOTE:  Due to a weakness in the Fletcher 32 checksum (ie it cannot distinguish between
3603        //     blocks of all 0 bits and blocks of all 1 bits), we need to add additional information
3604        //     to the checksum so that we will not miss errors on packets that contain data of all
3605        //     zero or all one.  Therefore, we add in the start sample for each packet since that
[3297]3606        //     is readily available on the node.
[2166]3607        //
[2150]3608        if ( i == 0 ) {
[4393]3609            local_checksum = wl_update_checksum(((offset - sample_num) & 0xFFFF), SAMPLE_CHKSUM_RESET); 
[2150]3610        } else {
[4393]3611            local_checksum = wl_update_checksum(((offset - sample_num) & 0xFFFF), SAMPLE_CHKSUM_NOT_RESET); 
[2150]3612        }
[4401]3613       
[4393]3614        // NOTE:  Based on the packet processing above, the temporary variables:  tmp_int16_real and tmp_int16_imag
3615        //     will contain the value of the last entry in the packet which we will use for the checksum.
3616        //
3617        local_checksum = wl_update_checksum(((tmp_int16_real & 0xFFFF) ^ (tmp_int16_imag & 0xFFFF)), SAMPLE_CHKSUM_NOT_RESET);
[2150]3618       
3619        // If we need a response, then wait for it
[4393]3620        //
[2150]3621        if ( need_resp == 1 ) {
3622
3623            // Initialize loop variables
3624            timeout   = 0;
3625            done      = 0;
3626            rcvd_size = 0;
3627           
3628            // Process each return packet
3629            while ( !done ) {
3630
3631                // If we hit the timeout, then try to re-transmit the packet
3632                if ( timeout >= TRANSPORT_TIMEOUT ) {
3633               
3634                    // If we hit the max number of retrys, then abort
3635                    if ( num_retrys >= TRANSPORT_MAX_RETRY ) {
3636                        die_with_error("Error:  Reached maximum number of retrys without a response... aborting.");                   
3637                    } else {
3638                        // Roll everything back and retransmit the packet
3639                        num_retrys += 1;
3640                        offset     -= sample_num;
3641                        i          -= 1;
3642                        break;
3643                    }
3644                }
3645
[4393]3646                // Receive packet (socket error checking done by function)
[2185]3647                rcvd_size = receive_socket( index, rcvd_max_size, (char *) rcvd_buffer );
[2150]3648
3649                if ( rcvd_size > 0 ) {
[4416]3650                    resp_args   = (uint32 *) ( rcvd_buffer + cmd_hdr_size );
3651                   
3652                    write_iq_response = wl_process_write_iq_response(resp_args, sample_iq_id, local_checksum, write_iq_ready_warn);
[2150]3653
[4416]3654                    // If we have a checksum error in fast write mode, then switch to slow write and start over
3655                    if (write_iq_response == SAMPLE_CHECKSUM_FAILED) {
[2150]3656                        if ( slow_write == 0 ) {
[2880]3657                            if ( suppress_iq_warnings == 0 ) {
[4706]3658                                printf("WARNING:  Checksums do not match on pkt %d.\n", i);
3659                                printf("    Expected = %08x  Received = %08x.  Restarting Write IQ using 'slow write'.\n\n", local_checksum, endian_swap_32(resp_args[1]));
3660                                printf("    This message generally occurs when the node is not able to keep up with the\n");
3661                                printf("    Write IQ data transfer rate from the host.  If this message occurs frequently\n");
3662                                printf("    please do one of the following:\n");
3663                                printf("        1) If the node is transmitting or receiving while trying to perform the \n");
3664                                printf("           Write IQ, then add a delay until the node is finished.  For example,\n");
3665                                printf("               'pause(1.5 * NUM_SAMPLES * 1/(40e6));'\n");
3666                                printf("           after any triggers and before the Write IQ request.\n");
3667                                printf("        2) Adjust the inter-packet Write IQ wait time for the transport:\n");
3668                                printf("               wl_mex_udp_transport('write_iq_set_pkt_wait_time', wait_time)\n");
3669                                printf("           where 'wait_time' is in microseconds and is larger than the current\n");
3670                                printf("           wait time of %d microseconds.\n", wait_time);
3671                                printf("        3) Suppress all IQ warnings for the transport:\n");
3672                                printf("               wl_mex_udp_transport('suppress_iq_warnings')\n\n");
[2880]3673                            }
[2150]3674                           
3675                            slow_write = 1;
3676                            offset     = start_sample;
3677                            i          = -1;
3678                            break;
3679                        } else {
3680                            die_with_error("Error:  Checksums do not match when in slow write... aborting.");
3681                        }
3682                    }
[4416]3683
3684                    // If the node is not ready, then start over (any required waiting has already been completed)                   
3685                    if (write_iq_response == SAMPLE_IQ_NOT_READY) {
3686                        write_iq_ready_warn = 0;
[2150]3687                   
[4416]3688                        offset     = start_sample;
3689                        i          = -1;
3690                        break;
3691                    }
3692                   
[2150]3693                    timeout = 0;
3694                    done    = 1;
3695                } else {
3696                    // If we do not have a packet, increment the timeout counter
3697                    timeout++;
3698                }
3699            }  // END while( !done )
[4416]3700        } else {
3701            // Check if the node has sent us a packet that we were not expecting
3702            rcvd_size = receive_socket( index, rcvd_max_size, (char *) rcvd_buffer );
3703           
3704            if ( rcvd_size > 0 ) {
3705                resp_args = (uint32 *) ( rcvd_buffer + cmd_hdr_size );
3706               
3707                write_iq_response = wl_process_write_iq_response(resp_args, sample_iq_id, 0, write_iq_ready_warn);
3708
3709                // If the node is not ready, then start over (any required waiting has already been completed)
3710                if (write_iq_response == SAMPLE_IQ_NOT_READY) {
3711                    write_iq_ready_warn = 0;
3712                   
3713                    offset     = start_sample;
3714                    i          = -1;
3715                }
3716            }
[2150]3717        }  // END if need_resp
[4416]3718
[2150]3719       
[4401]3720        // Wait for the requisite time between packets
3721        if ( wait_time != 0 ) {
3722            wl_usleep( wait_time );
3723        }
[2150]3724    }  // END for num_pkts
3725
3726
3727    if ( offset != num_samples ) {
[2880]3728        printf("ERROR:  Issue with calling function.  \n");
[2150]3729        printf("    Requested %d samples, sent %d sample based on other packet information: \n", num_samples, offset);
3730        printf("    Number of packets to send %d, Max samples per packet %d \n", num_pkts, max_samples);
3731    }
3732   
3733    // Free locally allocated memory   
3734    free( send_buffer );
3735    free( rcvd_buffer );
[4393]3736   
[2150]3737    // Finalize outputs
[4393]3738    //   NOTE:  seq_num is a uint32 so that it can capture values > 2^16 which could potentially
3739    //          occur with the new larger buffer sizes.
3740    //
[2150]3741    if ( seq_num > seq_start_num ) {
3742        *num_cmds += seq_num - seq_start_num;
3743    } else {
3744        *num_cmds += (0xFFFF - seq_start_num) + seq_num;
3745    }
3746   
[3297]3747    *checksum = local_checksum;
3748   
[2150]3749    return offset;
3750}
3751
3752
3753
3754/*****************************************************************************/
3755/**
[4401]3756*  Function:  wl_compute_write_wait_time
3757*
3758*  Function to compute the wait time for the write function in micro-seconds
3759*
3760******************************************************************************/
3761uint32 wl_compute_write_wait_time(uint32 hw_ver, uint32 buffer_id, uint32 max_samples) {
3762    uint32         j;
3763    uint32         buffer_count      = 0;
3764    uint32         wait_time         = 0;
3765
3766
3767    // Write baseband buffers can saturate the Ethernet wire.  However, for small packets the
3768    // node cannot keep up and therefore we need to delay the next transmission based on:
3769    // 1) packet size and 2) number of buffers being written
3770   
3771    // In case the default implementation based on experimental data does not work as expected for your
3772    // setup, you can optionally specify a user Write IQ packet wait time that will override the default
3773    // implementation.
3774   
3775    if ( use_user_write_iq_wait_time == 1 ) {
3776       
3777        wait_time = user_write_iq_wait_time;
3778
3779    } else {
3780   
3781        // NOTE:  This is a simplified implementation based on experimental data.  It is by no means optimized for
3782        //     all cases.  Since WARP v2 and WARP v3 hardware have drastically different internal architectures,
3783        //     we first have to understand which HW the Write IQ is being performed and scale the wait_times
3784        //     accordingly.  Also, since we can have one transmission of IQ data be distributed to multiple buffers,
3785        //     we need to increase the timeout if we are transmitting to more buffers on the node in order to
3786        //     compensate for the extended processing time.  One other thing to note is that if you view the traffic that
3787        //     this generates on an oscilloscope (at least on Window 7 Professional 64-bit), it is not necessarily regular
3788        //     but will burst 2 or 3 packets followed by an extended break.  Fortunately there is enough buffering in
3789        //     the data flow that this does not cause a problem, but it makes it more difficult to optimize the data flow
3790        //     since we cannot guarentee the timing of the packets at the node. 
3791        //
3792        //     If you start receiving checksum failures and need to adjust timing, please do so in the code below.
3793        //
3794        switch ( hw_ver ) {
3795            case TRANSPORT_WARP_HW_v2:
3796                // WARP v2 Hardware only supports small ethernet packets
3797 
3798                buffer_count = 0;
3799
3800                // Count the number of buffers in the buffer_id
3801                for( j = 0; j < TRANSPORT_WARP_RF_BUFFER_MAX; j++ ) {
3802                    if ( ( ( buffer_id >> j ) & 0x1 ) == 1 ) {
3803                        buffer_count++;
3804                    }
3805                }
3806           
3807                // Note:  Performance drops dramatically if this number is smaller than the processing time on
3808                //     the node.  This is due to the fact that you perform a slow write when the checksum fails. 
3809                //     For example, if you change this from 160 to 140, the Avg Write IQ per second goes from
3810                //     ~130 to ~30.  If this number gets too large, then you will also degrade performance
3811                //     given you are waiting longer than necessary.
3812                //
3813                // Currently, wait times are set at:
3814                //     1 buffer  = 160 us
3815                //     2 buffers = 240 us
3816                //     3 buffers = 320 us
3817                //     4 buffers = 400 us
3818                //
3819                // This is due to the fact that processing on the node is done thru a memcpy and takes
3820                // a fixed amount of time longer for each additonal buffer that is transferred.
3821                //               
3822                wait_time = 80 + ( buffer_count * 80 );
3823            break;
3824           
3825            case TRANSPORT_WARP_HW_v3:
3826                // In WARP v3 hardware, we need to account for both small packets as well as jumbo frames.  Also,
3827                // since the WARP v3 hardware uses a DMA to transfer packet data, the processing overhead is much
3828                // less than on v2 (hence the smaller wait times).  Through experimental testing, we found that
3829                // for jumbo frames, the processing overhead was smaller than the length of the ethernet transfer
3830                // and therefore we do not need to wait at all.  We have not done exhaustive testing on ethernet
3831                // packet size vs wait time.  So in this simplified implementation, if your Ethernet MTU size is
3832                // less than 9000 bytes (ie approximately 0x8B8 samples) then we will insert a 40 us, or 50 us,
3833                // delay between transmissions to give the board time to keep up with the flow of packets.
3834                //
3835                if ( max_samples < 0x800 ) {
3836                    if ( buffer_id == 0xF ) {
3837                        wait_time = 50;
3838                    } else {
3839                        wait_time = 40;
3840                    }
3841                }
3842             break;
3843             
3844             default:
[4698]3845                printf("WARNING:  WARP HW version of node is not recognized:  Received:  %d   Expected:  %d or %d\n", hw_ver, TRANSPORT_WARP_HW_v2, TRANSPORT_WARP_HW_v3);
3846                printf("WARNING:      This could be an issue with the version of the MEX you are trying to use.\n");
3847                printf("WARNING:      Please check that WARPLab MEX UDP Transport v%s is the required version \n", WL_MEX_UDP_TRANSPORT_VERSION);
3848                printf("WARNING:      for your WARPLab release.\n");
[4401]3849             break;
3850        }
3851    }  // END if ( use_user_write_iq_wait_time == 1 )
3852   
3853    return wait_time;
3854}
3855
3856
3857
3858/*****************************************************************************/
3859/**
[4416]3860*  Function:  wl_process_write_iq_response
3861*
3862*  Function to process the write IQ response
3863*
3864******************************************************************************/
3865uint32 wl_process_write_iq_response(uint32 * args, uint32 sample_iq_id, uint32 checksum, uint32 iq_ready_warn) {
3866
3867    // Response variables
3868    uint32                node_status       = 0;
3869    uint32                node_checksum     = 0;
3870    uint32                node_sample_iq_id = 0;
3871    uint32                wait_time         = 0;
3872
3873    // Return variable
3874    uint32                return_val        = SAMPLE_RESPONSE_SUCCESS;
3875   
3876    // Get the IQ ID from the response
3877    node_sample_iq_id = endian_swap_32( args[1] );
3878
3879    // Only process packets for the current sample_iq_id
3880    if (node_sample_iq_id == sample_iq_id) {
3881        node_status       = endian_swap_32( args[0] );
3882       
3883        switch (node_status) {
3884            // ------------------------------------------------------------------------------------       
3885            case SAMPLE_IQ_ERROR:
3886                printf("SAMPLE_IQ_ERROR:\n");
3887                printf("    Due to limitations on the node, it is not possible to do a Write IQ while the\n");
3888                printf("    node is transmitting in 'Continuous Tx' mode.  Please stop the current transmission\n");
3889                printf("    and try the Write IQ again\n");
3890           
3891                die_with_error("ERROR:  Node returned 'SAMPLE_IQ_ERROR'.  See above for debug information.");
3892            break;
3893           
3894            // ------------------------------------------------------------------------------------       
3895            case SAMPLE_IQ_NOT_READY:
3896                // If the node is not ready, then we need to wait until the node is ready and try again from the
3897                // beginning of the Write IQ.
3898                //
3899                wait_time = wl_compute_sample_wait_time( &args[3] );
3900                               
3901                // Wait until the samples should be done
3902                if ( wait_time != 0 ) {
3903                    wl_usleep( wait_time + 100 );
3904                }
3905               
3906                // Print warning
3907                if (iq_ready_warn == 1) {
3908                    printf("WARNING:  Node was not ready to process Write IQ request.  Waiting to request again.\n");
[4706]3909                    printf("    This warning can be removed by waiting until the node is not busy with a TX or RX \n");
3910                    printf("    operation.  To do this, please add 'pause(1.5 * NUM_SAMPLES * 1/(40e6));' after\n");
3911                    printf("    any triggers and before the Write IQ request.\n\n");
[4416]3912                }
3913               
3914                // Reset the loop variables
3915                return_val = SAMPLE_IQ_NOT_READY;
3916            break;
3917           
3918            // ------------------------------------------------------------------------------------       
3919            case CMD_PARAM_SUCCESS:
3920                // If the response was a success, then check the checksum
3921                //
[4472]3922                node_checksum  = endian_swap_32( args[2] );
[4416]3923
3924                // Compare the checksum values
3925                if ( node_checksum != checksum ) {
3926               
3927                    // Reset the loop variables
3928                    return_val = SAMPLE_CHECKSUM_FAILED;
3929                }
3930            break;
3931       
3932            // ------------------------------------------------------------------------------------       
3933            default:
3934                // Print that there was an error; Reset the loop and continue
3935                printf("ERROR:  Unknown write IQ response status = %d\n", node_status);
3936                return_val = SAMPLE_RESPONSE_ERROR;
3937            break;
3938        }
3939    }
3940   
3941    return return_val;
3942}
3943
3944
3945
3946/*****************************************************************************/
3947/**
[2150]3948*  Function:  wl_update_checksum
3949*
3950*  Function to calculate a Fletcher-32 checksum to detect packet loss
3951*
3952******************************************************************************/
3953unsigned int wl_update_checksum(unsigned short int newdata, unsigned char reset ){
3954    // Fletcher-32 Checksum
3955    static unsigned int sum1 = 0;
3956    static unsigned int sum2 = 0;
3957
[4293]3958    if( reset == SAMPLE_CHKSUM_RESET ){ sum1 = 0; sum2 = 0; }
[2150]3959
3960    sum1 = (sum1 + newdata) % 0xFFFF;
3961    sum2 = (sum2 + sum1   ) % 0xFFFF;
3962
3963    return ( ( sum2 << 16 ) + sum1);
3964}
3965
3966
3967
[4416]3968/*****************************************************************************/
3969/**
3970*  Function:  wl_compute_sample_wait_time
3971*
3972*  Function to compute the wait time based on the args:
3973*      args[0]       - Tx status
3974*      args[1]       - Current Tx read pointer
3975*      args[2]       - Tx length
3976*      args[3]       - Rx status
3977*      args[4]       - Current Rx write pointer
3978*      args[5]       - Rx length
3979*
3980******************************************************************************/
3981uint32 wl_compute_sample_wait_time(uint32 * args) {
3982
3983    uint32 node_tx_status    = endian_swap_32( args[0] );
3984    uint32 node_tx_pointer   = endian_swap_32( args[1] );
3985    uint32 node_tx_length    = endian_swap_32( args[2] );
3986    uint32 node_rx_status    = endian_swap_32( args[3] );
3987    uint32 node_rx_pointer   = endian_swap_32( args[4] );
3988    uint32 node_rx_length    = endian_swap_32( args[5] );
3989   
3990    // NOTE:  node_*_length and node_*_pointer are in bytes.  To convert the difference to microseconds,
3991    //    we need to divide by: 160  (ie 40e6 sample / sec * 4 bytes / sample * 1 usec => 160 bytes / usec)
3992    //
3993    uint32 tx_wait_time      = (node_tx_status) ? ((node_tx_length - node_tx_pointer) / 160) :0;
3994    uint32 rx_wait_time      = (node_rx_status) ? ((node_rx_length - node_rx_pointer) / 160) :0;;
3995
3996    // Return the greater of the TX or RX wait time   
3997    return ((tx_wait_time > rx_wait_time) ? tx_wait_time : rx_wait_time);
3998   
3999}
4000
4001
[2185]4002#ifdef WIN32
4003
[2150]4004/*****************************************************************************/
4005/**
4006*  Function:  uSleep
4007*
4008*  Since the windows Sleep() function only has a resolution of 1 ms, we need
4009*  to implement a usleep() function that will allow for finer timing granularity
4010*
4011******************************************************************************/
4012void wl_mex_udp_transport_usleep( int wait_time ) {
4013    static bool     init = false;
4014    static LONGLONG ticks_per_usecond;
4015
4016    LARGE_INTEGER   ticks_per_second;   
4017    LONGLONG        wait_ticks;
4018    LARGE_INTEGER   start_time;
4019    LONGLONG        stop_time;
4020    LARGE_INTEGER   counter_val;
4021
4022    // Initialize the function
4023    if ( !init ) {
4024   
4025        if ( QueryPerformanceFrequency( &ticks_per_second ) ) {
4026            ticks_per_usecond = ticks_per_second.QuadPart / 1000000;
4027            init = true;
4028        } else {
4029            printf("QPF() failed with error %d\n", GetLastError());
4030        }
4031    }
4032
4033    if ( ticks_per_usecond ) {
4034   
4035        // Calculate how many ticks we have to wait
4036        wait_ticks = wait_time * ticks_per_usecond;
4037   
4038        // Save the performance counter value
4039        if ( !QueryPerformanceCounter( &start_time ) )
4040            printf("QPC() failed with error %d\n", GetLastError());
4041
4042        // Calculate the stop time
4043        stop_time = start_time.QuadPart + wait_ticks;
4044
4045        // Wait until the time has expired
4046        while (1) {       
4047
4048            if ( !QueryPerformanceCounter( &counter_val ) ) {
4049                printf("QPC() failed with error %d\n", GetLastError());
4050                break;
4051            }
4052           
4053            if ( counter_val.QuadPart >= stop_time ) {
4054                break;
4055            }
4056        }
4057    }
4058}
4059
4060
[4293]4061
4062/*****************************************************************************/
4063/**
4064*  Function:  usec timestamp
4065*
4066******************************************************************************/
4067uint32 wl_mex_udp_transport_usec_timestamp() {
4068    static bool     init = false;
4069    static LONGLONG ticks_per_usecond;
4070
4071    LARGE_INTEGER   ticks_per_second;   
4072    LARGE_INTEGER   counter_val;
4073
4074    // Initialize the function
4075    if ( !init ) {
4076   
4077        if ( QueryPerformanceFrequency( &ticks_per_second ) ) {
4078            ticks_per_usecond = ticks_per_second.QuadPart / 1000000;
4079            init = true;
4080        } else {
4081            printf("QPF() failed with error %d\n", GetLastError());
4082        }
4083    }
4084
4085    if ( ticks_per_usecond ) {
4086   
4087        // Save the performance counter value
4088        if ( !QueryPerformanceCounter( &counter_val ) )
4089            printf("QPC() failed with error %d\n", GetLastError());
4090
4091        return (uint32)((counter_val.QuadPart / ticks_per_usecond) & 0x7FFFFFFF);
4092           
4093    }
4094   
4095    return 0;
4096}
4097
4098
[2185]4099#endif
[2150]4100
Note: See TracBrowser for help on using the repository browser.