/* -*-	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 "cmu-trace.h"

/////////////////////// bler table /////////////////////////////////

double BlerTable::table[2][8] = {
		{6.31, 7.94, 10, 15.85, 25.12, 100, 2000, 10000000000},
		{0.2, 0.15, 0.1, 0.05, 0.02, 0.001, 0.0001, 0.00001}
	};

BlerTable::BlerTable() {
}

double BlerTable::getbler(double ebno) {
	int i;
	double bler;
	bler = 1e-38;
	for (i=7; i>-1; i--) {
		if (table[0][i] > ebno) {
			bler = table[1][i];
		} else {
			return bler;
		}
	}
	return bler;
}

/////////////////////////////////////////////////////////////////////

int hdr_phy::offset_;

// TCL Class for Header
static class PhyUmtsHeaderClass : public PacketHeaderClass {
public:
	PhyUmtsHeaderClass() : PacketHeaderClass("PacketHeader/PhyUmts",
							sizeof(hdr_phy)){
		bind_offset(&hdr_phy::offset_);
	}
} class_hdr_phy;

// TCL Class for PhyUmts
static class PhyUmtsClass : public TclClass {
public:
	PhyUmtsClass() : TclClass("Phy/Umts") {}
	TclObject* create(int, const char*const*) {
		return (new PhyUmts());
	}
} class_phy_umts;

// static variables
double PhyUmts::slot_time_ = 0;
double PhyUmts::rach_slot_time_ = 0;
int PhyUmts::verbose_ = {0};
static int PhyIndex_ = 0;

//constructor
PhyUmts::PhyUmts() : LinkDelay(), pUpSlot_(this), pRachUpSlot_(this), pTxPkt_(this), pRxPkt_(this),
    pPreamble_(this), pCell_(this), pBler_(this), downtarget_(0), uptarget_(0), ll_(0), netif_(0), mac_(0)
{
	int i,j;

	bind("verbose_", &verbose_);

	ue_address_ = PhyIndex_++;
	nodeb_address_ = -1;
	ue_state_ = 0;

	pkttoTx_= new PacketQueue();
	rachtoTx_= new PacketQueue();

	rach_recv_ = NULL;
	prach_temp_ = new PacketQueue();
	data_temp_ = new PacketQueue();

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

	error_rate_ = 0;
	rx_power_ = 0;
	tx_power_ = 0;
	threshold_ = 4.652e-10; // the value of Rxthresh_ in WirelessPhy *100

	//max_num_ms shud be less that MAX_NUM_NODES
	if (PhyIndex_ > MAX_NUM_NODES) {
		if (verbose_==1)
		   printf("UE %d at %f PHY: Too many nodes taking part. shud be less than %d \n",ip_ue_,NOW, MAX_NUM_NODES);
		exit (-1);
	}

	radio_active_ = 0;
	bytes_slot = 0.0;
	num_preambles_ = 0;
	wait_ = 0;
	listen_sccpch_ = 0;
	listen_sch_ = 0;
	selected_sc_ = -1;
	selected_p_ = -1;
	handover_ = 0;
	in_seq_ = 0;
	hin_seq_ = 0;
	hnodeb_ = 0;
	count = 0;

	ip_nodeb_ = -1;
	ip_ue_ = -1;
	tti_ = 0.0;
	power_sch_ = 0;

	dl_ErrorRate = 1000000000;
  	lastRate = 0;
  	Prx = 0;
  	dlfreq = -1;
  	ulfreq = -1;
  	Ioth = 0;
  	noIoth = 0;
	BTable = new BlerTable();

	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_ = -1;
	w_control.signature_ = -1;

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

	paging_group_ = -1;
	up_slot_=0;

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

	// initialization of timers
	pUpSlot_.start((Packet *) (& intr_), 0);
	pRachUpSlot_.start((Packet *) (& intr1_), 0);
	pBler_.start((Packet *) (& intrb), 0);
}

int PhyUmts::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);
}

// Return the number of DPDCHs arrived
int PhyUmts::n_rx_dpdch()
{
	int i, n = 0;
	for (i=0 ; i < MAX_NUM_DPDCH ; i++) {
		if (rx_dpdch[i] != NULL)
			n++;
	}
	return (n);
}

// Packets received from NodeB
void PhyUmts::recv_from_phy(Packet* p, Handler *h)
{
	int i, j, len;
	struct hdr_cmn *ch = HDR_CMN(p);
	struct hdr_phy *ph = HDR_PHY_UMTS(p);

	// if error, drop packet
	if ((ch->error() == 1) && (ch->channel_t() != DPCCH)) {
		Packet::free(p);
		p = NULL;
		return;
	}

	switch (ch->channel_t()) {
		case SCH:
			if (listen_sch_ && (ph->p_scrambling_c() != w_control.p_scrambling_)) {
				// Trying to find another NodeB with receiving power higher than ours
				cell_selection(p);
			} else{
				Packet::free(p);
				p = NULL;
			}
			break;
		case AICH:
			if ((ph->scrambling_c() == w_control.scram_tx_) && (ph->signature() == w_control.signature_) && (ph->da() == ue_address_)) {
				// Response of a Preamble, now is possible to tx RACH
				if (verbose_==1)
					printf("UE %d at %f PHY: AICH correctly arrived with signature %d\n",ip_ue_,NOW, ph->signature());
				pPreamble_.stop();	// stop the preamble timer
				num_preambles_ = 0;
				mk_msg_rach();	// send message part RACH
			} else {
				Packet::free(p);
			}
			break;
		case PICH:
			if ((ph->scrambling_c() == w_control.p_scrambling_) && (ph->pi() >= 0)) {
				// Begin of Paging procedure for some mobile of my paging group
				if (ph->pi() == paging_group_){
					if (verbose_==1)
						printf("UE %d at %f PHY: PICH with our paging group, listening to PCH\n",ip_ue_, NOW);
					listen_sccpch_ = 1;	// for initiating procedure
				}
			}
			Packet::free(p);
			break;
		case PCCPCH:
			if (ph->scrambling_c() == w_control.p_scrambling_) {
				// measurements of power
				if ((ph->rx_power() <= threshold_) && (ph->rx_power() < power_sch_) && (!handover_)) {
					struct hdr_ll *lh = HDR_LL(p);
					if (verbose_==1)
						printf("UE %d at %f PHY: initiating HANDOVER PROCEDURE power arrived %e and power_sch: %e\n"
							,ip_ue_, NOW, ph->rx_power(), power_sch_);
					lh->lltype() = LL_HANDOVER;
					send_to_rrc(p->copy());	// directive to RRC
				}
				power_sch_ = ph->rx_power();	// take measurement from PCCPCH
				for (i=0; i<MAX_NUM_SCRAM; i++){	// update free resources
					for (j=0; j<MAX_NUM_SIG + 1; j++){
						w_control.free_res_[i][j] = ph->free_resources_[i][j];
					}
				}
//				for (i=0; i<MAX_NUM_SCRAM - 1; i++){
//					w_control.dl_scramb_[i] = ph->used_dl_scramb_[i];
//				}
				for (i=0; i<MAX_NUM_RACH; i++){	// update free RACH
					w_control.free_rach_[i] = ph->rach_[i];
				}
				Packet::free(p);
			} else {
				Packet::free(p);
				p = NULL;
				return;
			}
			break;
		case SCCPCH:	// carrying FACH and PCH transport channels
			if (((ph->scrambling_c() == w_control.p_scrambling_) || (ph->scrambling_c() == selected_sc_)) && (ph->da() == ue_address_)){
				struct hdr_ll *lh = HDR_LL(p);

				//Transport Channel: PCH, for Paging procedure
				if (lh->lltype() == LL_PAGING) {
					if (listen_sccpch_) {
						if (verbose_==1)
							printf("UE %d at %f PHY: PCH received\n",ip_ue_, NOW);
						send_to_rrc(p);	// directive to RRC
						listen_sccpch_ = 0;
						break;
					} else {
						Packet::free(p);
					}
    				}

				//Transport Channel: FACH, for Setup, Handover and Release procedures and Acknowledge
				if (lh->lltype() == LL_SETUP_REPLY){
					if (ph->bofsec_ == 1) {	// begin of sequence
						struct hdr_ip *ih = HDR_IP(p);
						paging_group_ = ph->paging_group_;	// take paging group
						if (handover_ = 1)
							ulfreq = ph->freq();	// take new uplink freq for transmitting
						handover_ = 0;
						w_control.p_scrambling_ = selected_sc_;
						hnodeb_ = nodeb_address_;
						nodeb_address_ = selected_p_;
						ip_nodeb_ = ih->saddr();
						hin_seq_ = in_seq_;
						in_seq_ = 0;
						selected_sc_ = -1;
						selected_p_ = -1;
						ch->channel_t() = FACH;	// ch mapping
						uptarget_->recv(p, this);
					} else {
						Packet::free(p);
						p = NULL;
					}
				}
				if (lh->lltype() == LL_RES_REPLY){
					if (ph->bofsec_ == 1) {
						if (verbose_==1)
							printf("UE %d at %f PHY: FACH with LL_RES_REPLY recived, ul_freq=%d\n",ip_ue_, NOW, ph->freq());
						ulfreq = ph->freq();	// take new uplink freq for transmitting
						ch->channel_t() = FACH; 	// ch mapping
						uptarget_->recv(p, this);
					} else {
						Packet::free(p);
						p = NULL;
					}
				}
				if (ch->ptype() == PT_ACK){	// RLC-Acknowledge mode
					//if (verbose_==1)
					//	printf("UE %d at %f PHY: ACK received in PHY\n",ip_ue_, NOW);
					ch->channel_t() = FACH;	// ch mapping
					uptarget_->recv(p, this);
				}
				if ((lh->lltype() == LL_RELEASE_REPLY) || (lh->lltype() == LL_RELEASE_REQ)){
					ch->channel_t() = FACH;	// ch mapping
					uptarget_->recv(p, this);
				}
			} else {
				Packet::free(p);
				p = NULL;
			}
			break;
		case DPCCH:			// Dedicated control channel
			if (ph->da_ == ue_address_) {		// we are the destination
				if (verbose_==1)
					printf("UE %d at %f PHY: DPCCH arrived with %d DPDCHs and rx power %e\n",
						ip_ue_, NOW, ph->dpcch_control().n_dpdch_, ph->rx_power());
				Prx = ph->rx_power();		// take power measurements
				lastRate = ph->dpcch_control().dpdch_rate_;	// and rate
				d_control.n_dpdch_ = ph->dpcch_control().n_dpdch_;
				len = n_rx_dpdch();
				if (len != d_control.n_dpdch_) {	// check if all DPDCH have arrived
					for (j=0; j<len; j++) {
						Packet::free(rx_dpdch[j]);
						rx_dpdch[j] = NULL;
					}
					Packet::free(p);
					p = NULL;
					break;
				}
				decode();		// all DPDCHs received, starting decoding
				Packet::free(p);
				p = NULL;
			} else {		// For interference modelling
				if ((ph->sa() != nodeb_address_) && (ph->freq() == dlfreq)) {
					Ioth += ph->rx_power();	// take power measurements
					noIoth++;
				}
				Packet::free(p);
				p = NULL;
			}
			break;
		case DPDCH:			// Dedicated data channel
			if (ph->da_ == ue_address_) {	// we are the destination
				len = n_rx_dpdch();
				rx_dpdch[len] = p;	// store DPDCH packet in buffer
			} else {
				Packet::free(p);
				p = NULL;
			}
			break;
		default:
			break;
	}

	return;
}

void PhyUmts::recv(Packet* p, Handler *h)
{
	hdr_cmn *ch = HDR_CMN(p);
	hdr_phy *ph = HDR_PHY_UMTS(p);
	hdr_ip *ih = HDR_IP(p);
	hdr_ll *lh = HDR_LL(p);

	double stime;
	int i,len;

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

		if (ue_state_ == 0){	// UE is switched off, drop packet
			Packet::free(p);
			p = NULL;
		} else {
			if (ch->channel_t()==PCCPCH) {
				recv_from_phy(p, this);
				return;
			}

			radioSwitch(ON);
			stime = TX_Time(p);	// simulation of reception time
			pRxPkt_.start(p, stime);
		}
		return;
	}

	if (ch->direction() == hdr_cmn::DOWN) {
		switch (ch->channel_t()) {
			case RACH:
				if ((ch->ptype() == PT_ACK) || (lh->lltype() == LL_RELEASE_REQ)) {
					// ACK from RLC Layer and Release request from RRC layer
					ph->sa() = ue_address_;
					mk_msg_rach(p);	// send RACH message without preamble
					return;
				}
				if (wait_ == 1) {	// already in other RACH procedure.
					prach_temp_->enque(p);
				} else {
					wait_ = 1;
					rach_recv_ = p->copy();
					if (lh->lltype() == LL_SETUP){	// switch on procedure or handover
						if (verbose_==1)
							printf("UE %d at %f PHY: Cell search procedure starts. Listen to SCH\n", ip_ue_, NOW);
						listen_sch_ = 1;
						pCell_.start(20*slot_time_); // start timer for cell search procedure
					} else {
						tx_preamble();	// send preamble for RACH procedure
					}
					Packet::free(p);
					p = NULL;
				}
				break;
			case DCH:
				// if (verbose_==1)
					//printf("UE %d at %f PHY: DCH with flow %d arrived at PHY with uid:%d\n", ip_ue_, NOW, ih->flowid(), ch->uid());
				ul_mux(p);	// to uplink multiplexing chain
				break;
			default:
				if (verbose_==1)
					printf("UE %d at %f PHY: Pkt of flow %d arrived at PHY with uid:%d and direction%d:\n",ip_ue_, NOW, ih->flowid(), ch->uid(), ch->direction());
				pkttoTx_->enque(p);
				break;
		}
	}
return;
}

// looking for an available Scrambling Code
int PhyUmts::scrambling_allot()
{
	int index_c_, i = 0;
	// begin in an aleatory place
	index_c_ = Random::integer(MAX_NUM_SCRAM-1);
	while (i < MAX_NUM_SCRAM) {
		if (w_control.free_res_[0][index_c_] == 0)
			return (index_c_); // index_c is an available scrambling code
		i++;
		index_c_++;
		if (index_c_ == MAX_NUM_SCRAM)
			index_c_ = 0;
	}
	return (-1);  // no free resources
}

// For Setup and Handover Procedure
void PhyUmts::cell_selection(Packet* p)
{
	hdr_phy *ph = HDR_PHY_UMTS(p);

	if (verbose_==1)
		printf("UE %d at %f PHY: analizing SCH power: %e from NodeB with phyaddr %d.....\n\n", ip_ue_,NOW, ph->rx_power(), ph->p_scrambling_c());
	if (ph->rx_power() > power_sch_){
		// higher power than the one that we have stored, store the new one
		selected_sc_ = ph->p_scrambling_c();  // new node B primary scrambling code
		selected_p_ = ph->sa();	// new node B physical address
		power_sch_ = ph->rx_power();
		if (verbose_==1)
			printf("UE %d at %f PHY: Nodeb with higher power and scram: %d\n", ip_ue_,NOW, ph->p_scrambling_c());
	}
	Packet::free(p);
	p = NULL;
	return;
}

// for transmitting the RACH preamble
void PhyUmts::tx_preamble()
{
	int i, j, index_c_, index_s_;

	// looking for an available RACH
	i = 0;
	j = Random::integer(MAX_NUM_RACH-1);
	while (i < MAX_NUM_RACH) {
		if (w_control.free_rach_[j] == 0)  // j is an available RACH
			break;
		i++;
		j++;
		if (j == MAX_NUM_RACH)
			j = 0;
	}

	// check if there is an available RACH
	if (i == MAX_NUM_RACH) {
		// there is not any available RACH
		// arbitly limited the backoff timer from 4 to 12 slots.-
		pPreamble_.stop();
		pPreamble_.start();	//start Preamble timer
		return;
	} else {
		// j is the available RACH to use
		// looking for an available Scrambling Code
		index_c_ = scrambling_allot();
		// check if there is an available Scrambling Code
		if (index_c_ == -1) {
			// there is not any available Scrambling Code
			// arbitly limited the backoff timer from 4 to 12 slots.-
			pPreamble_.stop();
			pPreamble_.start();	//start Preamble timer
			return;
		} else {
			// index_c_ is the available Scrambling Code
			// looking for an available Signature
			i = 0;
			index_s_ = Random::integer(MAX_NUM_SIG-1)+1;
			while (i < MAX_NUM_SIG) {
				if (w_control.free_res_[index_s_][index_c_] == 0)  // available Signature
					break;
				i++;
				index_s_++;
				if (index_s_ == MAX_NUM_SIG + 1)
					index_s_ = 1;
			}
			// check if there is an available Signature
			if (i == MAX_NUM_SIG) {
				// there is not any available Signature
				// arbitly limited the backoff timer from 4 to 12 slots.-
				pPreamble_.stop();
				pPreamble_.start();	//start Preamble timer
				return;
			} else {
	 		// Preamble with parameters: j, index_c_, index_s_
				if (verbose_==1)
					printf("UE %d at %f PHY: PREAMBLE with rach: %d, sc: %d, sign: %d\n", ip_ue_,NOW,j, index_c_, index_s_);
				// storing preamble characteristics
				w_control.signature_ = index_s_;
				w_control.rach_tx_ = j;
				w_control.scram_tx_ = index_c_;

				// building the preamble message
				Packet* sp = Packet::alloc();
				struct hdr_cmn *chsp = HDR_CMN(sp);
				hdr_phy *phsp = HDR_PHY_UMTS(sp);
				hdr_ip *ihsp = HDR_IP(sp);
				hdr_ll *lhrach_recv = HDR_LL(rach_recv_);
				hdr_ip *ihrach_recv = HDR_IP(rach_recv_);

				if (lhrach_recv->lltype() == LL_SETUP){
					// for setup and handover procedures
					phsp->first_access_ = 1;
					phsp->da_ = selected_p_;		// destination address
				} else {
					// for resource allocation
					phsp->first_access_ = 0;
					phsp->da_ = nodeb_address_;		// destination address
				}
				phsp->sa_ = ue_address_;	// source address
				phsp->scrambling_c_ = index_c_;	// scrambling code
				phsp->signature_ = w_control.signature_;	// signature
				phsp->used_rach_ = w_control.rach_tx_;		// RACH
				phsp->sf_ = 16;  			// spreading factor
				phsp->k_ = w_control.signature_;	// k for the channelisation code

				chsp->direction_ = hdr_cmn::DOWN;
				chsp->ptype() = PT_PREAMBLE;
				// calculating message size
				chsp->size_=(int)(UMTS_TBLength / (phsp->sf_ * 8));  // bits = 2560/SF
				if ((UMTS_TBLength % (phsp->sf_ * 8)) != 0){
					chsp->size_++;
				}
				chsp->error()= 0;
				chsp->channel_t()=PRACH; // channel allocation
				chsp->next_hop() = ip_nodeb_;
				chsp->prev_hop_ = ip_ue_;

				ihsp->saddr() = ihrach_recv->saddr();
				ihsp->daddr() = ihrach_recv->daddr();

				rachtoTx_->enque(sp);  // store preamble in the RACH transmission queue

				// arbitly limited the backoff timer from 4 to 12 slots.-
				pPreamble_.stop();
				pPreamble_.start(); //start Preamble timer
				num_preambles_++;
			}
		}
	}
	return;
}


// for channel coding
void PhyUmts::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;
}

// for channel decoding
void PhyUmts::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;
}

// uplink multiplexing chain
void PhyUmts::ul_mux(Packet* p)
{
	int i;
	hdr_cmn *chp = HDR_CMN(p);
	hdr_phy *php = HDR_PHY_UMTS(p);

	c_coding(p);			// for Channel Coding
	php->size() = chp->size_;   // store size
	php->sa_ = ue_address_;	// source address
	php->da_ = nodeb_address_;	// destination address

	data_temp_->enque(p);  // store packet for sending it over the air interface
	return;
}


// decoding packets with direction UP
void PhyUmts::decode()
{
	int i, j;
	Packet* p;
	hdr_cmn *chp;
	hdr_ip *ihp;
	hdr_phy *php;
	hdr_phy *phrecv;

	// demux DPDCH and extract packets
	for (i=0; i<d_control.n_dpdch_; i++) {
		phrecv = HDR_PHY_UMTS(rx_dpdch[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);
			hdr_rlc_umts *rhp = HDR_RLC_UMTS(p);
			if (php->sa() == nodeb_address_) {
				if (in_seq_ == -1) {
					// Previusly packets arrived not in sequence
					if (php->seqno_ == 0) {
						// first physical fragment of a packet
						if (php->eofsec_ == 1) {
							// last physical fragment of a packet
							chp->channel_t() = DCH; // ch mapping
							dl_demux(p->copy()); // send it to demux chain
							if (verbose_==1)
								printf("UE %d at %f PHY: decode: sending end of sequence of flow %d uid %d and size %d from NodeB %d\n",
									ip_ue_,NOW, ihp->flowid(),chp->uid(),chp->size(),php->sa());
							in_seq_ = 0; // waiting for fragment 0
						} else {
							// is not the last fragment, drop it
							if (verbose_==1)
								printf("UE %d at %f PHY: a decode: pkt in seq: %d ue %d flow %d uid %d and size %d from NodeB %d\n",
									ip_ue_,NOW, php->seqno_, php->sa(), ihp->flowid(),chp->uid(),chp->size(),php->sa());
							in_seq_ = 1; // waiting fragment number 1
							Packet::free(p);
						}
					} else {
						// is not the first fragment, drop the packet
						if (verbose_==1)
							printf("UE %d at %f PHY: decode: not seq: Drop pkt from NodeB %d\n",ip_ue_,NOW, php->sa());
						Packet::free(p);
					}
				} else if (php->seqno_ == 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
						chp->channel_t() = DCH;   // ch mapping
						dl_demux(p->copy());   // send it to demux chain
						in_seq_ = 0;  // waiting fragment number 0
						if (verbose_==1)
							printf("UE %d at %f PHY: decode: sending end of sequence of flow %d uid %d and size %d from NodeB %d dpdch con size %f\n",
								ip_ue_,NOW, ihp->flowid(),chp->uid(),chp->size(),php->sa(),phrecv->size());
					} else {
						// is not the last fragment, drop the packet
						if (verbose_==1)
							printf("UE %d at %f PHY: decode: pkt in seq: %d ue %d flow %d uid %d and size %d from NodeB %d dpdch con size %f\n",
								ip_ue_, NOW, php->seqno_, php->sa(), ihp->flowid(),chp->uid(),chp->size(),php->sa(),phrecv->size());
						in_seq_++;
						Packet::free(p);
					}
				} else {  // Previous packets arrived in sequence
					// Previous packets arrived in sequence
					if (verbose_==1)
						printf("UE %d at %f PHY: decode: pkt not in seq: %d waiting %d ue %d flow %d uid %d and size %d from NodeB %d\n",
							ip_ue_,NOW, php->seqno_, in_seq_, php->sa(), ihp->flowid(),chp->uid(),chp->size(),php->sa());
					in_seq_ = -1; // not in sequence
					Packet::free(p);
				}
			// used during Handover Procedure
			} else if (php->sa() == hnodeb_) {
				if (hin_seq_ == -1) {		// Previusly packets arrived in not sequence
					if (php->seqno_ == 0) {
						// first physical fragment of a packet
						if (php->eofsec_ == 1) {
							// last physical fragment of a packet
							chp->channel_t() = DCH; // ch mapping
							dl_demux(p->copy());  // send it to demux chain
							if (verbose_==1)
								printf("UE %d at %f PHY: decode: pkt UP of flow %d uid %d and size %d from NodeB %d\n",
									ip_ue_,NOW, ihp->flowid(),chp->uid(),chp->size(),php->sa());
							hin_seq_ = 0;  // waiting fragment number 0
						} else {
							// is not the last fragment, drop the packet
							if (verbose_==1)
								printf("UE %d at %f PHY: decode: pkt in seq: %d ue %d flow %d uid %d and size %d from NodeB %d\n",
								ip_ue_, NOW, php->seqno_, php->sa(), ihp->flowid(),chp->uid(),chp->size(),php->sa());
							hin_seq_ = 1; // waiting for fragment number 1
							Packet::free(p);
						}
					} else {
						// is not the first fragment, drop the packet
						if (verbose_==1)
							printf("UE %d at %f PHY: decode: not seq: Drop pkt from NodeB %d\n",ip_ue_,NOW,php->sa());
						Packet::free(p);
					}
				} else if (php->seqno_ == hin_seq_) {	// Previusly packets arrived in sequence
					// is the fragment number that we expected
					if (php->eofsec_ == 1) {
						// last physical fragment of a packet
						chp->channel_t() = DCH;  // ch mapping
						dl_demux(p->copy());  // send it to demux chain
						hin_seq_ = 0;  // waiting fragment number 0
						if (verbose_==1)
							printf("UE %d at %f PHY: decode: pkt UP of flow %d uid %d and size %d from NodeB %d\n",
								ip_ue_,NOW, ihp->flowid(),chp->uid(),chp->size(),php->sa());
					} else {
						// is not the last fragment, drop the packet
						if (verbose_==1)
							printf("UE %d at %f PHY: decode: pkt in seq: %d ue %d flow %d uid %d and size %d from NodeB %d\n",
								ip_ue_,NOW, php->seqno_, php->sa(), ihp->flowid(),chp->uid(),chp->size(),php->sa());
						hin_seq_++;
						Packet::free(p);
					}
				} else {  // Previous packets arrived in sequence
					// is not the fragment number that we expected
					if (verbose_==1)
						printf("UE %d at %f PHY: decode: pkt not in seq: %d waiting %d ue %d flow %d uid %d and size %d from NodeB %d\n",
							ip_ue_,NOW, php->seqno_, hin_seq_, php->sa(), ihp->flowid(),chp->uid(),chp->size(),php->sa());
					hin_seq_ = -1;
					Packet::free(p);
				}
			}
		}
		Packet::free(rx_dpdch[i]); // remove resources
		rx_dpdch[i] = NULL;
	}
	return;
}

// downlink demultiplexing channel
void PhyUmts::dl_demux(Packet* p)
{
	int i;
	hdr_cmn *chp = HDR_CMN(p);
	hdr_phy *php = HDR_PHY_UMTS(p);

	chp->direction_ = hdr_cmn::UP;
	//if (verbose_==1)
	//printf("UE %d at %f PHY: phy --->> mac pkt con uid: %d y size: %d, direccion: %d\n", ip_ue_, NOW,chp->uid(), chp->size(), chp->direction());
	c_decoding(p);			// channel decoding

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

// Building dedicated channels depending on the given bytes per slot
void PhyUmts::mk_dpxch(void)
{
	// Rate matching
	int i, j, sf_, k_, n_dpdch;
	double index, temp, sum, mod, tbytes, total_size;

	hdr_cmn * chp;
	hdr_phy * php;
	hdr_rlc_umts * rh;
	tbytes = 0.0;
	sum = 0.0;
	total_size = bytes_slot;  // maximum physical bytes that is possible sending in one slot

	if (data_temp_->length() > 0) {  // no flows to tx
		Packet* p;
		// looking for an available Scrambling Code
		if (up_slot_ == 0) {
			d_sc_ = scrambling_allot();
		}
		// d_sc_ is the available Scrambling Code
		if (total_size > MAX_NUM_BYTES_PER_SLOT){
			// is necesary 2 or more DPDCH, with SF=4
			// building DPDCH packets
			n_dpdch = (int)(total_size / MAX_NUM_BYTES_PER_SLOT);
			mod = (int)(total_size) % (MAX_NUM_BYTES_PER_SLOT);
			if (mod > 0)
				n_dpdch++;
			if (n_dpdch > MAX_NUM_DPDCH)
				n_dpdch = MAX_NUM_DPDCH;
			// n_dpdch is the number of necessary DPDCH for that application

			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->size() = 0;
				phsp->n_dpdch_ = i+1;
				sum = 0.0;
				// extracting packets from the buffer to transmit
				while (data_temp_->length() > 0){
					p = data_temp_->deque();
					php = HDR_PHY_UMTS(p);
					rh = HDR_RLC_UMTS(p);
					sum += php->size();
					// check if there is enough space in that DPDCH
					if (sum <= MAX_NUM_BYTES_PER_SLOT) {
						// there is enough space for the whole packet
						php->seqno_ = count;
						php->eofsec_ = 1;
						phsp->dpdch_control_->enque(p->copy());
						phsp->size() += php->size();
						count = 0;
						Packet::free(p);
						if (sum == MAX_NUM_BYTES_PER_SLOT)
							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 - sum;
						php->seqno_ = 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 - sum;
						php->size() = temp - (MAX_NUM_BYTES_PER_SLOT - sum);
						count++;
						// storing the rest of the packet in the buffer
						data_temp_->enqueHead(p);
						break;
					}
				}
				sf_ = 4; // spreading factor
				// calculate 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
				send_msg(sp->copy(), d_sc_, 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 = (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->size() = 0;
			phsp->n_dpdch_ = 1;
			n_dpdch = 1;
			// extract packets from the buffer to transmit
			while ((tbytes < total_size) && (data_temp_->length() > 0)){
				p = data_temp_->deque();
				php = HDR_PHY_UMTS(p);
				rh = HDR_RLC_UMTS(p);
				tbytes += php->size();
				if (tbytes <= total_size) {
					// checks if is possible to send the whole packet
					php->seqno_ = count;
					php->eofsec_ = 1;
					phsp->dpdch_control_->enque(p->copy());
					phsp->size() += php->size();
					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_ = 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;
					count++;
					// storing the rest of the packet in the buffer
					data_temp_->enqueHead(p);
					break;
				}
			}
			k_ = sf_/4; // calculates k_
			chsp->channel_t()=DPDCH;  // ch mapping
			send_msg(sp->copy(), d_sc_, 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_ = (2560 / (UMTS_SlotTime * sf_)) * n_dpdch;
		chsp->channel_t()=DPCCH;  // ch mapping
		phsp->size() = 1.25;
		phsp->dpcch_control().n_dpdch_ = n_dpdch;  // number of DPDCH multiplexed with the DPCCH

		if (verbose_==1)
			printf("UE %d at %f PHY: DPCCH/%dDPDCH with SF=%d sent to NodeB %d in upslot %d\n", ip_ue_, NOW,phsp->dpcch_control_.n_dpdch_, sf_, ip_nodeb_, up_slot_);
		send_msg(sp->copy(), d_sc_, 256, 0); // send DPCCH message
		Packet::free(sp);
		sp = NULL;
		return;
	}

	return;
}

// send DPDCHs and DPCCH over the air interface to Node B
void PhyUmts::send_msg(Packet* p, int scram_c, int sf, int k)
{
	int i;
	hdr_cmn *chp = HDR_CMN(p);
	hdr_phy *php = HDR_PHY_UMTS(p);

	php->sa_ = ue_address_;	// source address
	php->da_ = nodeb_address_;	// destination address
	php->scrambling_c_ = scram_c;	// scrambling code
	php->sf_ = sf;  			// spreading factor
	php->k_ = k;			// k for the channelisation code
	php->freq() = ulfreq;		// uplink frequence

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

//	if (verbose_==1)
//		printf("UE %d at %f PHY: send_msg: long: %d\n", ip_ue_, NOW,chp->size());
	pkttoTx_->enque(p);		// prepare pkt to tx onto air interface
	return;
}

//  rach without preamble
void PhyUmts::mk_msg_rach(Packet* p)
{
	int i, j, index_c_;
	hdr_cmn *chsp = HDR_CMN(p);
	hdr_ll *lhsp = HDR_LL(p);
	hdr_phy *phsp = HDR_PHY_UMTS(p);

	// looking for an available Scrambling Code
	index_c_ = scrambling_allot();
	if (index_c_ == -1) {
		// there is not any available Scrambling Code
		chsp->ptype() = PT_RRC_FAILURE;
		wait_ = 0;
		num_preambles_ = 0;
		send_to_rrc(p); // directive to RRC
		return;
	} else {
		// index_c_ is the available Scrambling Code
		phsp->scrambling_c_ = index_c_;	// scrambling code
		phsp->used_rach_ = w_control.rach_tx_;	// RACH

		// choose a random spreading factor
		i = Random::integer(3)+5;
		phsp->sf_ = 2;
		for (j=0; j<i-1; j++)
			phsp->sf_ *= 2;

		phsp->k_ = 0;	// k for the channelisation code
//		if (verbose_==1)
//			printf("UE %d at %f PHY: prach built for ACK with sf %d, k %d towards %d\n", ip_ue_,NOW, phsp->sf_, phsp->k_, phsp->da());
		phsp->bofsec_ = 1; // begin of RACH tx
		phsp->eofsec_ = 1; // end of RACH tx

		// 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_++;
		}
		if (chsp->ptype() != PT_ACK){
			// is not an RLC ACK
			chsp->next_hop() = ip_nodeb_;
		}
		chsp->channel_t()=PRACH; // ch mapping
		rachtoTx_->enqueHead(p->copy());  // store PRACH message in the RACH transmission queue
	}
	Packet::free(p);
	p = NULL;
	return;
}

// send RACH with preamble
void PhyUmts::mk_msg_rach()
{
	int i, j, index_c_;
	Packet* csp = Packet::alloc();

	hdr_cmn *chsp = HDR_CMN(rach_recv_);
	hdr_ll *lhsp = HDR_LL(rach_recv_);
	hdr_phy *phsp = HDR_PHY_UMTS(rach_recv_);

	// looking for an available Scrambling Code
	index_c_ = scrambling_allot();
	// check if there is an available Scrambling Code
	if (index_c_ == -1) {
		// there is not any available Scrambling Code
		chsp->ptype() = PT_RRC_FAILURE;
		wait_ = 0;
		num_preambles_ = 0;
		send_to_rrc(rach_recv_); // directive to RRC
		Packet::free(csp);
		return;
	} else {
		// index_c_ is the available Scrambling Code
		phsp->scrambling_c_ = index_c_;	// scrambling code
		phsp->used_rach_ = w_control.rach_tx_;	// RACH
		phsp->sa() = ue_address_;
		if (lhsp->lltype() == LL_SETUP){
			phsp->da() = selected_p_;
			chsp->next_hop() = BROADCAST;
		}else{
			chsp->next_hop() = ip_nodeb_;
			phsp->da() = nodeb_address_;
		}

		// choose a random spreading factor
		i = Random::integer(3)+5;
		phsp->sf_ = 2;
		for (j=0; j<i-1; j++)
			phsp->sf_ *= 2;

		phsp->k_ = phsp->sf_ * w_control.signature_ / 16; // k for the channelisation code
		phsp->eofsec_ = 0;

		// 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->channel_t()=PRACH; // ch mapping

		if (verbose_==1)
			printf("UE %d at %f PHY: prach built with sf %d, k %d towards %d\n", ip_ue_,NOW, phsp->sf_, phsp->k_, phsp->da());
		// control part
		csp = rach_recv_->copy();

		hdr_phy *phcsp = HDR_PHY_UMTS(csp);
		hdr_cmn *chcsp = HDR_CMN(csp);
		phcsp->sf_ = 256;  			// spreading factor
		phcsp->k_ = 16 * w_control.signature_ + 15; // k for the channelisation code

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

		// length of a RACH message part is a frame (8 slots)
		for (i=0; i<8; i++){
			if (i == 0){
				phsp->bofsec_ = 1; // begin of RACH tx
				phcsp->bofsec_ = 1;
			} else {
				phsp->bofsec_ = 0;
				phcsp->bofsec_ = 0;
			}
			if (i == 7){
				phsp->eofsec_ = 1; // end of RACH tx
				phcsp->eofsec_ = 1;
			}
			rachtoTx_->enque(rach_recv_->copy());  // store PRACH data part in the RACH transmission queue
			rachtoTx_->enque(csp->copy());    // store PRACH control part in the RACH transmission queue
		}
	}
	Packet::free(rach_recv_);
	rach_recv_ = NULL;
	Packet::free(csp);
	csp = NULL;
	return;
}

// send directives directly to RRC
void PhyUmts::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 destination broadcasted in S-CCPCH
{
	hdr_cmn *chp = HDR_CMN(p);

	chp->direction_ = hdr_cmn::UP; // put direction UP
	chp->channel_t() = TO_RRC; // ch mapping
	ll_->recv(p, this);
}

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

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

// calculate the transmission time
double PhyUmts::TX_Time(Packet *p) {

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

// transmit packets to Node B
void PhyUmts::tx_to_nodeb(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 the transmission time
//	if (verbose_==1)
//		printf("UE %d at %f PHY: txing pkt, sendtime=%f, size=%d\n", ip_ue_, NOW, stime, ch->size());
	pTxPkt_.start(p->copy(), stime);  // start the transmission timer
	hdr_ll* lh = HDR_LL(p);
	hdr_ip* ih = HDR_IP(p);
	downtarget_->recv(p->copy(), this); // send the packet to Node B
}



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

// Called during Handover and Setup procedures when CellTimer expires.
void PhyUmts::cellHandler()
{
	listen_sch_ = 0;

	if (selected_sc_ < 0) {
		// We don't received higher power than my NodeB's power of any cell.
		//Stops the handover procedure
		if (verbose_==1)
			printf("UE %d at %f PHY: No NodeB with higher power than mine. Stop HANDOVER PROCEDURE...\n", ip_ue_, NOW);
		handover_ = 0;
		wait_ = 0;
		Packet::free(rach_recv_);
		return;
	}
	// we have found a Node B nearest than the other one
	if (verbose_==1)
		printf("UE %d at %f PHY: selecting Nodeb with scram code: %d\n", ip_ue_,NOW, selected_sc_);
	tx_preamble();  // transmit RACH preamble
	return;
}

// Called when PreambleTimer expires.
// We have not received the waited AICH as a response of the PREAMBLE sent
void PhyUmts::PreambleHandler()
{
	if (num_preambles_ < MAX_NUM_PREAMBLE) {
		// we can continue transmitting preambles
		if (verbose_==1)
			printf("UE %d at %f PHY: other preamble -----------------------------------------%d\n", ip_ue_,NOW, num_preambles_);
		tx_preamble();  // transmit RACH preamble
	} else {
		// we have transmit the maximum number of preambles
		hdr_cmn *ch = HDR_CMN(rach_recv_);
		ch->ptype() = PT_RRC_FAILURE;
		wait_ = 0;
		num_preambles_ = 0;
		send_to_rrc(rach_recv_);  // directive to RRC
	}
}


// Called when BlerTimer expires.
// Timer for calculating the Interference
void PhyUmts::blerHandler(Event *e)
{
	double Eb_No, bler, I=0;
	// Restart timer for next time.
	pBler_.start((Packet *)e, 5*UMTS_FrameTime);
	if ((lastRate > 0) && (noIoth > 0)) {
		// if there has been any interference
		I = Ioth / noIoth;
		Eb_No = (CHIP_RATE * Prx) / (lastRate * (I + Pn));
	} else {
		// no interference at all
		Eb_No = 1e38;
	}
	Ioth = 0;
	noIoth = 0;
	bler = BTable->getbler(Eb_No); // consult table (Eb_No)
	dl_ErrorRate = (int)(1/bler) + 1;
	if (verbose_)
		printf("UE %d at %f PHY: blerHandler,,, {lastRate Ioth noIoth I Prx EbNo bler dl_ErrorRate}\n[%f %e %d %e %e %e %e %lu]\n", ip_ue_,NOW, lastRate, Ioth ,noIoth, I, Prx, Eb_No, bler, dl_ErrorRate);
	mac_->dl_interference(dl_ErrorRate); // send the results to MAC
	return;
}


// Called after the transmission of a packet when TxPktUmtsTimer expires.
// Frees the pkt. Turns off the radio.
void PhyUmts::sendHandler(Event *e)
{
	Packet::free((Packet *)e);
	e = NULL;
	radioSwitch(OFF);
	return;
}

// Called after the reception of a packet when RxPktUmtsTimer expires.
// Turns the radio off and call recv_from_phy
void PhyUmts::recvHandler(Event *e)
{
	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 UpSlotUmtsTimer expires.
void PhyUmts::upslotHandler(Event *e)
{
  	int i,len;

	// Restart timer for next slot.
	pUpSlot_.start((Packet *)e, slot_time_);
	mk_dpxch();   // DPDCH Multiplexing and DPCCH creation

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

	// program the next slot
	up_slot_ ++;
	if (up_slot_ == SLOTS_PER_FRAME) {
		up_slot_ = 0;
	}
	return;
}

// Called when RachUpSlotUmtsTimer expires.
void PhyUmts::rachupslotHandler(Event *e)
{
  	int i,len;

	// Restart timer for next slot.
	pRachUpSlot_.start((Packet *)e, rach_slot_time_);

AGAIN:
	// check if there is any packet in the buffer
	if (rachtoTx_->length() > 0) {

		Packet * prov = rachtoTx_->deque();
		struct hdr_phy *phprov = HDR_PHY_UMTS(prov);
		struct hdr_cmn *chprov = HDR_CMN(prov);
		struct hdr_ll *lhprov = HDR_LL(prov);

		//start transmission
		radioSwitch(ON);  // turn the radio ON

		if ((chprov->ptype() == PT_ACK) || (lhprov->lltype() == LL_RELEASE_REQ)){
			// RLC ACK or Release Request
			tx_to_nodeb(prov);  // transmit to Node B
			goto AGAIN;  // send more packets
		}

		tx_to_nodeb(prov);
		// preamble modulated in BPSK but RACH message part in QPSK
		if (chprov->ptype() != PT_PREAMBLE)
			tx_to_nodeb(rachtoTx_->deque());  // send the next packet

		// end of transmision of RACH message
		if (phprov->eofsec_ == 1)
			wait_ = 0;
	}

	// check if there is other rach waiting to be transmited
	if ((wait_ == 0) && (prach_temp_->length() > 0)){
		// begin RACH procedure
		rach_recv_ = prach_temp_->deque();
		wait_ = 1;
		num_preambles_ = 0;
		tx_preamble();   // transmit preamble
		if (verbose_==1)
			printf("UE %d at %f PHY: other rach to tx?????\n", ip_ue_,NOW );
	}
	return;
}

