/*
 * pathchirp_rcv.c
 * Copyright (c) 2003 Rice University
 * All Rights Reserved.
 *
 * Permission to use, copy, modify, distribute, and sell this software
 * and its documentation is hereby granted without
 * fee, provided that the above copyright notice appear in all copies
 * and that both that copyright notice and this permission notice
 * appear in supporting documentation, and that the name of Rice
 * University not be used in advertising or publicity pertaining to
 * distribution of the software without specific, written prior
 * permission.  Rice University makes no representations about the
 * suitability of this software for any purpose.  It is provided "as
 * is" without express or implied warranty. 
 *
 * RICE UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
 * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
 * FITNESS, IN NO EVENT SHALL RICE UNIVERSITY BE LIABLE FOR ANY
 * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
 * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
 * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
 * SOFTWARE.
 *
 * Author: Vinay Ribeiro, vinay@rice.edu.  */

/*
 * "pathchirp_rcv" receives chirp packets from "pathchirp_snd", estimates
 *  available bandwidth and writes this to a file.
 *
 *  pathchirp_rcv.c is based on udpread.c of the NetDyn tool.
 *
 */

/*
 * udpread.c
 * Copyright (c) 1991 University of Maryland
 * All Rights Reserved.
 *
 * Permission to use, copy, modify, distribute, and sell this software and its
 * documentation for any purpose is hereby granted without fee, provided that
 * the above copyright notice appear in all copies and that both that
 * copyright notice and this permission notice appear in supporting
 * documentation, and that the name of U.M. not be used in advertising or
 * publicity pertaining to distribution of the software without specific,
 * written prior permission.  U.M. makes no representations about the
 * suitability of this software for any purpose.  It is provided "as is"
 * without express or implied warranty.
 *
 * U.M. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL U.M.
 * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
 * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 *
 * Author:  Dheeraj Sanghi, Department of Computer Science.
 */

#include "../config.h"
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <sys/param.h>
#include <sys/ioctl.h>

#include <netinet/in.h>
#include <netdb.h>
#include <math.h>
#include "pathchirp.h"
#include <string.h>
#include <strings.h>
#include <unistd.h>
#include "realtime.h"

FILE *fd_instbw,*fd_param;/* file pointers for output files*/

FILE *fd_tmp;/*for debugging*/

double total_inst_bw_excursion=0.0;/* sum of chirp estimates over the number of
                            estimates specified */

double perc_bad_chirps=0.0;/*in the last write interval how many
                             chirps were affected by context
                             switching*/

/* parameters for new algorithm */
double decrease_factor=1.5;
int busy_period_thresh=5;

/*context switching threshold */
double context_receive_thresh=0.00005;/*50us*/

int soudp;/*socket for udp connection */

int num_pkts_to_skip;/*skip this many packets during analysis at each
                       write interval as they may be affected by
                       context switching*/

int pkts_per_write;/* how many packet are expected to arrive at each
                      write interval */

int num_inst_bw=11;/* number of estimates to smooth over for mean */

int inst_head=0;/* pointer to current location in circular buffer */

int inst_bw_count=0;/* total number of chirps used in estimation till
                       now*/

double inter_chirp_time;/*time between chirps as stated by the sender */

double write_interval=0.3; /* how often to write to file */
int pktsize=1000;
double low_rate=1.0,high_rate=120.0;/*rate range in chirp*/
double spread_factor=1.2; /* decrease in spread of packets within the
                             chirp*/

double largest_inter_arrival;

double *qing_delay,*qing_delay_cumsum;
double *rates,*av_bw_per_pkt,*iat;

struct tcprecord tcprecord;	/* log record */
double av_bw_min,av_bw_max;

double *inst_bw_estimates_excursion;/* pointer to interarrivals to look for */

int *missed_pkts;/* which pkts in chirp were missing*/

int num_interarrival;/* number of different interarrivals to keep track of */
int first_pkt=1;/* flag which is made 0 after first packet arrives */

