/*
 * Phy802_15_4Impl.java
 *
 * Created on July 1, 2008, 11:20 AM
 *
 * To change this template, choose Tools | Template Manager
 * and open the template in the editor.
 */

package sidnet.stack.mac.ieee802_15_4;

import bsh.This;
import sidnet.core.misc.TimerInterface;
import jist.runtime.JistAPI; 
import jist.runtime.JistAPI.Continuation;
import jist.swans.Constants;
import jist.swans.mac.MacAddress;
import jist.swans.field.FieldInterface;
import sidnet.jist.swans.MacInterface;
import jist.swans.misc.Message;
import jist.swans.misc.MessageAnno;
import jist.swans.radio.RadioInfo;

/**
 *
 * @author Oliver
 * Java adaptation after NS-2 C++ implementation
 */
/*
 * Copyright (c) 2003-2004 Samsung Advanced Institute of Technology and
 * The City University of New York. 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 must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *	This product includes software developed by the Joint Lab of Samsung 
 *      Advanced Institute of Technology and The City University of New York.
 * 4. Neither the name of Samsung Advanced Institute of Technology nor of 
 *    The City University of New York may be used to endorse or promote 
 *    products derived from this software without specific prior written 
 *    permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE JOINT LAB OF SAMSUNG ADVANCED INSTITUTE
 * OF TECHNOLOGY AND THE CITY UNIVERSITY OF NEW YORK ``AS IS'' AND ANY EXPRESS 
 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN 
 * NO EVENT SHALL SAMSUNG ADVANCED INSTITUTE OR THE CITY UNIVERSITY OF NEW YORK 
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE 
 * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
//PHY PIB attributes (Table 19)
enum PPIBenum
{
    phyCurrentChannel, /* = 0x00; */
    phyChannelsSupported,
    phyTransmitPower,
    phyCCAMode
}













public class Phy802_15_4Impl implements RadioInterface_802_15_4/*implements PhyInterface.Phy802_15_4/* : public WirelessPhy */
{
    //Non members in NS-2
    public final static short phyCCAHType = 1;
    public final static short phyEDHType  = 2;
    public final static short phyTRXHType = 3;
    public final static short phyRecvOverHType = 4;
    public final static short phySendOverHType = 5;
    // ---
    
    private PHY_PIB ppib;
    private PHYenum trx_state;		//tranceiver state: TRX_OFF/TX_ON/RX_ON
    private PHYenum trx_state_defer_set;	//defer setting tranceiver state: TX_ON/RX_ON/TRX_OFF/IDLE (IDLE = no defer pending)
    private PHYenum trx_state_turnaround;	//defer setting tranceiver state in case Tx2Rx or Rx2Tx
    private PHYenum tx_state;		//transmitting state: IDLE/BUSY
    private MacMessage_802_15_4 rxPkt;	//the packet meets the following conditions:
					// -- on the current channel
					// -- for this node (not interference)
					// -- with the strongest receiving power among all packets that are for this node and on the current channel
    private MacMessage_802_15_4 txPkt;			//the packet being transmitted
    private MacMessage_802_15_4 txPktCopy;		//the copy of the packet being transmitted
    private double[] rxTotPower = new double[27];		
    private double rxEDPeakPower;		
    private int[] rxTotNum = new int[27];		
    private int[] rxThisTotNum = new int[27];	
    private Mac802_15_4Impl mac;
    private Phy802_15_4Timer CCAH;
    private Phy802_15_4Timer EDH;
    private Phy802_15_4Timer TRXH;
    private Phy802_15_4Timer recvOverH;
    private Phy802_15_4Timer sendOverH;
    
    /** Not in NS-2: mac address of this interface. */
    protected MacAddress localAddr;
    
    /* START ::: OSI Stack Hookup methods +++++++++++++++++++ */
   
    // entity hookup
    /** MAC upcall entity reference */
    protected MacInterface.Mac802_15_4 macEntity;
    
    /** Self-referencing mac entity reference. */
    protected final RadioInterface_802_15_4 self;
    
    /** radio properties */
    protected RadioInfo radioInfo;
    
    /** Field downcall entity reference. */
    protected FieldInterface fieldEntity;
    
    /**
     * Hook up with the mac entity.
     *
     * @param mac mac entity
     */
    public void setMacEntity(MacInterface mac)
    {
        if(!JistAPI.isEntity(mac)) throw new IllegalArgumentException("expected entity");
        this.macEntity  = (MacInterface.Mac802_15_4) mac;
    }
    
    /**
     * Return proxy entity of this phy layer.
     *
     * @return self-referencing proxy entity.
     */
    public RadioInterface_802_15_4 getProxy()
    {
        return this.self;
    }
    
    /**
     * Hook up with the field entity.
     *
     * @param field field entity
     */
    public void setFieldEntity(FieldInterface field)
    {
        if(!JistAPI.isEntity(field)) throw new IllegalArgumentException("expected entity");
        this.fieldEntity = field;
    }
     /* END ::: OSI Stack Hookup methods +++++++++++++++++++ */

    public Phy802_15_4Impl(int id, RadioInfo sharedInfo)
    {
        this(new PHY_PIB(), id, sharedInfo);
    }
    
