== OFDM Reference Design - MAC/PHY Performance Characterization == This application characterizes the performance of the OFDM Reference Design and a user-supplied MAC implementation. This application utilizes multiple WARP nodes along with external PCs to orchestrate the experiments. The main focus of this design shows in real time how a computer running a client program written in a non-C language (TCL) can connect to a server, which interprets commands into warpnode management structures, using the server to send commands to the warp nodes, which execute the commands. In this application, the WARP nodes have no MAC or IP addresses. Both nodes run the same program. The following diagrams represent two possible topological configurations for the setup of this application: === Design Setup 1 (Recommended) === [[Image(OFDMReferenceDesign/Applications/Characterization:setup1.jpg, 600)]] === Design Setup 2 === [[Image(OFDMReferenceDesign/Applications/Characterization:setup2.jpg, 600)]] === Requirements === * 1 PCs (2 Preferred) with Ethernet interfaces * 2 WARP SISO or MIMO kits * TCL language compiler [http://www.activestate.com/activetcl/] * Router === Setup === 1. Connect each WARP node directly to the Ethernet interface of the router 2. Configure the PC Ethernet interfaces with IP address on the same subnet (10.0.0.1 and 10.0.0.2, for example) 3. Connect each PC directly to the Ethernet interface of the router === OFDM Reference Design Code === __Summary:__ The following extensions to the OFDM Reference Design expand node control by allowing a server to relay requests to warpnodes. The requests are sent as different C struct types. Command and Traffic structs set board parameters, while Command structs are used to stop and start transmissions, as well as request data (Stat structs) about the transmission. This model places the boards in a state where data for wireless transmission is locally created instead of taken from the Ethernet. The two complete codes for the modifications OFDM are the attached files csmaMac.c and warpnet_node2.c The development of creating an interactive warpnet control requires the introduction of new structure types for the warp platform. These two types are warpnodeStats (which contains Statistical Data) and warpnodeTraffic (which contains parameters for updating the board) The two types are defined as: Struct containing node statistics: {{{ typedef struct { unsigned char structType; unsigned char nodeID; unsigned char partnerID; ///Reserved Byte unsigned char reserved0; ///Used to track number of goodPackets unsigned int goodPackets; ///Used to track number of partnerBadPackets unsigned int partnerBadPackets; ///Used to track number of otherBadPackets unsigned int otherBadPackets; unsigned int rxBytes; unsigned int txBytes; ///Time in clock cycles. Recall PPC405 clock freq is 240000000 HZ. unsigned long long time; } warpnodeStats; }}} Struct containing traffic parameters for a node: {{{ typedef struct { unsigned char structType; /// unsigned char nodeID; ///Value of 1 denotes Transmit, Value of 0 denotes Receive unsigned char trafficMode; ///Reserved Byte unsigned char reserved0; ///Interval between start of each packet (in usec) unsigned int txInterval; ///The length of the packet (in bytes) unsigned short txPacketLen; ///Reserved Short unsigned short reserved1; } warpnodeTraffic; }}} warpnet_node.h needs to be expanded with these two new structs. Additionally, new pound defines are required for the new setup: Under ''//Struct Types for WARP Node <-> Server Packets'' {{{ #define STRUCTID_NODETRAFFIC 0x45 }}} Under ''//Node command codes'' {{{ #define NODECMD_SENDPACKET 0x64 #define NODECMD_SENDSTATS 0x65 #define NODECMD_START 0x66 #define NODECMD_STOP 0x67 #define NODECMC_REQUESTSTATS 0x68 }}} The traffic struct include a parameter called trafficMode, which specifies whether a board is transmitting or receiving. Include two #defines for Transmit and Receive: {{{ #define TRANSMIT 1 #define RECEIVE 0 }}} __Help:__ In warpmac.c and csmaMac.c you could comment out warpnet_node.h and replace it with the attached warpnet_node2.h The remainder of code extensions will occur in csmaMac.c For the new extension to the previous frame work, the following global variables will be required: {{{ ///Global Stat struct warpnodeStats myStats[WARPNET_NUMNODES]; ///Place holder for TRANSMIT or RECEIVE state unsigned char Mode; ///Measures time for beginning and end of transmission XTime start_timer, end_timer; unsigned int pktCount_good, pktCount_bad; ///Parameters used for warpmac_startPacketGeneration unsigned int Time; unsigned short packetlength; }}} This implementation of the OFDM Reference Design uses two similar functions for transmitting ack packets and stat packets when requested. Additionally, both functions require the seqNum as a parameter in order to ensure proper packet organization on the server-client side. The design of sendAck sends a warpnodeCommand with values, which denote it is the ack version of the warpnodeCommand ({{{myNodeCmd.cmdID = NODECMD_NODEACK}}}). The full design for sendAck is as follows: {{{ void sendAck(char rxSeqNum) { //Initialize Payload Address int pktBuf_payloadAddr; //Fill in a node command reply pkt myNodeCmd.structType = STRUCTID_NODECOMMAND; myNodeCmd.nodeID = myID; myNodeCmd.cmdID = NODECMD_NODEACK; myNodeCmd.cmdParam = rxSeqNum; //Fill in the ethernet packet's header info txEthPktHeader.ethType = WARPNET_ETHTYPE_NODE2SVR; memset(&(txEthPktHeader.srcAddr), (unsigned char)myID, 6); //Generic source address; ignored throughout memset(&(txEthPktHeader.dstAddr), (unsigned char)0xFF, 6); //Broadcast destination address //Fill in the ethernet packet's header info txEthPktHeader.pktLength = sizeof(warpnetEthernetPktHeader) + sizeof(warpnodeCommand); txEthPktHeader.numStructs = 1; txEthPktHeader.seqNum = rxSeqNum; //Copy over data for transmission over wire pktBuf_payloadAddr = warpphy_getBuffAddr(WARPNET_PKTBUFFINDEX); memcpy((void *)pktBuf_payloadAddr, &(txEthPktHeader), sizeof(warpnetEthernetPktHeader)); memcpy((void *)pktBuf_payloadAddr+sizeof(warpnetEthernetPktHeader), &(myNodeCmd), sizeof(warpnodeCommand)); //Send the packet over the wire warpmac_sendRawEthernetPacket((void *)pktBuf_payloadAddr, txEthPktHeader.pktLength); return; } }}} The design of sendStatPacket requires a few small modifications: {{{ void sendStatsPacket(char rxSeqNum) { //Initialize Payload Address unsigned int pktBuf_payloadAddr; //Fill in the ethernet packet's header info txEthPktHeader.pktLength = sizeof(warpnetEthernetPktHeader) + (WARPNET_NUMNODES*sizeof(warpnodeStats)); txEthPktHeader.numStructs = WARPNET_NUMNODES; txEthPktHeader.seqNum = rxSeqNum; //Copy the ethernet header and stats payload to a memroy buffer pktBuf_payloadAddr = warpphy_getBuffAddr(WARPNET_PKTBUFFINDEX); memcpy((void *)pktBuf_payloadAddr, &(txEthPktHeader), sizeof(warpnetEthernetPktHeader)); //Copy each stats struct (one per partner) memcpy((void *)pktBuf_payloadAddr+sizeof(warpnetEthernetPktHeader), &myStats, WARPNET_NUMNODES*sizeof(warpnodeStats)); //Send the packet over the wire warpmac_sendRawEthernetPacket((void *)pktBuf_payloadAddr, txEthPktHeader.pktLength); return; } }}} The function processControlStruct updates the node with the parameters in the warpnodeControl struct: {{{ void processControlStruct(warpnodeControl* ctrlStruct) { //xil_printf("Processing Control Struct\r\n"); //Interpret the control struct's modulation rate value switch(ctrlStruct->modOrder) { /*** Non-autorate version ***/ case 0x1: //BPSK pktFullRate = HDR_FULLRATE_BPSK; warp_userio_lcd_printline("Mod order: BPSK ", 16, 8, 1); break; case 0x2: //QPSK pktFullRate = HDR_FULLRATE_QPSK; warp_userio_lcd_printline("Mod order: QPSK ", 16, 8, 1); break; case 0x4: //16-QAM pktFullRate = HDR_FULLRATE_QAM_16; warp_userio_lcd_printline("Mod order: 16QAM", 16, 8, 1); break; case 0x6: //64-QAM pktFullRate = HDR_FULLRATE_QAM_64; warp_userio_lcd_printline("Mod order: 64QAM", 16, 8, 1); break; /*******************/ default: //xil_printf("Invalid Mod order - Recieved: %x\r\n", ctrlStruct->modOrder); //Invalid value, so ignore it and leave txFulrate as-is break; } //Set the transmit power for both radios warpphy_setTxPower(ctrlStruct->txPower); //xil_printf("Power changed to %d\r\n", ctrlStruct->txPower); //Use the circle of LEDs as a Tx power indicator warp_userioboard_set_leds((1<<((8+(ctrlStruct->txPower))/8))-1); unsigned char charsToPrint[16]; snprintf(charsToPrint, 16, "Tx Pwr: 0x%2x ", (ctrlStruct->txPower)); //xil_printf("Tx Power changed to: %x\r\n", ctrlStruct->txPower); warp_userio_lcd_printline(charsToPrint, 16, 6, 1); //Set the center frequency of both radios // Values 1-14 are 2.4GHz channels // Values 15-37 are 5GHz channels // Any other value is invalid and ignored if( ((ctrlStruct->channel) > 0) && ((ctrlStruct->channel) <= 37) ) { if((ctrlStruct->channel) <= 14) warpphy_setChannel(GHZ_2, ctrlStruct->channel); else warpphy_setChannel(GHZ_5, (ctrlStruct->channel) - 14); } snprintf(charsToPrint, 16, "Channel: %2d ", (ctrlStruct->channel)); warp_userio_lcd_printline(charsToPrint, 16, 7, 1); return; } }}} In previous designs of warpnet, the emacRx_callback was called when data was specified for the node and when it was specified for transmission over the air. The new design makes a distinction between these two types through mgmtFromNetworkLayer_callback and dataFromNetworkLayer_callback The mgmtFromNetworkLayer_callback extends previous code used to process management structures because this callback processes the new traffic struct and the new node commands. Because the nodes are on the same Ethernet network, the management callback checks if the Ethernet type is from the server to node or vice-versa. If a packet is a NODE2SVR, the node has received an ack packet or stat packet from the other node. Because both nodes are on the same network and see all traffic on the network, the implementation for SVR2NODE nodes checks if the nodeID in the structs equals myID. In the NODECMD, check the cmdStruct->cmdID for the IDs of NODECMD_RESETSTATS, NODECMD_START, NODECMD_STOP, and NODECMC_REQUESTSTATS: NODECMD_RESETSTATS needs to reset all values in myStats and send an ack packet if requested: {{{ case NODECMD_RESETSTATS: if( (cmdStruct->nodeID) == myID) { //Reset the good/bad pkt counts and Rx/Tx byte counts for(i=0; i 0) sendAck(rxSeqNum); } }}} NODECMD_START sets a xtime variable start_timer and begins packet generation if the traffic struct had set Mode to TRANSMIT: {{{ case NODECMD_START: XTime_GetTime(&start_timer); if (Mode==TRANSMIT){ warpmac_startPacketGeneration(packetlength,Time); //second argument is interval in usec } }}} NODECMD_STOP stops packet generation, calculates the time difference, updates the time variable in myStats, and sends an ack if requested: {{{ case NODECMD_STOP: //xil_printf("NODECMC_STOP \r\n"); warpmac_stopPacketGeneration(); XTime_GetTime(&end_timer); long long xtime=0; xtime=end_timer-start_timer; for(i=0; inodeID) == myID) { //Send Ack Packet if(rxSeqNum > 0) sendAck(rxSeqNum); } }}} NODECMC_REQUESTSTATS sends the stats packet and ack packet if requested: {{{ case NODECMC_REQUESTSTATS: if( (cmdStruct->nodeID) == myID) { sendStatsPacket(rxSeqNum); //Send Ack Packet if(rxSeqNum > 0) sendAck(rxSeqNum); } }}} Whenever phyRx_badHeader_callback is called, the otherBadPackets count should be incremented: {{{ int i; //Update every stats struct, since we don't know where this bad pkt came from for(i=0; inodeID) == myID) { //Send Ack Packet if(rxSeqNum > 0) sendAck(rxSeqNum); Time=trafficStruct->txInterval; Mode=trafficStruct->trafficMode; packetlength=trafficStruct->txPacketLen; } rxPktOffset += sizeof(warpnodeTraffic); }}} When phyRx_goodHeader_callback is called, either the count for goodPackets or partnerBadPackets is incremented. If the callback enters the if(state&GOOD) update the goodPacket count, rxBytes and txBytes as follows: {{{ myStats[srcNode].goodPackets++; myStats[srcNode].rxBytes += (packet->header.length + NUM_HEADER_BYTES + NUM_PAYLOAD_CRC_BYTES + NUM_PAYLOAD_TAIL_BYTES); myStats[srcNode].txBytes += NUM_HEADER_BYTES; }}} If the callback enters the if(state&BAD) update the partnerBadPackets: {{{ myStats[srcNode].partnerBadPackets++; }}} In the main function, set warpmac_enableDataFromNetwork, warpmac_setBaseRate, and warpmac_enableDummyPacketMode. For this application, the boards will be left in DummyPacketMode to prevent the nodes from transmitting information from the wire. (Due to the nature of the set up with routers, the TCP ack packets sent from the TCL-client computer to the server computer will be sent over the wireless channel. This can create problem because the computers will retransmit the data when they don't receive TCP-acks) Set these global variables to default values: {{{ Mode=0; sendStats=0; Time=0; packetlength=1470; int i; //Initialize the Stats Structs for(i=0; i