/* -*-	Mode:C++; c-basic-offset:8; tab-width:8; indent-tabs-mode:t -*- */

/* By Pablo Martin and Paula Ballester,
 * Strathclyde University, Glasgow.
 * June, 2003.
*/

/* Copyright (c) 2003 Strathclyde University of Glasgow, Scotland.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code and binary code must contain
 * the above copyright notice, this list of conditions and the following
 * disclaimer.
 *
 * 2. All advertising materials mentioning features or use of this software
 * must display the following acknowledgement:
 * This product includes software developed at Strathclyde University of
 * Glasgow, Scotland.
 *
 * 3. The name of the University may not be used to endorse or promote
 * products derived from this software without specific prior written
 * permission.
 * STRATHCLYDE UNIVERSITY OF GLASGOW, MAKES NO REPRESENTATIONS
 * CONCERNING EITHER THE MERCHANTABILITY OF THIS SOFTWARE OR THE
 * SUITABILITY OF THIS SOFTWARE FOR ANY PARTICULAR PURPOSE.  The software
 * is provided "as is" without express or implied warranty of any kind.
*/

#include "packet.h"
#include "delay.h"
#include "connector.h"
#include "packet.h"
#include "random.h"
#include "mobilenode.h"
#include "address.h"
#include <unistd.h>
#include "arp.h"
#include "ll.h"
#include "rlc-umts.h"
#include "queue.h"
#include "mac.h"
#include "phy-umts.h"
#include "phy-umts-nodeb.h"
#include "cmu-trace.h"

// TCL class for PhyUmtsNodeB
static class PhyUmtsNodebClass : public TclClass {
public:
	PhyUmtsNodebClass() : TclClass("Phy/UmtsNodeB") {}
	TclObject* create(int, const char*const*) {
		return (new PhyUmtsNodeb());
	}
} class_phy_nodeb_umts;

// variables
double PhyUmtsNodeb::slot_time_ = 0;
double PhyUmtsNodeb::aich_slot_time_ = 0;
int PhyUmtsNodeb::verbose_ = {0};
static int PhyNodeBIndex_ = 1000;

//constructor
PhyUmtsNodeb::PhyUmtsNodeb() : LinkDelay(), pDownSlot_(this), pAichSlot_(this), pTxPkt_(this),
	pRxPkt_(this), pPaging_(this), pBler_(this), downtarget_(0), uptarget_(0), ll_(0)
{
	int i,j;

	bind("verbose_", &verbose_);

	nodeb_address_ = PhyNodeBIndex_++;

	pkttoTx_= new PacketQueue();
	aichtoTx_= new PacketQueue();
	pchtoTx_= new PacketQueue();
	pch_recv_ = NULL;
	paging_temp_ = new PacketQueue();

	slot_time_= 0.000667 ;//umts slot is of 666.6 microseconds
	aich_slot_time_= 0.00125 ;//umts slot is of 1.25 miliseconds
	tx_rate = (int) ((8*80) / slot_time_) ;

	radio_active_ = 0;
	tti_ = 0.0;

	ip_nodeb_ = -1;
	wait_ = 0;

	for (i=0; i<MAX_NUM_SCRAM; i++) {
		for (j=0; j< MAX_NUM_SIG + 1; j++) {
			w_control.free_res_[i][j] = 0;
		}
	}
/*	for (i=0; i< MAX_NUM_SCRAM -1; i++) {
		w_control.dl_scramb_[i] = 0;
	}*/
	for (i=0; i< MAX_NUM_RACH; i++) {
		w_control.free_rach_[i] = 0;
	}

	w_control.p_scrambling_ = nodeb_address_ + 1000;

	for (j=0; j<MAX_NUM_UE; j++){
		data_temp_[j] = new PacketQueue();
	}

	for (j=0; j<MAX_NUM_UE; j++){
		ue_id[j] = -1;
		error[j] = 1000000000;
		addr[j] = -1;
	}

	for (j=0; j<MAX_NUM_UE; j++){
		ue_info_[j].paging_group = -1;
		ue_info_[j].bytes_slot = 0.0;
		ue_info_[j].in_seq_ = 0;
		ue_info_[j].count = 0;
	  	ue_info_[j].ul_ErrorRate = 1000000000;
		ue_info_[j].lastRate = 0;
	  	ue_info_[j].Prx = 0;
		ue_info_[j].ul_freq = -1;
		ue_info_[j].nSamples = 0;
		ue_info_[j].IntPower = 0;
	}

	for (j=0; j<MAX_NUM_UE; j++){
		for (i=0; i<MAX_NUM_DPDCH; i++){
			rx_dpdch[j][i] = NULL;
		}
	}

	for (j=0; j<MAX_NUM_FREQ; j++){
		Ioth[j] = 0;
  		noIoth[j] = 0;
	}
	BTable = new BlerTable();

	intr1_ = intr_ ; //reqd for the slot timers
	intr = intr_ ;
	intrb = intr_;

	// start timers
	pDownSlot_.start((Packet *) (& intr_), 0);
	pAichSlot_.start((Packet *) (& intr1_), 0);
	pBler_.start((Packet *) (& intrb), 0);
}


int PhyUmtsNodeb::command(int argc, const char*const* argv)
{
	Tcl& tcl = Tcl::instance();
	if (argc == 3) {
		if (strcmp(argv[1], "log-target") == 0) {
			logtarget_ = (NsObject*) TclObject::lookup(argv[2]);
			if(logtarget_ == 0)
				return TCL_ERROR;
			return TCL_OK;
		}
		if (strcmp(argv[1], "ll") == 0) {
			ll_ = (LL*) TclObject::lookup(argv[2]);
                  assert(ll_);
			return (TCL_OK);
		}
		if (strcmp(argv[1], "mac") == 0) {
			mac_ = (Mac*) TclObject::lookup(argv[2]);
                  assert(mac_);
			return (TCL_OK);
		}
		if (strcmp(argv[1], "netif") == 0) {
			netif_ = (Phy*) TclObject::lookup(argv[2]);
			return TCL_OK;
		}
		if (strcmp(argv[1], "down-target") == 0) {
			downtarget_ = (NsObject*) TclObject::lookup(argv[2]);
			return (TCL_OK);
		}
		if (strcmp(argv[1], "up-target") == 0) {
			uptarget_ = (NsObject*) TclObject::lookup(argv[2]);
			return (TCL_OK);
		}
	}
	else if (argc == 2) {
		if (strcmp(argv[1], "down-target") == 0) {
			tcl.resultf("%s", downtarget_->name());
			return (TCL_OK);
		}
		if (strcmp(argv[1], "up-target") == 0) {
			tcl.resultf("%s", uptarget_->name());
			return (TCL_OK);
		}
	}
	return LinkDelay::command(argc, argv);
}