    public Phy802_15_4Impl(PHY_PIB pp, int id, RadioInfo sharedInfo)
    {
        radioInfo = sharedInfo;
        
        int i;
	ppib = pp;
	trx_state = PHYenum.p_RX_ON;
	trx_state_defer_set = PHYenum.p_IDLE;
	tx_state = PHYenum.p_IDLE;
	rxPkt = null;
	for (i=0;i<27;i++)
	{
		rxTotPower[i] = 0.0;
		rxTotNum[i] = 0;
		rxThisTotNum[i] = 0;
	}
	mac = null;   
        
        CCAH = new Phy802_15_4Timer(this, phyCCAHType, id);
        EDH  = new Phy802_15_4Timer(this, phyEDHType, id);
        TRXH = new Phy802_15_4Timer(this, phyTRXHType, id);
        recvOverH = new Phy802_15_4Timer(this, phyRecvOverHType, id);
        sendOverH = new Phy802_15_4Timer(this, phySendOverHType, id);
        
        this.localAddr = new MacAddress(id);
        
         // proxy
        self = (RadioInterface_802_15_4)JistAPI.proxy(this, RadioInterface_802_15_4.class);
    }
    
    public void setMac(Mac802_15_4Impl mac)
    {
        this.mac = mac;
    }
    
    // Not in NS-2. Part of the RadioInterface
    public void setSleepMode(boolean sleep)
    {
        //setMode(sleep ? Constants.RADIO_MODE_SLEEP : Constants.RADIO_MODE_IDLE);
        trx_state = sleep ? PHYenum.p_TRX_OFF : PHYenum.p_RX_ON; // ??? p_RX_ON or p_TX_ON
    }
    
    public RadioInfo getRadioInfo()
    {
        return radioInfo;
    }
    
    public void macObj(Mac802_15_4Impl m)
    {
        mac = m;
    }
    
    public boolean channelSupported(byte channel)
    {
        return ((ppib.phyChannelsSupported & (1 << channel)) != 0);
    }
    
    public double getRate_BitsPerSecond(char dataOrSymbol) /* bits/s */
    {
        double rate;
	
	if (ppib.phyCurrentChannel == 0)
	{
		if (dataOrSymbol == 'd')
			rate = Def.BR_868M;
		else
			rate = Def.SR_868M;
	}
	else if (ppib.phyCurrentChannel <= 10)
	{
		if (dataOrSymbol == 'd')
			rate = Def.BR_915M;
		else
			rate = Def.SR_915M;
	}
	else
	{
		if (dataOrSymbol == 'd')
			rate = Def.BR_2_4G;
		else
			rate = Def.SR_2_4G;
	}
	//return (rate*1000);
        return (double)((rate * 1000)); // bits/second
    }
    public double trxTime(MacMessage_802_15_4 p, boolean phyPkt /* = false */) // expressed in seconds
    {
        int phyHeaderLen;
	double trx;
	hdr_cmn ch = p.HDR_CMN();
	
	phyHeaderLen = (phyPkt) ? 0 : Def.defPHY_HEADER_LEN;
	trx = (double)((ch.size() + phyHeaderLen) / getRate_BitsPerSecond('d'));
        if (Def.DEBUG802_15_4_packetsize)
        {
            System.out.println("[" + JistAPI.getTime() + "][#" + localAddr + "][Phy802_15_4.trxTime] - PHY header size (bytes): " + phyHeaderLen);
            System.out.println("[" + JistAPI.getTime() + "][#" + localAddr + "][Phy802_15_4.trxTime] - TOTAL (MAC+PHY+payload) packet size (bytes): " + (ch.size() + phyHeaderLen));
        }
        if (Def.DEBUG802_15_4_transmissiontime)
        {
            System.out.println("transmission rate bits/second " + getRate_BitsPerSecond('d'));
            System.out.println("trx(transmission time required to send the packet of size " + ch.size() + " is = " + trx);
        }
	return trx;
    }
    
    public void construct_PPDU(byte psduLength,MacMessage_802_15_4 psdu)
    {
        //not really a new packet in simulation, but just update some packet header fields.
	hdr_lrwpan wph = psdu.HDR_LRWPAN();
	hdr_cmn ch = psdu.HDR_CMN();
	
	wph.SHR_PreSeq = Def.defSHR_PreSeq;
	wph.SHR_SFD = Def.defSHR_SFD;
	wph.PHR_FrmLen = psduLength;
	//also set channel (for filtering in simulation)
	wph.phyCurrentChannel = ppib.phyCurrentChannel;
	ch.setSize(psduLength + Def.defPHY_HEADER_LEN);
	ch.setTxtime(trxTime(psdu,true));
    }
    
    public void PD_DATA_request(byte psduLength,MacMessage_802_15_4 psdu)
    {
         if (Def.DEBUG802_15_4)
            System.out.println("[PHY.PD_DATA_request()]");
        
        hdr_cmn ch = psdu.HDR_CMN();

	//check packet length
	if (psduLength > Const.aMaxPHYPacketSize)
	{
            System.out.println("[" + JistAPI.getTime() + "]<PHY>(node" + localAddr + ") Invalid PSDU/MPDU length: type = " + Trace.wpan_pName(psdu) +", src = " + Trace.p802_15_4macSA(psdu) + ", dst = " + Trace.p802_15_4macDA(psdu) + ", uid = " + ch.uid() + ", mac_uid = " + psdu.HDR_LRWPAN().uid + ", size = " + ch.size());

            mac.PD_DATA_confirm(PHYenum.p_UNDEFINED);
            return;
	}
	
	if (trx_state == PHYenum.p_TX_ON)
	{
		if (tx_state == PHYenum.p_IDLE)
		{
                    if (Def.DEBUG802_15_4)
                        System.out.println("[" + JistAPI.getTime() + "]<PHY>(node" + localAddr + ") sending pkt: type = " + Trace.wpan_pName(psdu) +", src = " + Trace.p802_15_4macSA(psdu) + ", dst = " + Trace.p802_15_4macDA(psdu) + ", uid = " + ch.uid() + ", mac_uid = " + psdu.HDR_LRWPAN().uid + ", size = " + ch.size());
                
                    //construct a PPDU packet (not really a new packet in simulation, but still <psdu>)
                    construct_PPDU(psduLength,psdu);
                    //somehow the packet size is set to 0 after sendDown() -- ok, the packet is out and anything can happen (we shouldn't care once it's out)
                    //so we have to calculate the transmission time before sendDown()
                    double trx_time = trxTime(psdu,true);
                    //send the packet to Radio (channel target) for transmission
                    txPkt = psdu;
                    txPktCopy = psdu.copy();	//for debug purpose, we still want to access the packet after transmission is done
                    
                    //sendDown(psdu);			//WirelessPhy::sendDown()
                    psdu.HDR_CMN().direction_ = hdr_cmn.dir_t.UP;  // Not in NS-2
                    fieldEntity.transmit(radioInfo, psdu, (long)(trx_time * Constants.SECOND));  
                    
                    tx_state = PHYenum.p_BUSY;		//for carrier sense
                    sendOverH.startTimer(trx_time);
		}
		else	//impossible
                    assert(false);
	}
	else
	{
                if (Def.DEBUG802_15_4)
                    System.out.println("[D][TRX][" + /* __FILE__ + */ " :: " + /* __FUNCTION__ + */ "][" + JistAPI.getTime() + "]<PHY>(node" + localAddr + ") dropping pkt: type = " + Trace.wpan_pName(psdu) +", src = " + Trace.p802_15_4macSA(psdu) + ", dst = " + Trace.p802_15_4macDA(psdu) + ", uid = " + ch.uid() + ", mac_uid = " + psdu.HDR_LRWPAN().uid + ", size = " + ch.size());

		mac.PD_DATA_confirm(trx_state);
	}
    }
    