struct pkt_info *packet_info;/* keeps track of packet numbers, receive
                                time, send time*/

int num_pkts_in_info=0;/* how big packet_info currently is*/

int dstPort = RECVPORT;	/* destination port */
int tcpPort = READPORT;	/* destination port */

#define MAXHNAME      140             /* maximum hostname size */

/* usage information */
void usage()
{
  (void) fprintf (stderr,"usage: pathchirp_rcv \n \t -w <write interval (min), default=0.3min> \n \t -n <number of estimates to smooth over, default=11>\n \t -d <decrease factor (>1), default=1.5>\n \t -b <busy period length (integer >2), default=5>\n \t -T <receiver port (TCP setup), default=%d>\n \t -U <receiver port (chirp UDP), default=%d>\n \t -h Help: produces this output\n",READPORT,RECVPORT);
  exit (1);
}


/* recording contents of packet */
void update_info(long np,double snd_time,double rcv_time)
{
  packet_info[num_pkts_in_info].num=np;
  packet_info[num_pkts_in_info].snd_time=snd_time;
  packet_info[num_pkts_in_info].rcv_time=rcv_time;
  num_pkts_in_info++;
  if ( num_pkts_in_info>=1.5*pkts_per_write)
    fprintf(stderr,"Too many packets being stored\n");
}

/*Creates files for writing. The file name is <src name>_<dst
name>_<starting timestamp>.{instbw,par} 

".instbw" contains <time> <mean> 

".par" contains the parameters of the experiment for
future reference 

*/

void open_dump_files(char *src,char *dst)
{

  char param_file_name[MAXFNAME];
  char instbw_file_name[MAXFNAME];
  char time_string[40];
  struct	timeval tv;		/* timestamp */

  /* alloc space for timestamp */
  (void) gettimeofday (&tv, (struct timezone *) 0);
  
  strtok(src,".");
  strtok(dst,".");
  
  strcpy(instbw_file_name,src);
  strcat(instbw_file_name,"_");
  strcat(instbw_file_name,dst);
  strcat(instbw_file_name,"_");
  
  
  sprintf(time_string,"%ld",tv.tv_sec);
  
  strcat(instbw_file_name,time_string);
  strcpy(param_file_name,instbw_file_name);
  strcat(param_file_name,".par");
  
  strcat(instbw_file_name,".instbw");

  fd_instbw = fopen(instbw_file_name, "w");
  if (fd_instbw == NULL) {
    fprintf(stderr, "Error opening dump file\n");
  }

  fprintf(stderr, "Opening file: %s\n",instbw_file_name);

  fd_param = fopen(param_file_name, "w");
  if (fd_param == NULL) {
    fprintf(stderr, "Error opening dump file\n");
  }

  /* only for debug*/
  /*fd_tmp = fopen("algo2_tmp", "w");*/



}

/* read in command line arguements  */