// checks the number of dpdch received
int PhyUmtsNodeb::n_rx_dpdch(int id)
{
	int i, n = 0;
	for (i=0 ; i < MAX_NUM_DPDCH ; i++) {
		if (rx_dpdch[id][i] != NULL)
			n++;
	}
	return (n); // number of dpdch received and stored of a user
}

// check if the packet is for this Node B
int PhyUmtsNodeb::packet_for_me(Packet* p)
{
	int i;
	hdr_phy *ph = HDR_PHY_UMTS(p);

	i = look_for(ph->sa());   // checks if the user is in its table of addresses
	if (i != -1){
		// the user is already registered
		ph->sa() = i; // change the user address with the internal addressing
		return (1);
	}

	// the user is not registered
	if (ph->first_access_ == 1){
		// if the first access of that user: setup or handover
		for (i=0; i<MAX_NUM_UE; i++){
			if (ue_id[i] < 0){
				// registered the user in the table of addresses
				ue_id[i] = ph->sa();
				ph->sa() = i; // change the user address with the internal addressing
				if (verbose_==1)
					printf("Nodeb %d at %f PHY: registering UE with phyaddr %d\n", ip_nodeb_, NOW, ue_id[i]);
				return (1);
			}
		}
	}
	// the user is not registered in our cell
	return (0);
}


// process the packets received from the air interface
void PhyUmtsNodeb::recv_from_phy(Packet* p, Handler *h)
{
	int i, j, len;
	hdr_cmn *ch = HDR_CMN(p);
	hdr_phy *ph = HDR_PHY_UMTS(p);

	if (ch->error() == 1) { // if the packet is erroneus drop packet
		Packet::free(p);
		p = NULL;
		return;
	}

	switch (ch->channel_t()) {

		case PRACH:
			// it can be a RACH preamble or a RACH message
			if (ch->ptype_ == PT_PREAMBLE){
				// RACH preamble received
				if (verbose_==1)
					printf("Nodeb %d at %f PHY: preamble from UE phyaddr %d and signature %d\n",ip_nodeb_, NOW, ph->sa(), ph->signature_);
				if (w_control.free_res_[ph->scrambling_c_][ph->signature_] == 0) {
					// store the signature used by the preamble to use it in AICH
					sig_ue_[ph->sa()] = ph->signature_;
					tx_aich(p);  // transmit AICH message
				} else {   // the signature was not free
					Packet::free(p);
					break;
				}
			} else {  // message RACH
				// RACH control part, we don't need it, so drop it
				if ((ph->sf_ == 256) && (ph->k_ == 16 * sig_ue_[ph->sa()] + 15)) {
					Packet::free(p);
					p = NULL;
					break;
				} else { //  RACH data part
					if (ph->bofsec_ == 1) {
						// begin of RACH data part, check if the resources were free
						if (w_control.free_res_[ph->scrambling_c_][0] == 0) {
							// the resources were free, allocate them
							w_control.free_res_[ph->scrambling_c_][0] == 1;
							// now the resources are not available
							// check if is the paging response
							hdr_ll *lh = HDR_LL(p);
							if (lh->paging_ok_) {
								// is a paging response, take the paging message stored
								hdr_phy *php = HDR_PHY_UMTS(pch_recv_);
								if (ue_id[ph->sa()] == php->da()){
									// is the paging response expected
									if (verbose_==1)
										printf("Nodeb %d at %f PHY: Paging_ok_ rec from UE phyaddr %d\n", ip_nodeb_, NOW, ph->sa());
									pPaging_.stop(); // stop the paging timer
									// empty pch queue
									while(pchtoTx_->length() > 0) {
										Packet::free(pchtoTx_->deque());
									}
									wait_ = 0;   // to begin more paging procedures
									ch->channel_t() = RACH;  // ch mapping
									uptarget_->recv(p, this);  // send to MAC
									Packet::free(pch_recv_);
									pch_recv_ = NULL;
									// check if there is other rach waiting to be transmited
									if (paging_temp_->length() > 0){
										wait_ = 1;
										// take other PCH, and begin paging request
										pch_recv_ = paging_temp_->deque();
										tx_pich(pch_recv_->copy()); // transmit PICH
									}
									break;
								} else {
									// is not the response expected, drop packet
									Packet::free(p);
									p = NULL;
								}
							}
						} else {
							Packet::free(p);
							break;
						}
//						if (verbose_==1)
//							printf("Nodeb %d at %f PHY: rach with sf %d, k %d\n", ip_nodeb_, NOW,ph->sf_, ph->k_);
						ch->channel_t() = RACH;  // ch mapping
						ph->sa_ = ue_id[ph->sa()];	// change the source address
						uptarget_->recv(p, this); // send the RACH to MAC
					} else {
						// is the end of RACH message
						if (ph->eofsec_ == 1){
							// free resources
							w_control.free_res_[ph->scrambling_c_][0] == 0;
						}
						Packet::free(p);
						p = NULL;
					}
				}
			}
			break;

		case DPCCH:
			if (verbose_==1)
				printf("Nodeb %d at %f PHY: DPCCH arrived with %d DPDCHs and rx power %e\n",
					ip_nodeb_, NOW, ph->dpcch_control().n_dpdch_, ph->rx_power());
			if ((ph->sf_ == 256) && (ph->k_ == 0)) {
				// taking power meassurement for calculating the interference
				ue_info_[ph->sa()].Prx = ph->rx_power();  // power
				ue_info_[ph->sa()].lastRate = ph->dpcch_control().dpdch_rate_; // transmission rate
				ue_info_[ph->sa()].nSamples ++;
				ue_info_[ph->sa()].IntPower += ph->rx_power();
				// take number of dpdch
				d_control[ph->sa()].n_dpdch_ = ph->dpcch_control().n_dpdch_;
				len = n_rx_dpdch(ph->sa());  // check the number of dpdchs received
				// check if all dpdchs transmitted has been received
				if (len != d_control[ph->sa()].n_dpdch_){
					// they have not been received, drop the dpdch stored
					for (j=0; j<len; j++) {
						Packet::free(rx_dpdch[ph->sa()][j]);
						rx_dpdch[ph->sa()][j] = NULL;
					}
					break;
				}
				// all dpdch has been received
				decode(ph->sa());  // decode the dpdchs
				Packet::free(p);
			} else {
				Packet::free(p);
			}
			break;
		case DPDCH:
			// store DPDCH packet in buffer in the next free possition
			len = n_rx_dpdch(ph->sa());
			rx_dpdch[ph->sa()][len] = p;
			break;

		default:
			break;
	}

	return;
}