    public void PD_DATA_indication(byte psduLength,MacMessage_802_15_4 psdu,byte ppduLinkQuality)
    {
        //refer to sec 6.7.8 for LQI details
	hdr_lrwpan wph = psdu.HDR_LRWPAN();

	wph.ppduLinkQuality = ppduLinkQuality;

	if (sendUp(psdu) == 0)
        {
            //	MacMessage_802_15_4::free(psdu);
            psdu = null;
        }
	//else // ???
            //uptarget_.recv(psdu, (Handler) 0);
            //macEntity.receive(psdu); //???
    }
    
    public void PLME_CCA_request()
    {
         if (Def.DEBUG802_15_4)
            System.out.println("[PHY.PLME_CCA_request]");
        
        if (trx_state == PHYenum.p_RX_ON)
	{
		//perform CCA
		//refer to sec 6.7.9 for CCA details
		//we need to delay 8 symbols
		CCAH.startTimer(8/getRate_BitsPerSecond('s'));

	}
	else
		mac.PLME_CCA_confirm(trx_state);
    }
    
    public void PLME_ED_request()
    {
        if (trx_state == PHYenum.p_RX_ON)
	{
		//perform ED
		//refer to sec 6.7.7 for ED implementation details
		//we need to delay 8 symbols
		rxEDPeakPower = rxTotPower[ppib.phyCurrentChannel];
		EDH.startTimer(8/getRate_BitsPerSecond('s'));
	}
	else
		mac.PLME_ED_confirm(trx_state, (byte)0);
    }
    
    public void PLME_GET_request(PPIBAenum PIBAttribute)
    {
        PHYenum t_status;
	
	switch(PIBAttribute)
	{
		case phyCurrentChannel:
		case phyChannelsSupported:
		case phyTransmitPower:
		case phyCCAMode:
			t_status = PHYenum.p_SUCCESS;
			break;
		default:
			t_status = PHYenum.p_UNSUPPORT_ATTRIBUTE;
			break;
	}
	mac.PLME_GET_confirm(t_status,PIBAttribute,/* & */ppib);
    }
    