void parse_cmd_line(argc,argv)
  int	argc;
     char	*argv[];
{
  char	*ptr;			/* to traverse the arguments */

      argc--; argv++;

  /* go through the arguments */
   while (argc > 0) {
      ptr = *argv;
      while (*ptr) switch (*ptr++) {

     case 'w':/* lowest rate to probe at */
       if (*ptr == 0) {
	 argc--; argv++;
	 if (*argv == 0) {
	   (void) fprintf (stderr,
			   "pathchirp_snd: no write interval '-w'.\n");
	   (void) exit (1);
	 }
	 write_interval = atof (*argv);
       }
       else {
	 write_interval = atof (*argv);
	 *ptr = 0;
       }

       break;

     case 'b':/* moving average length */
       if (*ptr == 0) {
	 argc--; argv++;
	 if (*argv == 0) {
	   (void) fprintf (stderr,
			   "pathchirp_snd: no busy period length '-b'.\n");
	   (void) exit (1);
	 }
	 busy_period_thresh = atoi (*argv);
       }
       else {
	 busy_period_thresh = atoi (*argv);
	 *ptr = 0;
       }
       fprintf(stderr,"busy period length is %d \n",busy_period_thresh);
       break;

     case 'd':/* moving average length */
       if (*ptr == 0) {
	 argc--; argv++;
	 if (*argv == 0) {
	   (void) fprintf (stderr,
			   "pathchirp_snd: no decrease factor '-d'.\n");
	   (void) exit (1);
	 }
	 decrease_factor = atof (*argv);
       }
       else {
	 decrease_factor = atof (*argv);
	 *ptr = 0;
       }
       fprintf(stderr,"decrease factor is %f \n",decrease_factor);
       break;

     case 'n':/* number of inst. bw. estimates to smooth over*/
       if (*ptr == 0) {
	 argc--; argv++;
	 if (*argv == 0) {
	   (void) fprintf (stderr,
			   "pathchirp_snd: no averaging length '-n'.\n");
	   (void) exit (1);
	 }
	 num_inst_bw = atoi (*argv);
       }
       else {
	 num_inst_bw = atoi (*argv);
	 *ptr = 0;
       }

       break;
           
 case 'T':		/* port */
       if( *ptr == 0) {
	 argc--; argv++;
	 if (*argv == 0) {
	   (void) fprintf (stderr,
			   "pathchirp_rcv: no port number given with '-T'.\n");
	   (void) exit (1);
	 }
	 tcpPort = atoi (*argv);
       }
       else {
	 tcpPort = atoi (ptr);
	 *ptr = 0;
       }
       break;
       

 case 'U':		/* port */
       if( *ptr == 0) {
	 argc--; argv++;
	 if (*argv == 0) {
	   (void) fprintf (stderr,
			   "pathchirp_rcv: no port number given with '-U'.\n");
	   (void) exit (1);
	 }
	 dstPort = atoi (*argv);
       }
       else {
	 dstPort = atoi (ptr);
	 *ptr = 0;
       }
       break;
       
 
      case '-':
	 break;

      case 'h':
      case 'H':
	 usage();
       
      default:
	 (void) fprintf (stderr,
			 "pathchirp_rcv: Unknown option '%c'\n", ptr[-1]);

      }
      argc--; argv++;
   }
 /* checking for negative rates */

  if (write_interval <=0)
     {perror ("pathchirp_rcv: write_interval invalid");
	 (void) exit (1);
     }
  if (decrease_factor <=1.0)
     {perror ("pathchirp_rcv: decrease_factor invalid");
	 (void) exit (1);
     }

  if (busy_period_thresh <=2)
     {perror ("pathchirp_rcv: busy_period_thresh invalid, must be integer >2");
	 (void) exit (1);
     }

}

/* open TCP connection and read parameters from sender */