// receive all the packets, either with UP or DOWN direction
void PhyUmtsNodeb::recv(Packet* p, Handler *h)
{
	hdr_cmn *ch = HDR_CMN(p);
	hdr_phy *ph = HDR_PHY_UMTS(p);
	hdr_ll *lh = HDR_LL(p);

	double stime;
	int i,len;

	if (ch->direction() == hdr_cmn::UP) {

		// for the interference model
		if ((ch->channel_t() == DPCCH) && (ph->freq() < 12)){
			// cpcch received in uplink frequence
			Ioth[ph->freq()] += ph->rx_power();
			noIoth[ph->freq()]++;
		}
		if (ch->error() == 1) {
			// if packet with error, drops it
			Packet::free(p);
			return;
		}

		if (nodeb_address_ == ph->da()) {
			// if the packet is for me
			if (packet_for_me(p)) {   // checks if the user is registered in its cell
				// simulation of reception time
				radioSwitch(ON); // turns the radio ON
				stime = TX_Time(p);  // calculates transmission time
				pRxPkt_.start(p, stime);  // stats reception timer
			} else { // the user is not register in this cell
				Packet::free(p);
				p=NULL;
			}
		} else {
			// the packet is not for me, drop it
			Packet::free(p);
			p=NULL;
		}

	}

	if (ch->direction() == hdr_cmn::DOWN) {
		switch (ch->channel_t()) {
			//case BCH:
			case PCH:
				// paging request received in PCH channel
				// checks if there is another paging procedure before
				if (wait_) { // there's another paging procedure running
					paging_temp_->enque(p);
				} else {
					// there is not paging procedure running
					wait_ = 1;
					pch_recv_ = p->copy();  // stores the paging message
					tx_pich(p);  // transmits PICH
				}
				break;

			case FACH:
				// this channel is goint to be used for:
				// setup and handover reply, resource allocation and RLC ACK mode
				if (lh->lltype() == LL_SETUP_REPLY) {
					// setup or handover procedure
					ue_registry(p);  // registries the new UE
					i = look_for(ph->da());  // looks for the UE in the addresses registry
					ue_info_[i].ul_freq = ph->freq();
				}
				if (lh->lltype() == LL_RES_REPLY) {
					// for resource allocation
					i = look_for(ph->da());
					ue_info_[i].ul_freq = ph->freq();
				}
				tx_fach(p);  // transmits FACH message
				break;

			case DCH:
				// user dedicated data channel
//				if (verbose_==1)
//					printf("Nodeb %d at %f PHY: pkt arrived at NodeB from MAC with %d and direction:%d\n", ip_nodeb_, NOW, ch->uid(), ch->direction());
				dl_mux(p);  // calls to the downlink multiplexing channel
				break;
			default:
				Packet::free(p);
				p = NULL;
				break;
		}
	}
	return;
}

/*
// looking for an available Scrambling Code
int PhyUmtsNodeb::scrambling_allot()
{
	int i = 0;
	i = Random::integer(MAX_NUM_SCRAM-1);
	if (w_control.dl_scramb_[i] == 0)
		w_control.dl_scramb_[i] = 1;
	return (i);
}
*/

// looks for a UE possition in the addressing array
int PhyUmtsNodeb::look_for(int phyaddr)
{
	int i;
	for (i=0; i<MAX_NUM_UE; i++) {
		if (ue_id[i] == phyaddr)
			return(i);  // internal address of the UE with physical address: phyaddr
	}
	return(-1); // UE not found
}

// registries the UE
void PhyUmtsNodeb::ue_registry(Packet* p)
{
	int i, j;

	hdr_phy *ph = HDR_PHY_UMTS(p);
	hdr_ll *lh = HDR_LL(p);

	if (verbose_==1)
		printf("Nodeb %d at %f PHY: giving paging group to UE phyaddr %d\n",ip_nodeb_, NOW, ph->da());
	if (lh->lltype() != LL_FAILURE){
		// there is enough resources for the new UE
		for (i=0; i<MAX_NUM_UE; i++) {
			j = Random::integer(10);
			if (ue_id[i] == ph->da()) {
				// gives the UE a random paging group
				ue_info_[i].paging_group = j;
				break;
			}
		}
	} else {
		// there is not enough resources to support the new UE
		i = look_for(ph->da());
		if (i != -1) {
			// removes the resources already allocated for that UE
			ue_id[i] = -1;
		}
	}
	return;
}

// transmits the FACH message
void PhyUmtsNodeb::tx_fach(Packet* p)
{
	int i, pg;
	hdr_cmn *ch = HDR_CMN(p);
	hdr_phy *ph = HDR_PHY_UMTS(p);
	hdr_ll *lh = HDR_LL(p);

	if (lh->lltype() == LL_SETUP_REPLY) {
		// for setup and handover procedure gives the UE its paging group
		i = look_for(ph->da());
		ph->paging_group_ = ue_info_[i].paging_group;
	}

	// fill physical and common headers
	ph->size()=1.25;
	ph->scrambling_c_ = w_control.p_scrambling_;	// primary scrambling code
	ph->sf_ = 256;  			// spreading factor

	// calculates the size in bytes depending on the spreading factor chosen
	ch->size_=(int)(UMTS_TBLength / (ph->sf_ * 8));
	if ((UMTS_TBLength % (ph->sf_ * 8)) != 0){
		ch->size_++;
	}
	ch->channel_t()=SCCPCH; // ch mapping

	if ((verbose_==1) && (ch->ptype() != PT_ACK))
		printf("Nodeb %d at %f PHY: tx FACH to ue %d\n", ip_nodeb_, NOW, ph->da());

	if ((ch->ptype() == PT_ACK) || (lh->lltype() == LL_RELEASE_REQ) || (lh->lltype() == LL_RELEASE_REPLY)){
		// transmit only in one slot, more is not needed
		ph->bofsec_ = 1; // begin of FACH tx
		ph->eofsec_ = 1; // end of FACH tx
		pkttoTx_->enqueHead(p->copy());   // stores the packet in the transmission buffer
      }else {
		// FACH transmission during a frame
		for (i=0; i<14; i++){
			if (i == 0){
				ph->bofsec_ = 1; // begin of FACH tx
			} else {
				ph->bofsec_ = 0;
			}
			if (i == 14)
				ph->eofsec_ = 1; // end of FACH tx
			pkttoTx_->enque(p->copy());   // stores the packet in the transmission buffer
		}
	}
	Packet::free(p);
	p = NULL;

	return;
}