    public void PLME_SET_TRX_STATE_request(PHYenum state)
    {
        boolean delay;
	PHYenum t_status;
        
         if (Def.DEBUG802_15_4)
            System.out.println("[PHY.PLME_SET_TRX_STATE_request()]");
	
	//ignore any pending request
	if (trx_state_defer_set != PHYenum.p_IDLE)
		trx_state_defer_set = PHYenum.p_IDLE;
	else if (TRXH.active)
	{
		TRXH.cancel();
	}

	t_status = trx_state;
	if (state != trx_state)
	{
		delay = false;
		if (((state == PHYenum.p_RX_ON)||(state == PHYenum.p_TRX_OFF))&&(tx_state == PHYenum.p_BUSY))
		{
			t_status = PHYenum.p_BUSY_TX;
			trx_state_defer_set = state;
		}
		else if (((state == PHYenum.p_TX_ON)||(state == PHYenum.p_TRX_OFF))
		        &&(rxPkt != null)&&(rxPkt.HDR_CMN().error() == 0 /* no error */))			//if after received a valid SFD
		{
			t_status = PHYenum.p_BUSY_RX;
			trx_state_defer_set = state;
		}
		else if (state == PHYenum.p_FORCE_TRX_OFF)
		{
			t_status = (trx_state == PHYenum.p_TRX_OFF) ? PHYenum.p_TRX_OFF : PHYenum.p_SUCCESS;
			trx_state = PHYenum.p_TRX_OFF;
			//terminate reception if needed
			if (rxPkt != null)
			{   
                            if(Def.DEBUG802_15_4)
                            { 
				hdr_cmn ch = rxPkt.HDR_CMN();
				hdr_lrwpan wph = rxPkt.HDR_LRWPAN();
                                System.out.println("[" + JistAPI.getTime() + "]<PHY>(node" + localAddr + ") FORCE TRX OFF sets error bit for incoming pkt: type = " + Trace.wpan_pName(rxPkt) +", src = " + Trace.p802_15_4macSA(rxPkt) + ", dst = " + Trace.p802_15_4macDA(rxPkt) + ", uid = " + ch.uid() + ", mac_uid = " + wph.uid + ", size = " + ch.size());
                            }
				rxPkt.HDR_CMN().setError(1);	//incomplete reception -- force packet discard
			}
			//terminate transmission if needed
			if (tx_state == PHYenum.p_BUSY)
			{
                                if(Def.DEBUG802_15_4)
                                { 
                                    hdr_cmn ch = txPkt.HDR_CMN();
                                    hdr_lrwpan wph = txPkt.HDR_LRWPAN();
                                    System.out.println("[" + JistAPI.getTime() + "]<PHY>(node" + localAddr + ") FORCE TRX OFF sets error bit for outgoing pkt: type = " + Trace.wpan_pName(txPkt) +", src = " + Trace.p802_15_4macSA(txPkt) + ", dst = " + Trace.p802_15_4macDA(txPkt) + ", uid = " + ch.uid() + ", mac_uid = " + wph.uid + ", size = " + ch.size());
                                }

				txPkt.HDR_CMN().setError(1);
				sendOverH.cancel();
				//MacMessage_802_15_4::free(txPktCopy);
				tx_state = PHYenum.p_IDLE;
				mac.PD_DATA_confirm(PHYenum.p_TRX_OFF);
				if (trx_state_defer_set != PHYenum.p_IDLE)
					trx_state_defer_set = PHYenum.p_IDLE;
			}
		}
		else
		{
			t_status = PHYenum.p_SUCCESS;
			if (((state == PHYenum.p_RX_ON)&&(trx_state == PHYenum.p_TX_ON))
			  ||((state == PHYenum.p_TX_ON)&&(trx_state == PHYenum.p_RX_ON)))
			{
				trx_state_turnaround = state;
				delay = true;
			}
			else
				trx_state = state;
		}
		//we need to delay <aTurnaroundTime> symbols if Tx2Rx or Rx2Tx
		if (delay)
		{
			trx_state = PHYenum.p_TRX_OFF;	//should be disabled immediately (further transmission/reception will not succeed)
			TRXH.startTimer(Const.aTurnaroundTime/getRate_BitsPerSecond('s'));
		}
		else
			mac.PLME_SET_TRX_STATE_confirm(t_status);
	}
	else
        {
		mac.PLME_SET_TRX_STATE_confirm(t_status);
                if (Def.DEBUG802_15_4)
                     System.out.println("[" + JistAPI.getTime() + "]<PHY>(node" + localAddr + ") SET TRX: old = " 
                            + 
                            ((trx_state == PHYenum.p_RX_ON)?"RX_ON":
                            (trx_state == PHYenum.p_TX_ON)?"TX_ON":
                            (trx_state == PHYenum.p_TRX_OFF)?"TRX_OFF":"???") 
                            +
                            " req = "
                            + 
                            ((state == PHYenum.p_RX_ON)?"RX_ON":
                            (state == PHYenum.p_TX_ON)?"TX_ON":
                            (state == PHYenum.p_TRX_OFF)?"TRX_OFF":
                            (state == PHYenum.p_FORCE_TRX_OFF)?"FORCE_TRX_OFF":"???") 
                            +
                            " ret = "
                            + 
                            ((t_status == PHYenum.p_RX_ON)  ? "RX_ON"   :
                            (t_status == PHYenum.p_TX_ON)  ? "TX_ON"   :
                            (t_status == PHYenum.p_TRX_OFF)? "TRX_OFF" :
                            (t_status == PHYenum.p_BUSY_TX)? "BUSY_TX" :
                            (t_status == PHYenum.p_BUSY_RX)? "BUSY_RX" :
                            (t_status == PHYenum.p_SUCCESS)? "SUCCESS" : "???"));
        }
    }
    
    public void PLME_SET_request(PPIBAenum PIBAttribute,PHY_PIB PIBAttributeValue)
    {
        PHYenum t_status;
	
	t_status = PHYenum.p_SUCCESS;
	switch(PIBAttribute)
	{
		case phyCurrentChannel:
			if (!channelSupported(PIBAttributeValue.phyCurrentChannel))
				t_status = PHYenum.p_INVALID_PARAMETER;
			if (ppib.phyCurrentChannel != PIBAttributeValue.phyCurrentChannel)
			{
				//any packet in transmission or reception will be corrupted
				if (rxPkt != null)
				{
                                     if(Def.DEBUG802_15_4)
                                     { 
                                            hdr_cmn ch = rxPkt.HDR_CMN();
                                            hdr_lrwpan wph = rxPkt.HDR_LRWPAN();
                                            System.out.println("[" + JistAPI.getTime() + "]<PHY>(node" + localAddr + ") SET phy channel sets error bit for incoming pkt: type = " + Trace.wpan_pName(rxPkt) +", src = " + Trace.p802_15_4macSA(rxPkt) + ", dst = " + Trace.p802_15_4macDA(rxPkt) + ", uid = " + ch.uid() + ", mac_uid = " + wph.uid + ", size = " + ch.size());
                                     }
                                     rxPkt.HDR_CMN().setError(1);
				}
				if (tx_state == PHYenum.p_BUSY)
				{
                                     if(Def.DEBUG802_15_4)
                                     { 
                                            hdr_cmn ch = txPkt.HDR_CMN();
                                            hdr_lrwpan wph = txPkt.HDR_LRWPAN();
                                            System.out.println("[" + JistAPI.getTime() + "]<PHY>(node" + localAddr + ") SET phy channel sets error bit for outgoing pkt: type = " + Trace.wpan_pName(txPkt) +", src = " + Trace.p802_15_4macSA(txPkt) + ", dst = " + Trace.p802_15_4macDA(txPkt) + ", uid = " + ch.uid() + ", mac_uid = " + wph.uid + ", size = " + ch.size());
                                     }

                                    txPkt.HDR_CMN().setError(1);
                                    sendOverH.cancel();
                                    //MacMessage_802_15_4::free(txPktCopy);
                                    tx_state = PHYenum.p_IDLE;
                                    mac.PD_DATA_confirm(PHYenum.p_TRX_OFF);
                                    if (trx_state_defer_set != PHYenum.p_IDLE)
                                        trx_state_defer_set = PHYenum.p_IDLE;
				}
				ppib.phyCurrentChannel = PIBAttributeValue.phyCurrentChannel;
			}
			break;
		case phyChannelsSupported:
			if ((PIBAttributeValue.phyChannelsSupported&0xf8000000) != 0)	//5 MSBs reserved
				t_status = PHYenum.p_INVALID_PARAMETER;
			else
				ppib.phyChannelsSupported = PIBAttributeValue.phyChannelsSupported;
			break;
		case phyTransmitPower:
			if (PIBAttributeValue.phyTransmitPower > 0xbf)
				t_status = PHYenum.p_INVALID_PARAMETER;
			else
				ppib.phyTransmitPower = PIBAttributeValue.phyTransmitPower;
			break;
		case phyCCAMode:
			if ((PIBAttributeValue.phyCCAMode < 1)
			 || (PIBAttributeValue.phyCCAMode > 3))
				t_status = PHYenum.p_INVALID_PARAMETER;
			else
				ppib.phyCCAMode = PIBAttributeValue.phyCCAMode;
			break;
		default:
			t_status = PHYenum.p_UNSUPPORT_ATTRIBUTE;
			break;
	}
	mac.PLME_SET_confirm(t_status,PIBAttribute);
    }
    