void tcp_connection()
{
  struct sockaddr_in src_tcp; /* sockets for tcp connections */
   struct sockaddr_in dst_tcp;

   int	rcv_size = MAXRCVBUF;	/* socket receive buffer size */
   int tcprec_size = (int)sizeof(struct tcprecord);
   int so,nso;			/* socket number for incoming conn. */
   
 /*FLAG FOR SETTING SO_REUSEADDR*/
   int flag_on;
   int cc,rsize;


 /*************************** get parameters from TCP connection *********
************/
  fprintf(stderr,"Waiting for parameters from TCP connection\n");

   /* create a socket for receiving log records */
   so = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
   if (so < 0) {
      perror("udpread: tcp socket");
      (void) exit (1);
   }

   /*the following is to use socket even if already bound */
   flag_on=YES;;
      if (setsockopt(so, SOL_SOCKET, SO_REUSEADDR, (char *)&flag_on,
 sizeof(flag_on))<0){
      perror ("udpread: setsockopt failed");
      (void) exit (1);
      }
 

   /* initialize address/port for that socket */
   (void) bzero ((char *)&src_tcp, sizeof (src_tcp));
   src_tcp.sin_family = AF_INET;
   src_tcp.sin_port = htons((u_short) tcpPort);

   /* bind address/port to socket */
   if (bind (so, (struct sockaddr *) &src_tcp, sizeof(src_tcp)) < 0) {
      perror ("udpread: bind");
      (void) exit (1);
   }

   (void) listen (so, 2);
   rsize = sizeof(dst_tcp);

   /* accept the connection */
   nso = accept (so, (struct sockaddr *)&dst_tcp, &rsize);
   if (nso < 0) {
      perror ("udpread: accept");
      (void) exit (1);
   }

   /* set socket receive buffer size to maximum so that we don't
    * get stuck because of lack of memory */
   if (setsockopt (nso, SOL_SOCKET, SO_RCVBUF, (char *) &rcv_size,
		   sizeof (rcv_size)) < 0) {
      perror ("udpread: receive buffer option");
      (void) exit (1);
   }

   /* we can close the original socket now */
   (void) close (so);
   cc = 0;
     do
	{
	 int new_cc;

      	 new_cc = read (nso, (char *) &(((char *)&tcprecord)[cc]),
		 tcprec_size - cc);
      	 if (new_cc < 0) {
	 	perror ("udpread: read");
	 	break;
      		}

	 cc += new_cc;
	}
     while (cc < tcprec_size);


 (void) close (nso);

 inter_chirp_time=(double)(ntohl (tcprecord.inter_chirp_time));
 low_rate=(double)(ntohl (tcprecord.low_rate))/10000.0;
 high_rate=(double)(ntohl (tcprecord.high_rate))/10000.0;
 spread_factor=(double)(ntohl (tcprecord.spread_factor))/10000.0;
 pktsize=(int) ntohl (tcprecord.pktsize);
 num_interarrival=(int) ntohl (tcprecord.num_interarrival);

 num_pkts_to_skip=num_interarrival+1;/*skip the first chirp */


  /************************** end tcp connection ***********************
**************/

}

/* create arrays */

void create_arrays()
{
  int count;

  inst_bw_estimates_excursion=(double *)calloc((int)(num_inst_bw),sizeof(double));

  qing_delay=(double *)calloc((int)(num_interarrival+1),sizeof(double));

  qing_delay_cumsum=(double *)calloc((int)(num_interarrival+1),sizeof(double));

  rates=(double *)calloc((int)(num_interarrival),sizeof(double));
  iat=(double *)calloc((int)(num_interarrival),sizeof(double));

  av_bw_per_pkt=(double *)calloc((int)(num_interarrival),sizeof(double));

  rates[num_interarrival-1]=high_rate*1000000.0;
  iat[num_interarrival-1]=8*((double) pktsize)/rates[num_interarrival-1];

  /* compute instantaneous rates within chirp */
  for (count=num_interarrival-2;count>=0;count--)
    {
      rates[count]=rates[count+1]/spread_factor;
      iat[count]=iat[count+1]*spread_factor;

    }


  missed_pkts=(int *)calloc((int)(num_interarrival+1),sizeof(int));
  memset(missed_pkts, 0, (int)(num_interarrival+1) * sizeof(int));
  
  /*allocating enough memory to store packet information */
  pkts_per_write=(int)((num_interarrival
			+1)*(60.0*write_interval*(1000000.0/inter_chirp_time)));
  packet_info=(struct pkt_info *)calloc(pkts_per_write*2,
					sizeof(struct pkt_info));
  memset(packet_info, 0, (int) pkts_per_write * sizeof(struct pkt_info));
  
  
  
  
}

/* Skip a few packets (return number) that may have arrived during the last
   computation period and could have been held up in the kernel */

int compute_pkts_to_skip(double last_write_time)
{
  int num_pkts_to_skip;
  double cur_time,num_chirps;
  struct	timeval tv;		/* timestamp */
  
  /* get the timestamp */
  (void) gettimeofday (&tv, (struct timezone *) 0);
  
  
  cur_time =  (double) (tv.tv_sec) 
	+ ((double)(tv.tv_usec))/1000000.0;
  
  num_chirps=1+(cur_time-last_write_time)/(inter_chirp_time/1000000.0);
  num_pkts_to_skip = num_chirps*(num_interarrival+1);

  return(num_pkts_to_skip);
}