// transmits AICH
void PhyUmtsNodeb::tx_aich(Packet* p)
{
	hdr_cmn *ch = HDR_CMN(p);
	hdr_phy *ph = HDR_PHY_UMTS(p);
	hdr_ip *ih = HDR_IP(p);
	Packet* sp = Packet::alloc();
	hdr_cmn *chsp = HDR_CMN(sp);
	hdr_phy *phsp = HDR_PHY_UMTS(sp);

	// fill physical and common headers

	phsp->size()=1.25;
	phsp->sa_ = nodeb_address_;	// source address
	phsp->da_ = ue_id[ph->sa()];		// destination address

	phsp->scrambling_c_ = ph->scrambling_c_;	// scrambling code
	phsp->signature_ = ph->signature_;		// signature
	phsp->sf_ = 256;  			// spreading factor
	phsp->k_ = ph->k_;	// k for the channelisation code

	// calculates the size in bytes depending on the spreading factor chosen
	chsp->size_=(int)(UMTS_TBLength / (phsp->sf_ * 8));
	if ((UMTS_TBLength % (phsp->sf_ * 8)) != 0){
		chsp->size_++;
	}
	chsp->direction_ = hdr_cmn::DOWN;
	chsp->error()= 0;
	chsp->channel_t()=AICH;   // ch mapping
	chsp->next_hop() = ch->prev_hop_;  

	if (verbose_==1)
		printf("Nodeb %d at %f PHY: AICH sent to UE phyaddr %d with signature: %d\n", ip_nodeb_, NOW, phsp->da(), phsp->signature_);
	aichtoTx_->enque(sp->copy()); // stores the packet in the AICH transmission buffer
	Packet::free(p);
	p = NULL;
	Packet::free(sp);
	sp = NULL;
	return;
}


// transmits PICH
void PhyUmtsNodeb::tx_pich(Packet* p)
{
	int uid, i;
	hdr_phy *ph = HDR_PHY_UMTS(p);
	Packet* sp = Packet::alloc();
	hdr_cmn *chsp = HDR_CMN(sp);
	hdr_phy *phsp = HDR_PHY_UMTS(sp);

	// fill physical and common headers

	phsp->sa_ = nodeb_address_;	// source address
	phsp->da_ = BROADCAST;	// destination address
	phsp->size()=2.25;  // 2.25 bytes por slot

	// looks for the UE destiny of the paging
	uid = look_for(ph->da());
	if (uid == MAX_NUM_UE) { // error!! we don't have this ue in our cell
		Packet::free(sp);
		Packet::free(p);
		return;
	}

	// the ue is registered in our cell
	phsp->pi() = ue_info_[uid].paging_group;  // allocates the UE destiny paging group
	phsp->scrambling_c_ = w_control.p_scrambling_;	// primary scrambling code
	phsp->sf_ = 256;  			// spreading factor

	// calculates the size in bytes depending on the spreading factor chosen
	chsp->size_=(int)(UMTS_TBLength / (phsp->sf_ * 8));  // bytes
	if ((UMTS_TBLength % (phsp->sf_ * 8)) != 0){
		chsp->size_++;
	}

	chsp->direction_ = hdr_cmn::DOWN;
	chsp->error()= 0;
	chsp->channel_t()=PICH;  // ch mapping
	chsp->next_hop() = PHY_BROADCAST;

	if (verbose_==1)
		printf("Nodeb %d at %f PHY: sending PICH to paging group %d\n", ip_nodeb_, NOW,phsp->pi());
	pkttoTx_->enque(sp->copy());   // stores the packet in the transmission buffer

	tx_pch(pch_recv_->copy());   // transmits the paging message
	Packet::free(p);
	p = NULL;
	Packet::free(sp);
	sp = NULL;
	return;
}

// Transmits the paging message
void PhyUmtsNodeb::tx_pch(Packet* p)
{
	int uid, i;
	hdr_cmn *ch = HDR_CMN(p);
	hdr_phy *ph = HDR_PHY_UMTS(p);
	hdr_ip *ih = HDR_IP(p);

	// fill physical and common headers

	ph->scrambling_c_ = w_control.p_scrambling_;	// primary scrambling code
	ph->sf_ = 256;  			// spreading factor

	// calculates the size in bytes depending on the spreading factor chosen
	ch->size_=(int)(UMTS_TBLength / (ph->sf_ * 8));  // bytes
	if ((UMTS_TBLength % (ph->sf_ * 8)) != 0){
		ch->size_++;
	}
	ch->error()= 0;
	ch->channel_t()=SCCPCH;  // ch mapping
	ch->next_hop() = ih->daddr();

	// it's transmited during a frame after the PICH
	for (i=0; i<15; i++){
		if (i == 0){
			ph->bofsec_ = 1; // begin of PCH tx
		} else {
			ph->bofsec_ = 0;
		}
		if (i == 15){
			ph->eofsec_ = 1; // end of PCH tx
		}
		pchtoTx_->enque(p->copy());  // stores the packet in the transmission buffer
	}

	if (verbose_==1)
		printf("Nodeb %d at %f PHY: sending PCH to UE phyaddr %d\n", ip_nodeb_, NOW, ph->da());
	pPaging_.start(0.02); // stats the paging timer and waits response for two frames
	Packet::free(p);
	p = NULL;
	return;
}