    public byte measureLinkQ(MacMessage_802_15_4 p)
    {
        /* NS-2:
        //Link quality measurement is somewhat simulation/implementation specific;
	//here's our way:
	int lq,lq2;

	//consider energy
	// Linux floating number compatibility
	//lq = (int)((p.txinfo_.RxPr/RXThresh_)*128);
	///
	{
	double tmpf;
	tmpf = p.txinfo_.RxPr / RXThresh_;
	lq = (int)(tmpf * 128);
	}
	if (lq > 255) lq = 255;

	//consider signal-to-noise
	// Linux floating number compatibility
	//lq2 = (int)((p.txinfo_.RxPr/HDR_LRWPAN(p).rxTotPower)*255);
	//
	{
	double tmpf;
	tmpf = p.txinfo_.RxPr/p.HDR_LRWPAN().rxTotPower;
	lq2 = (int)(tmpf * 255);
	}
	
	if (lq > lq2) lq = lq2;		//use worst value
		
	return (byte) lq;
        */
          
        return (byte)255; // ??? OLIVER: implement here, don't let 255 which means perfect quality
    }
    
    /* peek */
    public void peek(Message msg)
    {
        // DO NOT IMPLEMENT
    }
    
    public void transmit(Message msg, long delay, long duration,
        MessageAnno anno) {
      transmit(msg); // OLIVER: duration will be computed at PHY Layer ... all mac values will be ignored
    }

    public void transmit(Message msg, MessageAnno anno, long predelay,
        long duration, long postdelay) {
        // TODO
        //fieldEntity.transmit(radioInfo, msg, duration);
        transmit(msg); // OLIVER: duration will be computed at PHY Layer ... all mac values will be ignored
    }
    
    public void transmit(Message msg)
    {
        recv(msg, 0);
    }
    
    public void endTransmit()
    {
        // TODO
    }
    /* Upward call from Field. */
     /* Not in NS-2. Implemented from RadioNoiseIndep */
    public void receive(Message msg, Double powerObj_mW, Long durationObj)
    {
        recv(msg, powerObj_mW); // OLIVER: durationObj is ignored as it is recomputed locally
    }
    
    /* Not in NS-2. Implemented from RadioNoiseIndep */
    public void endReceive(Message msg, Double power, RFChannel rfChannel,
        Object event) {
        // NOT IMPLEMENTED
    }
           
    
    /* Downward call from MAC layers. MAC layer has a packets that needs to be transmitted */
    public void send(Message msg)
    {
        if (msg instanceof MacMessage_802_15_4)
            recv(msg, -1);
        else
            System.out.println("<WARNING>[Phy802_15_4Impl.send(Message)] - Received NON-MacMessage_802_15_4 compatible message. Phy802_15_4 is compatible only with Mac802_15_4 implementations");
    }
    
    // Not in NS-2 (actually, it was under the WirelessPhy)
    private int sendUp(MacMessage_802_15_4 p)
    {
        macEntity.receive(p, null); //????
        return p == null ? 0 : 1;
    }
    