/* computes bandwidth estimate and writes to to file */
void write_instant_bw(double inst_av_bw_excursion,double time_stamp)
{
  /*  double inst_median=0.0;*/
  double inst_mean=0.0;

  /*taking latest value and removing old one*/

 total_inst_bw_excursion+=inst_av_bw_excursion;
 total_inst_bw_excursion-=inst_bw_estimates_excursion[inst_head];

 inst_bw_estimates_excursion[inst_head]=inst_av_bw_excursion;


 inst_head++;
 /*circular buffer*/
 if (inst_head>=num_inst_bw)
   inst_head=0;

 if (inst_bw_count>num_inst_bw)
   {

     inst_mean=total_inst_bw_excursion/((double) num_inst_bw);

  /* write to file */
     fprintf(fd_instbw,"%f %f\n",time_stamp,inst_mean/1000000.0);

     fflush(fd_instbw);
   }
 else  inst_bw_count++;
   
}

/* compute the instantaneous bandwidth during this chirp, new algorithm */

double compute_inst_bw_excursion()
{
  int cur_loc=0;/* current location in chirp */
  int cur_inc=0;/* current location where queuing delay increases */
  int count;
  double inst_bw=0.0,sum_iat=0.0,max_q=0.0;

  /* set all av_bw_per_pkt to zero */
  memset(av_bw_per_pkt, 0, (int)(num_interarrival) * sizeof(double));
    
  /*find first place with an increase in queuing delay*/
  while(qing_delay[cur_inc]>=qing_delay[cur_inc+1])
    cur_inc++;
  
  cur_loc=cur_inc+1; 
  
  /* go through all delays */
  
  while(cur_loc<=num_interarrival)
    {
      /* find maximum queuing delay between cur_inc and cur_loc*/
      if (max_q<(qing_delay[cur_loc]-qing_delay[cur_inc]))
	max_q=qing_delay[cur_loc]-qing_delay[cur_inc];
      
      /* check if queuing delay has decreased to "almost" what it was at cur_inc*/
      if (qing_delay[cur_loc]-qing_delay[cur_inc]<(max_q/decrease_factor))
	{
	  if (cur_loc-cur_inc>=busy_period_thresh)
	    {
	      for (count=cur_inc;count<=cur_loc-1;count++)
		{
		  /*mark all increase regions between cur_inc and cur_loc as having
		    available bandwidth equal to their rates*/
		  if (qing_delay[count]<qing_delay[count+1])
		    av_bw_per_pkt[count]=rates[count];
		}
	    }
	  /* find next increase point */
	  cur_inc=cur_loc;
	  while(qing_delay[cur_inc]>=qing_delay[cur_inc+1] && cur_inc<num_interarrival)
	    cur_inc++;
	  cur_loc=cur_inc;
	  /* reset max_q*/
	  max_q=0.0;
	}

      cur_loc++;

    }
  /*mark the available bandwidth during the last excursion as the rate at its beginning*/
  if(cur_inc==num_interarrival)
    cur_inc--;
  
  for(count=cur_inc;count<num_interarrival;count++)
    {
      av_bw_per_pkt[count]=rates[cur_inc];
    }
  
  
  /* all unmarked locations are assumed to have available 
     bandwidth described by the last excursion */
  
  for(count=0;count<num_interarrival;count++)
    {
      if (av_bw_per_pkt[count]<1.0)
	av_bw_per_pkt[count]=rates[cur_inc];
      sum_iat+=iat[count];
      inst_bw+=av_bw_per_pkt[count]*iat[count];
    }

  inst_bw=inst_bw/sum_iat;
  return(inst_bw);
  
}

/* check for context switching during chirps */