// make P-CCPCH for broadcasting
void PhyUmtsNodeb::mk_pccpch(void)
{
	int i, j;
	Packet* sp = Packet::alloc();
	hdr_cmn *chsp = HDR_CMN(sp);
	hdr_phy *phsp = HDR_PHY_UMTS(sp);

	// fill physical and common headers

	phsp->size() = 1.125;
	// for broadcast
	for (i=0; i<MAX_NUM_SCRAM; i++){
		for (j=0; j<MAX_NUM_SIG + 1; j++){
			// broadcast the free uplink scrambling codes and signatures
			phsp->free_resources_[i][j] = w_control.free_res_[i][j];
		}
	}

//	for (i=0; i<MAX_NUM_SCRAM - 1; i++){
//		phsp->used_dl_scramb_[i] = w_control.dl_scramb_[i];
//	}

	for (i=0; i<MAX_NUM_RACH; i++){
		// broadcasts the free RACH channels
		phsp->rach_[i] = w_control.free_rach_[i];
	}

	phsp->sa_ = nodeb_address_;	// source address
	phsp->da_ = BROADCAST;		// destination address

	phsp->scrambling_c_ = w_control.p_scrambling_;	// primary scrambling code
	phsp->sf_ = 256;  			// spreading factor
	phsp->k_ = 1;	// k for the channelisation code

	chsp->direction_ = hdr_cmn::DOWN;
  	// calculates the size in bytes depending on the spreading factor chosen
	chsp->size_=(int)(2304 / (phsp->sf_ * 8));  // 2304 chips per slot
	if ((2304 % (phsp->sf_ * 8)) != 0){
		chsp->size_++;
	}
	chsp->error()= 0;
	chsp->channel_t() = PCCPCH; // ch mapping
	chsp->next_hop() = PHY_BROADCAST;

	pkttoTx_->enqueHead(sp->copy()); // stores the packet in head the transmission buffer
						   // it has to be the first packet transmitted in each slot
 	Packet::free(sp);
	sp = NULL;
	return;
}

// make SCH for broadcasting
void PhyUmtsNodeb::mk_sch(void)
{
	Packet* sp = Packet::alloc();
	hdr_cmn *chsp = HDR_CMN(sp);
	hdr_phy *phsp = HDR_PHY_UMTS(sp);

	// fill physical and common headers

	chsp->size_ = 2;  // 256 chips in 1 slot, it has not got scrambling code
	chsp->direction_ = hdr_cmn::DOWN;
	chsp->error()= 0;
	chsp->channel_t()=SCH;   // ch mapping
	chsp->next_hop() = PHY_BROADCAST;

	phsp->p_scrambling_c_ = w_control.p_scrambling_;	// primary scrambling code
										// to broadcast in the cell.
	phsp->sa_ = nodeb_address_;	// source address
	phsp->da_ = BROADCAST;		// destination address
	phsp->size() = 1.25;

	pkttoTx_->enque(sp->copy());  // stores the packet in the transmission buffer

	Packet::free(sp);
	return;
}


// uplink multiplexing chain
void PhyUmtsNodeb::dl_mux(Packet* p)
{
	int i, j;
	hdr_cmn *chp = HDR_CMN(p);
	hdr_phy *php = HDR_PHY_UMTS(p);
	hdr_rlc_umts *rhp = HDR_RLC_UMTS(p);

	c_coding(p);   // channel coding
	php->size() = chp->size_;  // stores the packet size

	i = look_for(php->da());
	// stores the packet in each user buffer to send them in dpdchs
	data_temp_[i]->enque(p);
	return;
}

// decodes data with direction UP
void PhyUmtsNodeb::decode(int id)
{
	int i, j;
	Packet* p;
	hdr_phy *phrecv;
	hdr_cmn *chp;
	hdr_ip *ihp;
	hdr_phy *php;

	// demux DPDCH and extract packets
	for (i=0; i<d_control[id].n_dpdch_; i++) {
		phrecv = HDR_PHY_UMTS(rx_dpdch[id][i]);
		// go through the array extracting packets
		while (phrecv->dpdch_control_->length() > 0) {
			p = phrecv->dpdch_control_->deque();
			chp = HDR_CMN(p);
			ihp = HDR_IP(p);
			php = HDR_PHY_UMTS(p);
			// Previusly packets arrived not in sequence
			if (ue_info_[id].in_seq_ == -1) {
				if (php->seqno_ == 0) {
					// first physical fragment of a packet
					if (php->eofsec_ == 1) {
						// last physical fragment of a packet
						if (verbose_==1)
							printf("Nodeb %d at %f PHY: decode: eofseq: pkt of flow %d uid %d and size %d\n",
								ip_nodeb_, NOW, ihp->flowid(),chp->uid(),chp->size());
						chp->channel_t() = DCH; // ch mapping
						ul_demux(p->copy()); // send it to uplink demux chain
						ue_info_[id].in_seq_ = 0;  // waiting for fragment 0
					} else {
						// is not the last fragment, drop it
						if (verbose_==1)
							printf("Nodeb %d at %f PHY:  decode: pkt in seq: %d ue %d flow %d uid %d and size %d\n",
								ip_nodeb_, NOW, php->seqno_, php->sa(), ihp->flowid(),chp->uid(),chp->size());
						ue_info_[id].in_seq_ = 1;  // waiting fragment number 1
						Packet::free(p);
					}
				} else {
					// is not the first fragment, drop the packet
					if (verbose_==1)
						printf("Nodeb %d at %f PHY: decode: not seq: Drop pkt\n",ip_nodeb_, NOW);
					Packet::free(p);
				}
			} else if (php->seqno_ == ue_info_[id].in_seq_) { // Previous packets arrived in sequence
				// is the fragment that we expected
				if (php->eofsec_ == 1) {
					// is the last physical fragment, send it to MAC
					if (verbose_==1)
						printf("Nodeb %d at %f PHY: decode: pkt of flow %d uid %d and size %d\n",
							ip_nodeb_, NOW, ihp->flowid(),chp->uid(),chp->size());
					chp->channel_t() = DCH;  // ch mapping
					ul_demux(p->copy());  // send it to demux chain
					ue_info_[id].in_seq_ = 0;  // waiting fragment number 0
				} else {
					// is not the last fragment, drop the packet
					ue_info_[id].in_seq_++;
					if (verbose_==1)
						printf("Nodeb %d at %f PHY: decode: pkt in seq: %d ue %d flow %d uid %d and size %d\n",
							ip_nodeb_, NOW, php->seqno_, php->sa(), ihp->flowid(),chp->uid(),chp->size());
					Packet::free(p);
				}
			} else {  // Previous packets arrived in sequence
				// Previous packets arrived in sequence
				if (verbose_==1)
					printf("Nodeb %d at %f PHY: decode: pkt not in seq: %d waiting %d ue %d flow %d uid %d and size %d\n",
						ip_nodeb_, NOW, php->seqno_, ue_info_[id].in_seq_, php->sa(), ihp->flowid(),chp->uid(),chp->size());
				ue_info_[id].in_seq_ = -1;  // not in sequence
				Packet::free(p);
			}
  		}

		Packet::free(rx_dpdch[id][i]);  // remove resources
		rx_dpdch[id][i] = NULL;
	}
	return;
}