    private void recv(Message msg, double powerObj_mW/* , Handler h */) // powerObj_mW is != -1 from upward calls and -1 for downward calls
    {
         if (Def.DEBUG802_15_4)
            System.out.println("[" + JistAPI.getTime() + "][#"+ localAddr + "][PHY.recv()]");
        
        MacMessage_802_15_4 p = null;
        if (msg instanceof MacMessage_802_15_4)
            p = ((MacMessage_802_15_4)msg).copy();
        if (p == null)
        {
            System.out.println("<WARNING>[Phy802_15_4].recv(msg, powerObj_mW) - a non MacMessage_802_15_4 formatted message received. Cannot handle these. Dropping message");
            return;
        }
        hdr_lrwpan wph = p.HDR_LRWPAN();
	hdr_cmn ch = p.HDR_CMN();
        FrameCtrl frmCtrl = new FrameCtrl();

	switch(ch.direction())
	{
	case DOWN:
            if (Def.DEBUG802_15_4)
                System.out.println("[" + JistAPI.getTime() + "][#" + localAddr + ")[PHY.recv()] -  outgoing pkt: type = " + Trace.wpan_pName(p) +", src = " + Trace.p802_15_4macSA(p) + ", dst = " + Trace.p802_15_4macDA(p) + ", uid = " + ch.uid() + ", mac_uid = " + wph.uid + ", size = " + ch.size());

            PD_DATA_request((byte) ch.size(),p);
            break;
	case UP:
	default:
		/* OLIVER: This was in NS-2. However, the problem is that sendUp() the way I did it pushes the packet to the MAC, before packet receival completes
                 *         and regardless if the packet destination is this node or not.
                 if (sendUp(p) == 0)	
		{
			//MacMessage_802_15_4::free(p);
                        p = null;
			return;
		}*/

		if (NFAILLINK.updateNFailLink(Def.fl_oper_est,/* index_ */ localAddr.hashCode()) == 0)
		{
			//MacMessage_802_15_4::free(p);
                        p = null;
			return;
		}

                if (LFAILLINK.updateLFailLink(Def.fl_oper_est,Trace.p802_15_4macSA(p),localAddr.hashCode()) == 0)	//broadcast packets can still reach here
                {
                        //MacMessage_802_15_4::free(p);
                        p = null;
                        return;
                }

                frmCtrl.FrmCtrl = wph.MHR_FrmCtrl;
                frmCtrl.parse();
		//tap out
		//if (mac.tap() && frmCtrl.frmType == Const.defFrmCtrl_Type_Data)
		//	mac.tap().tap(p);

		//if (node().energy_model() && node().energy_model().adaptivefidelity())
		//	node().energy_model().add_neighbor(Trace.p802_15_4macSA(p));

		//Under whatever condition, we should mark the media as busy.
		// --no matter the packet(s) is for this node or not, no matter
		//   what state the transceiver is in, RX_ON,TX_ON or TRX_OFF, and
		//   no matter which channel is being used.
		//Note that current WirelessPhy.sendUp() prevents packets with (Pr < CSThresh_)
		//from reaching here --. need to modify.WirelessPhy.sendUp() if we want to see
		//all the packets here (but seems no reason to do that).
		//	in dB as can be seen from following:
		//	not very clear (now CPThresh_ is just a ratio, not in dB?)
		
                p.txinfo_.RxPr = powerObj_mW; // OLIVER: Not in NS-2, where it was done differently
                rxTotPower[wph.phyCurrentChannel] += p.txinfo_.RxPr;
                
                if (Def.DEBUG802_15_4_rxTotNum)
                    System.out.println("[#" + localAddr + "-PHY.recv()] Increments rxTotNum (" + rxTotNum[wph.phyCurrentChannel] + " -> " + (rxTotNum[wph.phyCurrentChannel] + 1) + ")");
		rxTotNum[wph.phyCurrentChannel]++;
		if (EDH.active)
		if(rxEDPeakPower < rxTotPower[ppib.phyCurrentChannel])
			rxEDPeakPower = rxTotPower[ppib.phyCurrentChannel];
		
		if ((Trace.p802_15_4macDA(p) == localAddr.hashCode())			//packet for this node
		  ||(Trace.p802_15_4macDA(p) == Def.MAC_BROADCAST))		//broadcast packet
			rxThisTotNum[wph.phyCurrentChannel]++;

		if (trx_state == PHYenum.p_RX_ON)
		if (wph.phyCurrentChannel == ppib.phyCurrentChannel)	//current channel
		if ((Trace.p802_15_4macDA(p) == localAddr.hashCode())			//packet for this node
		  ||(Trace.p802_15_4macDA(p) == Def.MAC_BROADCAST))		//broadcast packet
		if (wph.SHR_SFD == Def.defSHR_SFD)				//valid SFD
		{
                        if (Def.DEBUG802_15_4)
                            System.out.println("[" + JistAPI.getTime() + "]<PHY>(node" + localAddr + ") incoming pkt: type = " + Trace.wpan_pName(p) +", src = " + Trace.p802_15_4macSA(p) + ", dst = " + Trace.p802_15_4macDA(p) + ", uid = " + ch.uid() + ", mac_uid = " + wph.uid + ", size = " + ch.size());
	
                        //wph.colFlag = false;
			if (rxPkt == null)
			{
				rxPkt = p;
				rxPkt.HDR_LRWPAN().rxTotPower = rxTotPower[wph.phyCurrentChannel];
			}
			else
			{
                                if (Def.DEBUG802_15_4)
                                    System.out.println("[D][COL][" + /* __FILE__ + */ " :: " + /* __FUNCTION__ + */ "][" + JistAPI.getTime() + "]<PHY>(node" + localAddr + ") COLLISION: \n\t First (power: " + rxPkt.txinfo_.RxPr +"): type = " + Trace.wpan_pName(rxPkt) +", src = " + Trace.p802_15_4macSA(rxPkt) + ", dst = " + Trace.p802_15_4macDA(rxPkt) + ", uid = " + rxPkt.HDR_CMN().uid_ + ", mac_uid = " + rxPkt.HDR_LRWPAN().uid + ", size = " + rxPkt.HDR_CMN().size() 
                                                     +
                                                    "\n\tSecond (power: " + p.txinfo_.RxPr + "): type = " + Trace.wpan_pName(p) +", src = " + Trace.p802_15_4macSA(p) + ", dst = " + Trace.p802_15_4macDA(p) + ", uid = " + ch.uid() + ", mac_uid = " + wph.uid + ", size = " + ch.size());
                                           

				//wph.colFlag = true;
				//rxPkt.HDR_LRWPAN().colFlag = true;
				//mac.nam.flashNodeColor(JistAPI.getTime());
				if (p.txinfo_.RxPr > rxPkt.txinfo_.RxPr)
				{
					//What should we do if there is a transceiver state set pending?
					//  1. continue defering (could be unbounded delay)
					//..2. set transceiver state now (the incoming packet ignored)
					//We select choice 1, as the traffic rate is supposed to be low.
					rxPkt = p;
					rxPkt.HDR_LRWPAN().rxTotPower = rxTotPower[wph.phyCurrentChannel];
				}
			}

		}
		if (rxPkt != null)
		if (rxPkt.HDR_LRWPAN().rxTotPower < rxTotPower[rxPkt.HDR_LRWPAN().phyCurrentChannel])
			rxPkt.HDR_LRWPAN().rxTotPower = rxTotPower[rxPkt.HDR_LRWPAN().phyCurrentChannel];
		assert(ch.size() > 0);
		if (ch.direction() != hdr_cmn.dir_t.UP)
		{
			System.out.println("MacMessage_802_15_4-flow direction not specified: sending up the stack on default.\n");
			ch.direction_ = hdr_cmn.dir_t.UP;	//we don't want MAC to handle the same problem
		}
		//Scheduler.instance().schedule(/* & */recvOverH, (Event )p, trxTime(p,true));
                recvOverH.startTimer(trxTime(p, true), p);
		break;
	}
    }
    