void check_context_switching()
{
  /**************** check for context switching *********************
		    mark packets of bad chirps        */

  int count,index,first_chirp_pkt_index;
  int num_pkts_this_chirp=0,num_bad_pkts_this_chirp=0;
  u_long np,first_chirp_pkt_num,last_chirp_pkt_num;
  int context_switch=0;
  double num_bad_chirps=0,total_chirps=0;


  np=packet_info[0].num;/*first packet stored*/
  first_chirp_pkt_index=0;
  first_chirp_pkt_num=(np/(num_interarrival+1))*(num_interarrival+1) + 1;

  last_chirp_pkt_num=first_chirp_pkt_num+num_interarrival;
  
  /* going through all packets in pkt_info */
  for (count=1;count<num_pkts_in_info;count++)
    {
      np=packet_info[count].num;
      
      if (np>last_chirp_pkt_num)/*check if new chirp*/
	  { 

	    total_chirps+=1.0;
	    if (context_switch==1)
	      num_bad_chirps+=1.0;

	    for (index=first_chirp_pkt_index;index<count;index++)
	      {
		packet_info[index].context_switch=context_switch;
	      }

	    context_switch=0;
	    /*inst bw parameter initialization */
	    num_pkts_this_chirp=1;
	    num_bad_pkts_this_chirp=0;
	    qing_delay[0]=0.0;
	    /*******************/

	    first_chirp_pkt_index=count;
	    first_chirp_pkt_num=((int)(np/(num_interarrival+1)))
	                        *(num_interarrival+1) + 1;
	    last_chirp_pkt_num=first_chirp_pkt_num+num_interarrival;

	  }
      else
	{
	  qing_delay[num_pkts_this_chirp]=packet_info[count].rcv_time-packet_info[first_chirp_pkt_index].rcv_time-packet_info[count].snd_time+packet_info[first_chirp_pkt_index].snd_time;

	  /* if qing delay of this packet exceeds that of the previous by a factor OR
	   receive time of this packet - receive time of prev. packet < threshold, mark chirp as bad*/
	  if(packet_info[count].rcv_time-packet_info[count-1].rcv_time<context_receive_thresh)
	    {
	      num_bad_pkts_this_chirp++;

	      /*	      fprintf(stdout,"pkt_num=%d,iat=%f,bad_pkts=%d\n",num_pkts_this_chirp,packet_info[count].rcv_time-packet_info[count-1].rcv_time,num_bad_pkts_this_chirp); */ /*debug*/

	      /*need 4 packets affected by context switching for chirp to be
		discarded*/
	      if (num_bad_pkts_this_chirp>4)
		context_switch=1;
	    }
	  num_pkts_this_chirp++;

	}
 
    }

  perc_bad_chirps=100.0*num_bad_chirps/total_chirps;

  if (perc_bad_chirps>50.0)
{  fprintf(stderr,"percentage of bad chirps=%f\n",perc_bad_chirps);
    fprintf(stderr,"WARNING: Too many bad chirps detected\n Packets probably being held up in NIC card/kernel\n");
}
}