// uplink demultiplexing chain
void PhyUmtsNodeb::ul_demux(Packet* p)
{
	int i;
	hdr_cmn *chp = HDR_CMN(p);
	hdr_phy *php = HDR_PHY_UMTS(p);

	chp->direction_ = hdr_cmn::UP;
	c_decoding(p);			// channel decoding

	uptarget_->recv(p, this);  // sends the packet to the MAC layer
	return;
}

// channel coding
void PhyUmtsNodeb::c_coding(Packet* p)
{
	hdr_cmn *chp = HDR_CMN(p);
	hdr_phy *php = HDR_PHY_UMTS(p);

	switch (php->c_coding()) {
		case TURBO:  // turbo coding
			chp->size() = 3*chp->size();
			break;
		case CONV_HALF:  // convolutional half
			chp->size() = 2*chp->size();
			break;
		case CONV_THIRD:    // convolutional third
			chp->size() = 3*chp->size();
			break;
		default:
			break;
	}
	return;
}

// channel decoding
void PhyUmtsNodeb::c_decoding(Packet* p)
{
	hdr_cmn *chp = HDR_CMN(p);
	hdr_phy *php = HDR_PHY_UMTS(p);

	switch (php->c_coding()) {
		case TURBO:  // turbo coding
			chp->size() = (int)(chp->size()/3);
			break;
		case CONV_HALF:   // convolutional half
			chp->size() = (int)(chp->size()/2);
			break;
		case CONV_THIRD:   // convolutional third
			chp->size() = (int)(chp->size()/3);
			break;
		default:
			break;
	}
	return;
}

// builds the pdpdchs and dpcch to transmit
void PhyUmtsNodeb::mk_dpxch(void)
{
	int i, j, uid, k, sf_, k_, index_c_, n_dpdch, freq;
	double index, temp, sum, mod, total_size, tbytes;
	nsaddr_t ipdaddr;
	hdr_cmn * chp;
	hdr_phy * php;
	hdr_ip * ihp;

	// builds dpdchs and dpcch for each UE receiving in its cell
	for (uid=0; uid < MAX_NUM_UE; uid++) {
		freq = -1;
		tbytes = 0.0;
		ipdaddr = -1;
		total_size = ue_info_[uid].bytes_slot;
		if (data_temp_[uid]->length() > 0) {
			index_c_ = w_control.p_scrambling_;  // primary scrambling code
			Packet* p;
			if (total_size > MAX_NUM_BYTES_PER_SLOT_DL){
				// it is necessary two or more dpdchs
				n_dpdch = (int)(total_size / MAX_NUM_BYTES_PER_SLOT_DL);
				mod = (int)(total_size) % MAX_NUM_BYTES_PER_SLOT_DL;
				if (mod > 0)
					n_dpdch++;
				if (n_dpdch > MAX_NUM_DPDCH)
					n_dpdch = MAX_NUM_DPDCH;
				// n_dpdch is the number of dpdchs requiered
				// building dpdchs
				for (i=0; i<n_dpdch; i++){
					Packet* sp = Packet::alloc();
					hdr_cmn *chsp = HDR_CMN(sp);
					hdr_phy *phsp = HDR_PHY_UMTS(sp);
					phsp->dpdch_control_ = new PacketQueue();
					phsp->da() = uid;
					phsp->size() = 0;
					phsp->n_dpdch_ = i+1;
					sum = 0.0;
					// if there are messages in the queue to be sent
					while (data_temp_[uid]->length() > 0){
						p = data_temp_[uid]->deque();
						php = HDR_PHY_UMTS(p);
						ihp = HDR_IP(p);
						ipdaddr = ihp->daddr();
						freq = php->freq();
						sum += php->size();
						// check if there is enough space in that DPDCH
						if (sum <= MAX_NUM_BYTES_PER_SLOT_DL) {
							// there is enough space for the whole packet
							php->seqno_ = ue_info_[uid].count;
							php->eofsec_ = 1;
							phsp->dpdch_control_->enque(p->copy());
							phsp->size() += php->size();
							ue_info_[uid].count = 0;
							Packet::free(p);
							if (sum == MAX_NUM_BYTES_PER_SLOT_DL){
								break;
							}
						} else {
							// no space to allocate all the packet in the current DPDCH
							sum -= php->size();
							temp = php->size();
							php->size() = MAX_NUM_BYTES_PER_SLOT_DL - sum;
							php->seqno_ = ue_info_[uid].count;
							php->eofsec_ = 0;
							// sending a part of the message
							phsp->dpdch_control_->enque(p->copy());
							phsp->size() = phsp->size() + MAX_NUM_BYTES_PER_SLOT_DL - sum;
							php->size() = temp - (MAX_NUM_BYTES_PER_SLOT_DL - sum);
							ue_info_[uid].count++;
							// storing the rest of the packet in the buffer
							data_temp_[uid]->enqueHead(p);
							break;
						}
					}
					sf_ = 4; // spreading factor
					// calculates k_
					if (i==0 || i==1)
						k_ = 1;
					if (i==2 || i==3)
						k_ = 3;
					if (i==4 || i==5)
						k_ = 2;
					chsp->channel_t()=DPDCH; // ch mapping
					chsp->next_hop() = ipdaddr;
					phsp->freq() = freq;  // downlink freq for that user
					send_msg(sp->copy(), index_c_, sf_, k_);  // sending DPDCH
					Packet::free(sp);
					sp = NULL;
				}
			} else {
				// only 1 DPDCH is needed
				// building DPDCH packet

				n_dpdch = 1;
				// calculate the necessary spreading factor for that application
				index = total_size;
				index = (2 * 320 / total_size);
				index = (int)(log(index)/log(2));
				sf_ = 2;
				for (i=0; i<((int)index - 1); i++) {
					sf_ *= 2;
				}
				// sf_ is the spreading factor chosen

				Packet* sp = Packet::alloc();
				hdr_cmn *chsp = HDR_CMN(sp);
				hdr_phy *phsp = HDR_PHY_UMTS(sp);
				phsp->dpdch_control_ = new PacketQueue();
				phsp->da() = uid;
				phsp->size() = 0;
				phsp->n_dpdch_ = 1;
				n_dpdch = 1;
				// extract packets from the buffer to transmit
				while ((tbytes < total_size) && (data_temp_[uid]->length() > 0)){
					p = data_temp_[uid]->deque();
					php = HDR_PHY_UMTS(p);
					ihp = HDR_IP(p);
					ipdaddr = ihp->daddr();
					freq = php->freq();
					hdr_rlc_umts *rhp = HDR_RLC_UMTS(p);
					tbytes += php->size();
					if (tbytes <= total_size) {
						// checks if is possible to send the whole packet
						php->seqno_ = ue_info_[uid].count;
						php->eofsec_ = 1;
						phsp->dpdch_control_->enque(p->copy());
						phsp->size() += php->size();
						ue_info_[uid].count = 0;
						Packet::free(p);
					} else {
						// no space to allocate all the packet in the current DPDCH
						tbytes -= php->size();
						temp = php->size();
						php->size() = total_size - tbytes;
						php->seqno_ = ue_info_[uid].count;
						php->eofsec_ = 0;
						// sending a part of the message
						phsp->dpdch_control_->enque(p->copy());
						phsp->size() = phsp->size() + total_size - tbytes;
						php->size() = temp - (total_size - tbytes);
						ue_info_[uid].count++;
						// storing the rest of the packet in the buffer
						data_temp_[uid]->enqueHead(p);
						break;
					}
				}
				k_ = sf_/4;  // calculates k_
				chsp->channel_t()=DPDCH;  // ch mapping
				phsp->freq() = freq;
				chsp->next_hop() = ipdaddr;
				// meter paquete en la cola de tx.
				send_msg(sp->copy(), index_c_, sf_, k_); // send packet over the air interface
				Packet::free(sp);
				sp = NULL;
			}
			// building DPCCH
			Packet* sp = Packet::alloc();
			hdr_cmn *chsp = HDR_CMN(sp);
			hdr_phy *phsp = HDR_PHY_UMTS(sp);
			// calculate de physical rate transmitted
			phsp->dpcch_control().dpdch_rate_ = (2 * 2560 / (UMTS_SlotTime * sf_)) * n_dpdch;
			phsp->da() = uid;
			chsp->channel_t()=DPCCH;
			phsp->freq() = freq;
			phsp->size() = 1.25;
			phsp->dpcch_control().n_dpdch_ = n_dpdch;

			if (verbose_==1)
			printf("Nodeb %d at %f PHY: DPCCH/%dDPDCH with SF=%d sent to UE phyaddr %d in downslot %d\n"
				,ip_nodeb_, NOW, phsp->dpcch_control_.n_dpdch_, sf_, phsp->da(), down_slot_);
			chsp->next_hop() = ipdaddr;
			send_msg(sp->copy(), index_c_, 256, 0);
			Packet::free(sp);
			sp = NULL;
			continue;
		}
	}
	return;
}