    public MacMessage_802_15_4 rxPacket() 
    {
        return rxPkt;
    }
	
    public static PHY_PIB PPIB;

    protected void CCAHandler()
    {
         if (Def.DEBUG802_15_4)
            System.out.println("[PHY.CCAHandler()] Note: determines if the channel is idle and reports its findings by calling Mac802_15_4.PLME_CCA_confirm()");
        
        PHYenum t_status;

	//refer to sec 6.7.9 for CCA details
	//  1. CCA will be affected by outgoing packets,
	//     incoming packets (both destined for this device 
	//     and not destined for this device) and other
	//     interferences.
	//  2. In implementation, we don't care about the details 
	//     and just need to perform an actual measurement.
	if ((tx_state == PHYenum.p_BUSY)||(rxTotNum[ppib.phyCurrentChannel] > 0))
	{
		t_status = PHYenum.p_BUSY;
	}
	else if (ppib.phyCCAMode == 1)	//ED detection
	{
		//sec 6.5.3.3 and 6.6.3.4
		// -- receiver sensitivity: -85 dBm or better for 2.4G
		// -- receiver sensitivity: -92 dBm or better for 868M/915M
		//sec 6.7.9
		// -- ED threshold at most 10 dB above receiver sensitivity.
		//For simulations, we simply compare with CSThresh_
		t_status = (rxTotPower[ppib.phyCurrentChannel] >= 
		  /* CSThresh_ */ radioInfo.getSensitivity_mW()  /* CSThresh_ is (W) */) 
		  ? PHYenum.p_BUSY : PHYenum.p_IDLE; // CSThresh - carrier sense threshold
	}
	else if (ppib.phyCCAMode == 2)	//carrier sense only
	{
		t_status = (rxTotNum[ppib.phyCurrentChannel] > 0) ? PHYenum.p_BUSY : PHYenum.p_IDLE;
	}
	else //if (ppib.phyCCAMode == 3)	//both
	{
		t_status = ((rxTotPower[ppib.phyCurrentChannel] >= 
		  /* CSThresh_ */ radioInfo.getSensitivity_mW() /* CSThresh_ is (W) */)
		  &&(rxTotNum[ppib.phyCurrentChannel] > 0)) ? PHYenum.p_BUSY : PHYenum.p_IDLE;
	}
	mac.PLME_CCA_confirm(t_status);
    }
    
    protected void EDHandler()
    {
        int energy;
	byte t_EnergyLevel;

	//refer to sec 6.7.7 for ED implementation details
	//ED is somewhat simulation/implementation specific; here's our way:

	/* Linux floating number compatibility
	energy = (int)((rxEDPeakPower/RXThresh_)*128);
	*/
	{
	double tmpf;
	tmpf = rxEDPeakPower/ /* RXThresh_ */ 
	  radioInfo.getSensitivity_mW() /* RXThresh_ is (W) */; 
	// RXThresh_: receive power threshold (W) OLIVER: same value as CSThresh_
	energy = (int)(tmpf * 128);
	}
	t_EnergyLevel = (energy > 255) ? (byte)255 : (byte)energy;
	mac.PLME_ED_confirm(PHYenum.p_SUCCESS,t_EnergyLevel);
    }
    
    protected void TRXHandler()
    {
        trx_state = trx_state_turnaround;
	//send a confirm
	mac.PLME_SET_TRX_STATE_confirm(trx_state);
    }
    