/* compute queuing delay, packet drops, and available bandwidth estimates*/
void compute_stats()
{ 
  int first_chirp=0,count,pkt1,count1,first_chirp_pkt_index;
  double total_qing_delay=0.0;
  int num_pkts_this_chirp=0;
  double inst_bw_excursion=0.0;

  u_long np,first_chirp_pkt_num,last_chirp_pkt_num;

  /* mark all packets affected by context switching */
  check_context_switching();

  for (count=0;count<num_interarrival+1;count++)
    {
      missed_pkts[count]=1;
    }

  np=packet_info[0].num;  /* first packet stored*/
  first_chirp_pkt_index=0;
  first_chirp_pkt_num=(np/(num_interarrival+1))*(num_interarrival+1) + 1;

  last_chirp_pkt_num=first_chirp_pkt_num+num_interarrival;
  
  missed_pkts[last_chirp_pkt_num-np]=0;/*packet sucessfully received*/


  /* going through all packets in pkt_info */
  for (count=1;count<num_pkts_in_info;count++)
    {
      np=packet_info[count].num;

      if (np>last_chirp_pkt_num)/*check if new chirp*/
	  { 

	    /*comptue inst bw only if all packets of chirp received and no context switch*/
	    if (first_chirp && packet_info[count-1].context_switch==0 && num_pkts_this_chirp==(num_interarrival+1))
	      {
		    inst_bw_excursion=compute_inst_bw_excursion();
		    write_instant_bw(inst_bw_excursion,packet_info[count].rcv_time);
	      }

	    first_chirp=1;

	    for (count1=0;count1<num_interarrival+1;count1++)
	      {
		missed_pkts[count1]=1;
	      }

	    /*inst bw parameter initialization */
	    total_qing_delay=0.0;
	    num_pkts_this_chirp=1;
	    qing_delay[0]=0.0;
	    /*******************/

	    first_chirp_pkt_index=count;
	    first_chirp_pkt_num=((int)(np/(num_interarrival+1)))
	                        *(num_interarrival+1) + 1;
	    last_chirp_pkt_num=first_chirp_pkt_num+num_interarrival;

	  }
      else
	{

	  if (!packet_info[count].context_switch)
	    {
	      pkt1=last_chirp_pkt_num-packet_info[count].num+1;
	      
	      /* check for instataneous bandwidth */
	      qing_delay[num_pkts_this_chirp]=packet_info[count].rcv_time-packet_info[first_chirp_pkt_index].rcv_time-packet_info[count].snd_time+packet_info[first_chirp_pkt_index].snd_time;
	      
	      total_qing_delay+=qing_delay[num_pkts_this_chirp];
	      num_pkts_this_chirp++;
	      
	    }

	}
      
      missed_pkts[last_chirp_pkt_num-np]=0;/*packet sucessfully received*/
    }

}


/* write parameters of experiment to .par file */

void write_parameters()
{
  fprintf(fd_param,"number of packets per chirp=%d\nprobe packet size=%d\nminimum instantaneous rate within chirp (Mbps)=%f\nmaximum instantaneous rate within chirp (Mbps)=%f\nspread factor=%f\nnumber of minutes between writes to file=%f\nnumber of chirp estimates to smooth over=%d\ndecrease factor=%f\nbusy period threshold=%d\n",num_interarrival+1,pktsize,low_rate,high_rate,spread_factor,write_interval,num_inst_bw,decrease_factor,busy_period_thresh);
 fflush(fd_param);

}

/* receive the chirps and perform available bandwidth estimations */