// sends data packets to the terminals
void PhyUmtsNodeb::send_msg(Packet* p, int scram_c, int sf, int k)
{
	int i, ue_address;
	hdr_cmn *chp = HDR_CMN(p);
	hdr_phy *php = HDR_PHY_UMTS(p);

	// get the ue destination address
	ue_address = ue_id[php->da()];

	php->sa_ = nodeb_address_;	// source address
	php->da_ = ue_address;		// destination address

	php->scrambling_c_ = scram_c;	// scrambling code
	php->sf_ = sf;  			// spreading factor
	php->k_ = k;			// k for the channelisation code

	// calculates size in bytes depending on the spreading factor chosen
	chp->size_=(int)(UMTS_TBLength / (php->sf_ * 8));  // bytes
	if ((UMTS_TBLength % (php->sf_ * 8)) != 0){
		chp->size_++;
	}

	chp->error()= 0;

	pkttoTx_->enque(p->copy());		// prepare pkt to tx onto air interface
	Packet::free(p);
	p=NULL;
	return;
}


// sends directives directly to the RRC layer
void PhyUmtsNodeb::send_to_rrc(Packet* p)
// method for communicating with the RRC layer. Possible types of messages are:
//	- PT_RRC_FAILURE	Failure when trying to tx RACH
//	- LL_HANDOVER	For handover decission
//	- LL_PAGING		Check the paging group broadcasted in PICH
{
	hdr_cmn *chp = HDR_CMN(p);
	hdr_phy *php = HDR_PHY_UMTS(p);

	chp->direction_ = hdr_cmn::UP; // puts direction UP
	chp->error()= 0;
	ll_->recv(p, (Handler*) 0);  // sends to RRC
}

// Turn on / off the radio
void PhyUmtsNodeb::radioSwitch(int i)
{
	radio_active_ = i;
	EnergyModel *em = netif_->node()->energy_model();
	if (i == ON) {   // turns the radio on
		if (em && em->sleep()){
			em->set_node_sleep(0);
		}
	}

	if (i == OFF) {  // turns the radio off
		if (em && !em->sleep()) {
			em->set_node_sleep(1);
		};
	}
}

// calculates the transmission time
double PhyUmtsNodeb::TX_Time(Packet *p) {

	double len=(HDR_CMN(p))->size();
	double t = (double)(len*8)/tx_rate;
    return t;
}

// transmits the packet to the terminals
void PhyUmtsNodeb::tx_to_ue(Packet *p)
{
	double stime;
	double temp;

	hdr_cmn* ch = HDR_CMN(p);
	hdr_phy* ph  = HDR_PHY_UMTS(p);

	ph->rx_power() = 0;
	stime = TX_Time(p);	// calculate tx time
//	if (verbose_==1)
//		printf("Nodeb %d at %f PHY: txing pkt, sendtime=%f, size=%d\n", ip_nodeb_, NOW, stime, ch->size());
	pTxPkt_.start(p->copy(), stime);	// start tx timer
	downtarget_->recv(p, this);
}

// store bytes_slot for the UE
void PhyUmtsNodeb::slot_bytes(int pdest, double bytes)
{
	int j;
	j = look_for(pdest);
	ue_info_[j].bytes_slot = bytes;
	return;
}