    protected void recvOverHandler(MacMessage_802_15_4 p)
    {
        byte lq;
	hdr_lrwpan wph = p.HDR_LRWPAN();
	hdr_cmn ch = p.HDR_CMN();

         if (Def.DEBUG802_15_4_rxTotNum)
            System.out.println("[" + JistAPI.getTime() + "] - #"+localAddr+"-PHY.recvOverHandler()] - decrements rxTotNum (for CS) " + rxTotNum[wph.phyCurrentChannel] + " -> " + (rxTotNum[wph.phyCurrentChannel] - 1) + ")");
        
	rxTotPower[wph.phyCurrentChannel] -= p.txinfo_.RxPr;
	rxTotNum[wph.phyCurrentChannel]--;
	
	if (rxTotNum[wph.phyCurrentChannel] == 0)
		rxTotPower[wph.phyCurrentChannel] = 0.0;

	if ((Trace.p802_15_4macDA(p) != localAddr.hashCode())
	  &&(Trace.p802_15_4macDA(p) != Def.MAC_BROADCAST))	//packet not for this node (interference)
        {
		//MacMessage_802_15_4::free(p);
                p = null;
        }
	else if (p != rxPkt)				//packet corrupted (not the strongest one) or un-detectable
	{
                if (Def.DEBUG802_15_4)
                    System.out.println("[D][" + ((wph.phyCurrentChannel != ppib.phyCurrentChannel) ? "CHN" : "NOT") + "][+ " + /* __FILE__ + */ "::" + /* __FUNCTION__ + */ "::" /* __LINE__*/ + "][" + JistAPI.getTime() + "]<PHY>(node " + localAddr + ") dropping pkt: type = " + Trace.wpan_pName(p) + ", src = " + Trace.p802_15_4macSA(p) + ", dst = " + Trace.p802_15_4macDA(p) + ", uid = " + ch.uid() + ", mac_uid = " + wph.uid + ", size = " + ch.size());

                rxThisTotNum[wph.phyCurrentChannel]--;
		//drop(p,(wph.phyCurrentChannel != ppib.phyCurrentChannel) ? "CHN":"NOT"); ???
	}
	else
	{
		rxThisTotNum[wph.phyCurrentChannel]--;
		//measure (here calculate) the link quality
		lq = measureLinkQ(p);
		//ch.size()  -= Def.defPHY_HEADER_LEN;
                ch.setSize(ch.size() - Def.defPHY_HEADER_LEN);
		rxPkt = null;
		if ((ch.size() <= 0) 
		    ||(ch.size() > Const.aMaxPHYPacketSize)
		    ||ch.error() == 1)		//incomplete reception (due to FORCE_TRX_OFF),data packet received during ED or other errors
		{
                    
                        if(Def.DEBUG802_15_4_err)
        			System.out.println("[D][ERR][" + /* __FILE__ + */  "::" + /* __FUNCTION__ + */ "::" + JistAPI.getTime() + "][" + /*__LINE__*/  "]<PHY>(node " + localAddr + ") dropping pkt: type = " + Trace.wpan_pName(p) + ", src = " + Trace.p802_15_4macSA(p) + ", dst = " + Trace.p802_15_4macDA(p) + ", uid = " + ch.uid() + ", mac_uid = " + wph.uid + ", size = " + ch.size());

			//drop(p,"ERR");
		}
		else
		{
                        if(Def.DEBUG802_15_4)
        			System.out.println("[" + /* __FILE__ + */ "::" + /* __FUNCTION__ + */ "][" + JistAPI.getTime() + "]<PHY>(node" + localAddr + ") incoming pkt:  type = " + Trace.wpan_pName(p) + ", src = " + Trace.p802_15_4macSA(p) + ", dst = " + Trace.p802_15_4macDA(p) + ", uid = " + ch.uid() + ", mac_uid = " + wph.uid + ", size = " + ch.size());

			PD_DATA_indication((byte)ch.size(),p,lq);	//MAC sublayer need to further check if the packet
								//is really received successfully or not.
		}
		if (trx_state_defer_set != PHYenum.p_IDLE)
		{
			trx_state_turnaround = trx_state_defer_set;
			trx_state_defer_set = PHYenum.p_IDLE;
			if (trx_state_turnaround == PHYenum.p_TRX_OFF)
			{
				trx_state = trx_state_turnaround;
				mac.PLME_SET_TRX_STATE_confirm(trx_state);
			}
			else
			{
				//we need to delay <aTurnaroundTime> symbols for Rx2Tx
				trx_state = PHYenum.p_TRX_OFF;	//should be disabled immediately (further reception will not succeed)
				TRXH.startTimer(Const.aTurnaroundTime/getRate_BitsPerSecond('s'));
			}
		}
	}
    }
    
    protected void sendOverHandler()
    {
        if(Def.DEBUG802_15_4)
		System.out.println("[" + /* __FILE__ + */ "::" + /* __FUNCTION__ + */ "][" + JistAPI.getTime() + "]<PHY>(node" + localAddr + ") sending over:  type = " + Trace.wpan_pName(txPktCopy) + ", src = " + Trace.p802_15_4macSA(txPktCopy) + ", dst = " + Trace.p802_15_4macDA(txPktCopy) + ", uid = " + txPktCopy.HDR_CMN().uid() + ", mac_uid = " + txPktCopy.HDR_LRWPAN().uid + ", size = " + txPktCopy.HDR_CMN().size());

	assert(tx_state == PHYenum.p_BUSY);
	assert(txPktCopy != null);
	//MacMessage_802_15_4::free(txPktCopy);
	tx_state = PHYenum.p_IDLE;
	mac.PD_DATA_confirm(PHYenum.p_SUCCESS);
	if (trx_state_defer_set != PHYenum.p_IDLE)
	{
		trx_state_turnaround = trx_state_defer_set;
		trx_state_defer_set = PHYenum.p_IDLE;
		if (trx_state_turnaround == PHYenum.p_TRX_OFF)
		{
			trx_state = trx_state_turnaround;
			mac.PLME_SET_TRX_STATE_confirm(trx_state);
		}
		else
		{
			//we need to delay <aTurnaroundTime> symbols for Rx2Tx
			trx_state = PHYenum.p_TRX_OFF;	//should be disabled immediately (further transmission will not succeed)
			TRXH.startTimer(Const.aTurnaroundTime/getRate_BitsPerSecond('s'));
		}
	}
    }

    public void endSetChannel(RFChannel channel) {
      // TODO Auto-generated method stub
      
    }

    public RFChannel getChannel() throws Continuation {
      // TODO Auto-generated method stub
      return null;
    }

    public void setChannel(RFChannel channel, long delay) {
      // TODO Auto-generated method stub
      
    }

};