void receive_chirp_pkts()
{  
double cur_pkt_rcv_time;
double cur_pkt_snd_time;
double last_write_time=0.0;
int num_pkts_since_last_write=0;

  int	*fromlen;
  char	data[MAXMESG];		/* Maxm size packet */

   char localhost[MAXHOSTNAMELEN];/*string with local host name*/
   int	rcv_size = MAXRCVBUF;	/* socket receive buffer size */

   int last_pkt_no_reorder=0;
  long	np;			/* packet number received */
  struct	udprecord *udprecord;	/* log record for a packet */
  /*FLAG FOR SETTING SO_REUSEADDR*/
  int flag_on_recv;


  struct	sockaddr_in src;	/* socket addr for incoming packets */
  
  struct	timeval *tp;		/* timestamp */
  /* alloc space for timestamp */
  tp = (struct timeval *)(malloc (sizeof (struct timeval)));
  
  fromlen = (int *) malloc (sizeof (int *));
  
  /* The first part of the data packet can be accessed
   * as the log record */
  udprecord = (struct udprecord *) data;
  
  /* create a socket to receive UDP packets */
  soudp = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
  if (soudp < 0) {
    perror("pathchirp_rcv: socket");
    exit(1);
  }
  
  /* initialize socket address for incoming connection */
  bzero((char *)&src, sizeof (src));
  src.sin_family = AF_INET;

  src.sin_port = htons((u_short) dstPort);
  
  /*the following is to use socket even if already bound */
  flag_on_recv = YES;
  
  if (setsockopt(soudp, SOL_SOCKET, SO_REUSEADDR,(char *) &flag_on_recv,
            sizeof(flag_on_recv))<0){
    perror ("pathchirp_rcv: setsockopt failed");
      (void) exit (1);
  }
  
  
  
  /* bind the address (port#, family) to the socket */
  if (bind (soudp, (struct sockaddr *) &src, sizeof(src)) < 0) {
    perror ("pathchirp_rcv: bind");
    exit (1);
  }
  
  /* set the socket receive buffer to maximum possible.
   * this will minimize the chance of losses at the endpoint */
  if (setsockopt(soudp, SOL_SOCKET, SO_RCVBUF, (char *)&rcv_size,
		 sizeof (rcv_size)) < 0) {
    perror ("pathchirp_rcv: receive buffer option");
    exit (1);
  }
  
  
  while (1) {
    static 	int cc;		/* char count for recv/send */


      /* read one packet */
      cc = recvfrom (soudp, data, MAXMESG, 0, (struct sockaddr *)0,
		     fromlen);



      if (cc < 0) {
	 perror ("pathchirp_rcv: read");
	 break;
      } 

        /* get the timestamp (kernel timestamp if possible) */


#ifdef HAVE_TIMESTAMP_IOCTL
      if(ioctl(soudp, SIOCGSTAMP, (void *)tp) < 0) {
	 (void) gettimeofday (tp, (struct timezone *) 0);
      }
#else
      (void) gettimeofday (tp, (struct timezone *) 0);
#endif

      /* get the packet number */
      np = (long) ntohl ((u_long) udprecord->num);


      /*
       * to end the connection, sender program will be sending a
       * few packets in the end with packet number 0. It sends
       * more than 1, so that at least one of them makes it to
       * the receiver and the receiver process can stop gracefully.
       */

      if (!np) {
	 (void) fprintf (stderr,
			 "pathchirp_rcv: Finished servicing this client\n");
   
	 break;
      }

      /*check if any packet out of order, if so discard it*/

      if (np>last_pkt_no_reorder)
	{
	  last_pkt_no_reorder=np;

	  cur_pkt_snd_time =  (double) (ntohl (udprecord->snd.tv_sec)) 
	    + ((double)(ntohl (udprecord->snd.tv_usec))/1000000.0);
	  cur_pkt_rcv_time= (double) tp->tv_sec + ((double)tp->tv_usec/1000000.0);
	  
	  if (!first_pkt)
	    {
	      num_pkts_since_last_write++;
	      
	      if (num_pkts_since_last_write>num_pkts_to_skip)
		update_info(np,cur_pkt_snd_time,cur_pkt_rcv_time);
	      
	      /* write output if time exceeded */
	      
	      if (cur_pkt_rcv_time>last_write_time+60.0*write_interval)
		{
		  
		  last_write_time=cur_pkt_rcv_time;
		  compute_stats();

		  num_pkts_in_info=0;
		  num_pkts_since_last_write=0;
		  
		  /* figuring how many pkts to skip */
		  num_pkts_to_skip=compute_pkts_to_skip(last_write_time);
		}
	      
	    }
	  else      /* we have received one packet */
	    {
	      first_pkt=0;
	      last_write_time=cur_pkt_rcv_time;
	      
	      /*OPEN FILE*/
	      gethostname(localhost,sizeof(localhost));
	      open_dump_files(tcprecord.localhost,localhost);

	      write_parameters();

	    }
	  
	}
  }
      
}
  

/* close all open files and sockets */  

void close_all()
{
   (void) close (soudp);
   fflush(fd_instbw);
   fflush(fd_param);

   fclose(fd_instbw);
   fclose(fd_param);
}


/* main function executing all others */

int main(argc,argv)
     int	argc;
     char	*argv[];
{
  parse_cmd_line(argc,argv);

  lockMe();

  tcp_connection();

  create_arrays();

  receive_chirp_pkts();

  close_all();

  return(0);
}