// Remove UE from ue_info_[]
void PhyUmtsNodeb::remove_ue(int addr)
{
	int j = look_for(addr);
	// Remove this ue from our structures
	ue_info_[j].paging_group = -1;
	ue_info_[j].bytes_slot = 0.0;
	ue_info_[j].in_seq_ = 0;
	ue_info_[j].count = 0;
	ue_info_[j].ul_ErrorRate = 1000000000;
	ue_info_[j].lastRate = 0;
	ue_info_[j].Prx = 0;
	ue_info_[j].ul_freq = -1;
	ue_info_[j].nSamples = 0;
	ue_info_[j].IntPower = 0;

	ue_id[j] = -1;
	return;
}

/********************************* HANDLERS **********************************/

void PhyUmtsNodeb::PagingHandler(Event *e)
{
	// We have not received response to the paging. Send failure message to RRC
	if (verbose_==1)
		printf("Nodeb %d at %f PHY: No response to the paging procedure....\n", ip_nodeb_, NOW);
	hdr_cmn *ch = HDR_CMN(pch_recv_);
	ch->channel_t() = TO_RRC;
	ch->ptype() = PT_RRC_FAILURE;
	ch->direction() = hdr_cmn::UP;
	wait_ = 0;
	send_to_rrc(pch_recv_);
}

// Called after transmission a packet ie when txTimer expires.
// Frees the pkt. Turns off the radio.
void PhyUmtsNodeb::sendHandler(Event *e)
{
    	Packet::free((Packet *)e);
	radioSwitch(OFF);
	return;
}

// Called when BlerTimer expires.
// Timer for calculating the Interference
void PhyUmtsNodeb::blerHandler(Event *e)
{
	double Eb_No, bler, I = 0;
	int i, j = 0;
	// Restart timer for next time.
	pBler_.start((Packet *)e, 5*UMTS_FrameTime);
	for (i=0; i<MAX_NUM_UE; i++) {
		if (ue_info_[i].ul_freq < 0) {
			// there is not interference for i freq
			continue;
		}
		if ((ue_info_[i].lastRate > 0) && (noIoth[ue_info_[i].ul_freq] > 0)) {
			// if there has been any interference
			if ((noIoth[ue_info_[i].ul_freq] - ue_info_[i].nSamples) > 0) {
				I = (Ioth[ue_info_[i].ul_freq] - ue_info_[i].IntPower)/ (noIoth[ue_info_[i].ul_freq] - ue_info_[i].nSamples);
				Eb_No = (CHIP_RATE * ue_info_[i].Prx) / (ue_info_[i].lastRate * I);
			} else {
				// no interference at all
				Eb_No = 1e38;
			}
		} else {
			// no interference at all
			Eb_No = 1e38;
		}
		bler = BTable->getbler(Eb_No);		// consult table (Eb_No)
		ue_info_[i].ul_ErrorRate = (int)(1/bler) + 1;
		if (verbose_)
			printf("Nodeb %d at %f PHY: blerHandler,,, {lastRate Ioth noIoth I Prx EbNo ue freq bler ul_ErrorRate}\n[%f %e %d %e %e %e %d %d %e %lu]\n",
				ip_nodeb_, NOW, ue_info_[i].lastRate, Ioth[ue_info_[i].ul_freq],noIoth[ue_info_[i].ul_freq], I, ue_info_[i].Prx, Eb_No, ue_id[i],ue_info_[i].ul_freq, bler, ue_info_[i].ul_ErrorRate);
		error[j] = ue_info_[i].ul_ErrorRate;
		addr[j] = ue_id[i];
		ue_info_[i].nSamples = 0;
		ue_info_[i].IntPower = 0;
		j++;
	}
	for (i=0; i<MAX_NUM_FREQ; i++){
		Ioth[i] = 0;
  		noIoth[i] = 0;
	}
	// put bounds to the arrays error[] and addr[]
	error[j] = 0;
	addr[j] = -1;
	mac_->ul_interference(addr, error);	// send the results to MAC
	return;
}

// Called after the reception of a packet when RxPktNodebTimer expires.
// Turns the radio off and call recv_from_phy
void PhyUmtsNodeb::recvHandler(Event *e)
{
	u_int32_t dst, src;
	int size;
	Packet * p = (Packet * )e;

	radioSwitch(OFF);	// turn the radio OFF
	recv_from_phy(p, this);	// send the packet to analize in method recv_from_phy()
	return;
}

// Called when DownSlotUmtsTimer expiers
void PhyUmtsNodeb::downslotHandler(Event *e)
{
  	int i,len;

	// Restart timer for next slot.
	pDownSlot_.start((Packet *)e, slot_time_);

	mk_dpxch();		// DPDCH Multiplexing and DPCCH creation

	mk_pccpch();	// PCCPCH tx

	while (pkttoTx_->length() > 0) {
		//start transmission
		radioSwitch(ON);	// turn the radio ON
		tx_to_ue(pkttoTx_->deque());	// transmit to UE
	}

	if (pchtoTx_->length() > 0) {
		radioSwitch(ON);	// turn the radio ON
		tx_to_ue(pchtoTx_->deque());	// transmit to UE
	}

	// program next slot
	down_slot_ ++;
	if (down_slot_ == SLOTS_PER_FRAME) {
		down_slot_ = 0; //ie wrap around.
	}

	// load broadcast information for the next slot.
	if (down_slot_ == 0)
		mk_sch();		// SCH tx

	// empty dl_used_scrambling codes array.
/*	for (i=0; i< MAX_NUM_SCRAM -1; i++) {
		w_control.dl_scramb_[i] = 0;
	}*/
	return;
}

// Called when AichSlotUmtsTimer expires.
void PhyUmtsNodeb::aichslotHandler(Event *e)
{
  	int i,len;

	// Restart timer for next slot.
	pAichSlot_.start((Packet *)e, aich_slot_time_);

	// transmit 1 packet from queue
	if (aichtoTx_->length() > 0) {

		//start transmission
		radioSwitch(ON);
//		if (verbose_==1)
//			    printf("Nodeb %d at %f PHY: in upslothandler. AICH TX slot=%d \n",
//				ip_nodeb_, NOW, down_slot_ );
		tx_to_ue(aichtoTx_->deque());	// transmit to UE
	}

	return;
}
