//NAME:
//  send_index_array_over_udp.c
//
//PURPOSE:
//  This program will send a 256-element array of ascending fixed point values (0, 1, 
//  2, ..., 256) to the GetCentroid firmware running on the MicroZed FPGA.  
//
//  The bitstream on the MicroZed uses a lwIP server (running on a Zynq-7010 SoC/FGPA). 
//  It uses UDP for Ethernet communication. It will read in the waveform data from the file 
//  specified on the command line. 
//
//COMPILING:
//  gcc -o send_index_array_over_udp.o send_index_array_over_udp.c
//
//CALLING SEQUENCE:
//    ./send_index_array_over_udp.o 
//
// Standard includes
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <sys/time.h>
#include <string.h>
#include <unistd.h>
#include <math.h>

// New includes relevant to this application
// types.h contains definitions of a number of data types used in system calls
#include <sys/types.h>
// socket.h includes a number of definitions of structures needed for sockets
#include <sys/socket.h>
// in.h contains constants and structures needed for internet domain addresses
#include <netinet/in.h>
// netdb.h contains more network related functions and structures, hostent, in particular
#include <netdb.h>

// Define defaults for IP address and port number on MicroZed
#define PORT_NUMBER 7
#define IP_ADDRESS "192.168.1.10"

// Definitions for our fixed point numbers
#define BW              32      // Total number of bits in fixed point data type
#define IW              24      // Number of bits left of decimal point in fixed point data type
#define BITDIV          256.0   // Divisor to shift fixed point to int and back to float

// Other defines
#define WAVE_SIZE 	256     // The number of elements in the wave (and index array) 

// Global arrays
float  IndArray[WAVE_SIZE]={0.};
int    FixedPointBuff[WAVE_SIZE]={0.};

// A simple error function that uses the perror call to print an error on stderr 
void error(const char *msg)
{
    perror(msg);
    exit(1);
}

// The main program
int main(int argc, char *argv[])
{

  // Declare variables
  int sockfd;               //file descriptors - array subscripts into file descriptor table
  int portno;               //stores the port number on which the server accepts connections
  int n;                    //return value for the read() and write() calls
  char* servername;         //a pointer to the IP address string or server name
 
  // Define the structures that contain internet addresses (defined in netinet/in.h)
  struct sockaddr_in serv_addr;

  // Define the hostent structure that defines a host computer on the Internet 
  struct hostent *server;

  // We are hard coding the IP and Port. See the #define statement above 
  server = gethostbyname(IP_ADDRESS);
  portno = PORT_NUMBER;

  // Open the socket - First argument is the address domain (internet domain - AF_INET or unix 
  // domain - AF_UNIX), Second argument is type of socket (stream - SOCK_STREAM or datagram - 
  // SOCK_DGRAM), Third argument is protocol - 0 allows OS to choose protocol (TCP for stream 
  // and UDP for datagram)
  // Returns an entry into file descriptor table
  sockfd = socket(AF_INET, SOCK_DGRAM, 0);
  if (sockfd < 0) error("ERROR opening socket\n\r");

  // Zero out the character array
  bzero((char *) &serv_addr, sizeof(serv_addr));

  // Assign the proper variables to the serv_addr sockaddr_in structure
  serv_addr.sin_family = AF_INET;          //Code for the address family - internet
  serv_addr.sin_port = htons(portno);      //Assign port number after going host byte->network byte order
  // And copy the server name into the socket structure address
  bcopy((char *)server->h_addr, (char *)&serv_addr.sin_addr.s_addr, server->h_length);

  // Connect the socket to the ip address and port that was assigned
  if (connect(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) error("ERROR connecting\n\r");

  /*************************************************************/
  /*                          READ IN DATA                     */
  /*************************************************************/
  // Now pack the fixed point array with the templates and covariance matrix
  int*  BuffPtr   = FixedPointBuff;
  float IndArr[WAVE_SIZE];
  int   FP_temp   = 0;
  int   i         = 0;
  int   j         = 0;

  // 1) Pack the index array
  for(i=0; i < WAVE_SIZE; ++i){
    IndArr[i] = i;
    FP_temp = round(IndArr[i] * (1 << (BW-IW)));
    *BuffPtr = FP_temp;
    BuffPtr++;
  }

  // 2) Write the index to the socket and transfer them to the FPGA 
  int BytesToSend = WAVE_SIZE*sizeof(int);
  int BytesSent   = 0;

  // 3) Write the given template/matrix
  BytesSent = write(sockfd, &FixedPointBuff[0], BytesToSend);
  if (n < 0) error("ERROR writing to socket\n\r");

  // Close the socket
  close(sockfd);

  // Print out a statement
  printf("Index array sent over socket with %d bytes\n\n", BytesSent);

  //Return nothing
  return 0;
  
}
