[net2-wg] Default LPL implementation

Razvan Musaloiu-E. razvanm at cs.jhu.edu
Tue Jan 6 17:19:32 PST 2009


Hi!

I have ready an implementation of a Default LPL. A git head with it 
is "default-lpl" from here:
 	http://hinrg.cs.jhu.edu/git/?p=razvanm/tinyos-2.x.git (web)
 	git://hinrg.cs.jhu.edu/git/razvanm/tinyos-2.x.git
I also attached a patch against the latest CVS tree.

Some notes:

  * In tests/TestNetworkLpl there is a version of the TestNetwork that is
    using the default lpl. The tweaking of the LPL is done using 3 defines:
 	LPL_DEF_LOCAL_SLEEP
 	LPL_DEF_RX_SLEEP
 	DELAY_AFTER_RECEIVE
    In the Wyman Park Testbed (47 nodes + one on desk + a basestation) with
    0.5s of sleeping and 20ms of delay after receive I got 98%-99% delivery
    ratio with a 5% duty-cycle. The test was a little longer than 14h and
    each SEND_INTERVAL was 60s.

  * Both the basestation and clients are reporting their stats using
    printf. In the TestNetworkLpl there is a tos-dump.py script that
    properly displays the data from both basestation and clients.

  * The stats are maintain using some global functions constucted using
    some macros. The code for this is in 3 files: Globals.h, GlobalsC.nc
    and GlobalsP.nc.

  * The LowPowerListening interface is strip downed to only provide the
    functions that provide sleepInterval values.

  * The LowPowerListeningControl is not yet implemented in LPL (but it is
    used by the LPP which is also almost ready).

If somebody (Om?) have time to give it a try and/or give some feedback 
would be great. :-)

--
Razvan ME
-------------- next part --------------
diff --git a/apps/tests/TestNetwork/TestNetworkC.nc b/apps/tests/TestNetwork/TestNetworkC.nc
index 28e248f..cb46243 100644
--- a/apps/tests/TestNetwork/TestNetworkC.nc
+++ b/apps/tests/TestNetwork/TestNetworkC.nc
@@ -49,7 +49,8 @@ implementation {
   bool firstTimer = TRUE;
   uint16_t seqno;
   enum {
-    SEND_INTERVAL = 8192
+//    SEND_INTERVAL = 8192
+    SEND_INTERVAL = 60*1024U
   };
 
   event void ReadSensor.readDone(error_t err, uint16_t val) { }  
@@ -113,10 +114,10 @@ implementation {
 
  
   event void Timer.fired() {
-    uint16_t nextInt;
+    uint32_t nextInt;
     call Leds.led0Toggle();
     dbg("TestNetworkC", "TestNetworkC: Timer fired.\n");
-    nextInt = call Random.rand16() % SEND_INTERVAL;
+    nextInt = call Random.rand32() % SEND_INTERVAL;
     nextInt += SEND_INTERVAL >> 1;
     call Timer.startOneShot(nextInt);
     if (!sendBusy)
diff --git a/apps/tests/TestNetworkLpl/CtpP.nc b/apps/tests/TestNetworkLpl/CtpP.nc
new file mode 100644
index 0000000..add5412
--- /dev/null
+++ b/apps/tests/TestNetworkLpl/CtpP.nc
@@ -0,0 +1,208 @@
+/*
+ * "Copyright (c) 2005 The Regents of the University  of California.  
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation for any purpose, without fee, and without written agreement is
+ * hereby granted, provided that the above copyright notice, the following
+ * two paragraphs and the author appear in all copies of this software.
+ * 
+ * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
+ * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF
+ * CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ * 
+ * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS."
+ *
+ */
+/*
+ * Copyright (c) 2006 Stanford University.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - 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.
+ * - Neither the name of the Stanford University nor the names of
+ *   its contributors may be used to endorse or promote products derived
+ *   from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``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 STANFORD
+ * UNIVERSITY OR ITS CONTRIBUTORS 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.
+ */
+
+#include "Ctp.h"
+
+/**
+ * A data collection service that uses a tree routing protocol
+ * to deliver data to collection roots, following TEP 119.
+ *
+ * @author Rodrigo Fonseca
+ * @author Omprakash Gnawali
+ * @author Kyle Jamieson
+ * @author Philip Levis
+ */
+
+
+configuration CtpP {
+  provides {
+    interface StdControl;
+    interface Send[uint8_t client];
+    interface Receive[collection_id_t id];
+    interface Receive as Snoop[collection_id_t];
+    interface Intercept[collection_id_t id];
+
+    interface Packet;
+    interface CollectionPacket;
+    interface CtpPacket;
+
+    interface CtpInfo;
+    interface LinkEstimator;
+    interface CtpCongestion;
+    interface RootControl;    
+  }
+
+  uses {
+    interface CollectionId[uint8_t client];
+    interface CollectionDebug;
+  }
+}
+
+implementation {
+  enum {
+    CLIENT_COUNT = uniqueCount(UQ_CTP_CLIENT),
+    FORWARD_COUNT = 12,
+    TREE_ROUTING_TABLE_SIZE = 10,
+    QUEUE_SIZE = CLIENT_COUNT + FORWARD_COUNT,
+    CACHE_SIZE = 4,
+  };
+
+  components ActiveMessageC;
+  components new CtpForwardingEngineP() as Forwarder;
+  components MainC, LedsC;
+  
+  Send = Forwarder;
+  StdControl = Forwarder;
+  Receive = Forwarder.Receive;
+  Snoop = Forwarder.Snoop;
+  Intercept = Forwarder;
+  Packet = Forwarder;
+  CollectionId = Forwarder;
+  CollectionPacket = Forwarder;
+  CtpPacket = Forwarder;
+  CtpCongestion = Forwarder;
+  
+  components new PoolC(message_t, FORWARD_COUNT) as MessagePoolP;
+  components new PoolC(fe_queue_entry_t, FORWARD_COUNT) as QEntryPoolP;
+  Forwarder.QEntryPool -> QEntryPoolP;
+  Forwarder.MessagePool -> MessagePoolP;
+
+  components new QueueC(fe_queue_entry_t*, QUEUE_SIZE) as SendQueueP;
+  Forwarder.SendQueue -> SendQueueP;
+
+  components new LruCtpMsgCacheC(CACHE_SIZE) as SentCacheP;
+  Forwarder.SentCache -> SentCacheP;
+
+  components new TimerMilliC() as RoutingBeaconTimer;
+  components new TimerMilliC() as RouteUpdateTimer;
+  components LinkEstimatorP as Estimator;
+  Forwarder.LinkEstimator -> Estimator;
+
+  components new LplAMSenderC(AM_CTP_DATA) as AMSenderC;
+  components new AMReceiverC(AM_CTP_DATA);
+  components new AMSnooperC(AM_CTP_DATA);
+
+  components new CtpRoutingEngineP(TREE_ROUTING_TABLE_SIZE, 128, 512000) as Router;
+
+  StdControl = Router;
+  StdControl = Estimator;
+  RootControl = Router;
+  MainC.SoftwareInit -> Router;
+  Router.BeaconSend -> Estimator.Send;
+  Router.BeaconReceive -> Estimator.Receive;
+  Router.LinkEstimator -> Estimator.LinkEstimator;
+
+  Router.CompareBit -> Estimator.CompareBit;
+
+  Router.AMPacket -> ActiveMessageC;
+  Router.RadioControl -> ActiveMessageC;
+  Router.BeaconTimer -> RoutingBeaconTimer;
+  Router.RouteTimer -> RouteUpdateTimer;
+  Router.CollectionDebug = CollectionDebug;
+  Forwarder.CollectionDebug = CollectionDebug;
+  Forwarder.CtpInfo -> Router;
+  Router.CtpCongestion -> Forwarder;
+  CtpInfo = Router;
+
+  
+  components new TimerMilliC() as RetxmitTimer;
+  Forwarder.RetxmitTimer -> RetxmitTimer;
+
+  components RandomC;
+  Router.Random -> RandomC;
+  Forwarder.Random -> RandomC;
+
+  MainC.SoftwareInit -> Forwarder;
+  Forwarder.SubSend -> AMSenderC;
+  Forwarder.SubReceive -> AMReceiverC;
+  Forwarder.SubSnoop -> AMSnooperC;
+  Forwarder.SubPacket -> AMSenderC;
+  Forwarder.RootControl -> Router;
+  Forwarder.UnicastNameFreeRouting -> Router.Routing;
+  Forwarder.RadioControl -> ActiveMessageC;
+  Forwarder.PacketAcknowledgements -> AMSenderC.Acks;
+  Forwarder.AMPacket -> AMSenderC;
+  Forwarder.Leds -> LedsC;
+  
+  components new LplAMSenderC(AM_CTP_ROUTING) as SendControl;
+  components new AMReceiverC(AM_CTP_ROUTING) as ReceiveControl;
+
+  LinkEstimator = Estimator;
+  
+  Estimator.Random -> RandomC;
+
+  Estimator.AMSend -> SendControl;
+  Estimator.SubReceive -> ReceiveControl;
+  Estimator.SubPacket -> SendControl;
+  Estimator.SubAMPacket -> SendControl;
+
+#if defined(PLATFORM_TELOSB) || defined(PLATFORM_MICAZ)
+#ifndef TOSSIM
+  components CC2420ActiveMessageC as PlatformActiveMessageC;
+#else
+  components DummyActiveMessageP as PlatformActiveMessageC;
+#endif
+#elif defined (PLATFORM_MICA2) || defined (PLATFORM_MICA2DOT)
+  components CC1000ActiveMessageC as PlatformActiveMessageC;
+#elif defined(PLATFORM_EYESIFXV1) || defined(PLATFORM_EYESIFXV2)
+  components WhiteBitAccessorC as PlatformActiveMessageC;    
+#else
+  components DummyActiveMessageP as PlatformActiveMessageC;
+#endif
+
+  Estimator.LinkPacketMetadata -> PlatformActiveMessageC;
+
+  // eventually
+  //  Estimator.LinkPacketMetadata -> ActiveMessageC;
+
+  MainC.SoftwareInit -> Estimator;
+}
diff --git a/apps/tests/TestNetworkLpl/DefaultLplP.nc b/apps/tests/TestNetworkLpl/DefaultLplP.nc
new file mode 100644
index 0000000..cc92737
--- /dev/null
+++ b/apps/tests/TestNetworkLpl/DefaultLplP.nc
@@ -0,0 +1,423 @@
+/*
+ * Copyright (c) 2005-2006 Rincon Research Corporation
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - 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.
+ * - Neither the name of the Rincon Research Corporation nor the names of
+ *   its contributors may be used to endorse or promote products derived
+ *   from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``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 THE
+ * RINCON RESEARCH OR ITS CONTRIBUTORS 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
+ */
+
+/**
+ * Low Power Listening for the CC2420.  This component is responsible for
+ * delivery of an LPL packet, and for turning off the radio when the radio
+ * has run out of tasks.
+ *
+ * The PowerCycle component is responsible for duty cycling the radio
+ * and performing receive detections.
+ *
+ * @author David Moss
+ */
+
+#include "DefaultLpl.h"
+#include "AM.h"
+#include "Globals.h"
+
+module DefaultLplP {
+  provides {
+    interface Init;
+    interface LowPowerListening;
+    interface Send;
+    interface Receive;
+  }
+  
+  uses {
+    interface Send as SubSend;
+    interface CC2420Transmit as Resend;
+    interface RadioBackoff;
+    interface Receive as SubReceive;
+    interface AMPacket;
+    interface SplitControl as SubControl;
+    interface PowerCycle;
+    interface CC2420PacketBody;
+    interface PacketAcknowledgements;
+    interface State as SendState;
+    interface State as RadioPowerState;
+    interface State as SplitControlState;
+    interface Timer<TMilli> as OffTimer;
+    interface Timer<TMilli> as SendDoneTimer;
+    interface Random;
+    interface Leds;
+  }
+}
+
+implementation {
+  
+  /** The message currently being sent */
+  norace message_t *currentSendMsg;
+  
+  /** The length of the current send message */
+  uint8_t currentSendLen;
+  
+  /** TRUE if the radio is duty cycling and not always on */
+  bool dutyCycling;
+
+  /**
+   * Radio Power State
+   */
+  enum {
+    S_OFF, // off by default
+    S_TURNING_ON,
+    S_ON,
+    S_TURNING_OFF,
+  };
+  
+  /**
+   * Send States
+   */
+  enum {
+    S_IDLE,
+    S_SENDING,
+  };
+  
+  enum {
+    ONE_MESSAGE = 0,
+  };
+  
+  /***************** Prototypes ***************/
+  task void send();
+  task void resend();
+  task void startRadio();
+  task void stopRadio();
+  
+  void initializeSend();
+  void startOffTimer();
+  uint16_t getActualDutyCycle(uint16_t dutyCycle);
+  
+  /***************** Init Commands ***************/
+  command error_t Init.init() {
+    dutyCycling = FALSE;
+    return SUCCESS;
+  }
+  
+  /***************** LowPowerListening Commands ***************/
+  /**
+   * Set this this node's radio sleep interval, in milliseconds.
+   * Once every interval, the node will sleep and perform an Rx check 
+   * on the radio.  Setting the sleep interval to 0 will keep the radio
+   * always on.
+   *
+   * This is the equivalent of setting the local duty cycle rate.
+   *
+   * @param sleepIntervalMs the length of this node's Rx check interval, in [ms]
+   */
+  command void LowPowerListening.setLocalSleepInterval(
+      uint16_t sleepIntervalMs) {
+    call PowerCycle.setSleepInterval(sleepIntervalMs);
+  }
+  
+  /**
+   * @return the local node's sleep interval, in [ms]
+   */
+  command uint16_t LowPowerListening.getLocalSleepInterval() {
+    return call PowerCycle.getSleepInterval();
+  }
+  
+  /**
+   * Configure this outgoing message so it can be transmitted to a neighbor mote
+   * with the specified Rx sleep interval.
+   * @param msg Pointer to the message that will be sent
+   * @param sleepInterval The receiving node's sleep interval, in [ms]
+   */
+  command void LowPowerListening.setRxSleepInterval(message_t *msg, 
+      uint16_t sleepIntervalMs) {
+    (call CC2420PacketBody.getMetadata(msg))->rxInterval = sleepIntervalMs;
+  }
+  
+  /**
+   * @return the destination node's sleep interval configured in this message
+   */
+  command uint16_t LowPowerListening.getRxSleepInterval(message_t *msg) {
+    return (call CC2420PacketBody.getMetadata(msg))->rxInterval;
+  }
+  
+  /***************** Send Commands ***************/
+  /**
+   * Each call to this send command gives the message a single
+   * DSN that does not change for every copy of the message
+   * sent out.  For messages that are not acknowledged, such as
+   * a broadcast address message, the receiving end does not
+   * signal receive() more than once for that message.
+   */
+  command error_t Send.send(message_t *msg, uint8_t len) {
+    if(call SplitControlState.getState() == S_OFF) {
+      // Everything is off right now, start SplitControl and try again
+      return EOFF;
+    }
+    
+    if(call SendState.requestState(S_LPL_SENDING) == SUCCESS) {
+      currentSendMsg = msg;
+      currentSendLen = len;
+      
+      // In case our off timer is running...
+      call OffTimer.stop();
+      call SendDoneTimer.stop();
+      
+      if(call RadioPowerState.getState() == S_ON) {
+        initializeSend();
+        return SUCCESS;
+        
+      } else {
+        post startRadio();
+      }
+      
+      return SUCCESS;
+    }
+    
+    return EBUSY;
+  }
+
+  command error_t Send.cancel(message_t *msg) {
+    if(currentSendMsg == msg) {
+      call SendState.toIdle();
+      call SendDoneTimer.stop();
+      startOffTimer();
+      return call SubSend.cancel(msg);
+    }
+    
+    return FAIL;
+  }
+  
+  
+  command uint8_t Send.maxPayloadLength() {
+    return call SubSend.maxPayloadLength();
+  }
+
+  command void *Send.getPayload(message_t* msg, uint8_t len) {
+    return call SubSend.getPayload(msg, len);
+  }
+  
+  
+  /***************** RadioBackoff Events ****************/
+  async event void RadioBackoff.requestInitialBackoff(message_t *msg) {
+    if((call CC2420PacketBody.getMetadata(msg))->rxInterval 
+        > ONE_MESSAGE) {
+      call RadioBackoff.setInitialBackoff( call Random.rand16() 
+          % (0x4 * CC2420_BACKOFF_PERIOD) + CC2420_MIN_BACKOFF);
+    }
+  }
+  
+  async event void RadioBackoff.requestCongestionBackoff(message_t *msg) {
+    if((call CC2420PacketBody.getMetadata(msg))->rxInterval 
+        > ONE_MESSAGE) {
+      call RadioBackoff.setCongestionBackoff( call Random.rand16() 
+          % (0x3 * CC2420_BACKOFF_PERIOD) + CC2420_MIN_BACKOFF);
+    }
+  }
+  
+  async event void RadioBackoff.requestCca(message_t *msg) {
+  }
+  
+
+  /***************** DutyCycle Events ***************/
+  /**
+   * A transmitter was detected.  You must now take action to
+   * turn the radio off when the transaction is complete.
+   */
+  event void PowerCycle.detected() {
+    // At this point, the duty cycling has been disabled temporary
+    // and it will be this component's job to turn the radio back off
+    // Wait long enough to see if we actually receive a packet, which is
+    // just a little longer in case there is more than one lpl transmitter on
+    // the channel.
+    
+    startOffTimer();
+  }
+  
+  
+  /***************** SubControl Events ***************/
+  event void SubControl.startDone(error_t error) {
+    if(!error) {
+      setRadioOnStart(getLocalTime());
+      call RadioPowerState.forceState(S_ON);
+      
+      if(call SendState.getState() == S_LPL_FIRST_MESSAGE
+          || call SendState.getState() == S_LPL_SENDING) {
+        initializeSend();
+      }
+    }
+  }
+    
+  event void SubControl.stopDone(error_t error) {
+    if(!error) {
+      addRadioOn(getLocalTime() - getRadioOnStart());
+      if(call SendState.getState() == S_LPL_FIRST_MESSAGE
+          || call SendState.getState() == S_LPL_SENDING) {
+        // We're in the middle of sending a message; start the radio back up
+        post startRadio();
+        
+      } else {        
+        call OffTimer.stop();
+        call SendDoneTimer.stop();
+      }
+    }
+  }
+  
+  /***************** SubSend Events ***************/
+  event void SubSend.sendDone(message_t* msg, error_t error) {
+   
+    switch(call SendState.getState()) {
+    case S_LPL_SENDING:
+      if(call SendDoneTimer.isRunning()) {
+        if(!call PacketAcknowledgements.wasAcked(msg)) {
+          post resend();
+          return;
+        }
+      }
+      break;
+      
+    case S_LPL_CLEAN_UP:
+      /**
+       * We include this state so upper layers can't send a different message
+       * before the last message gets done sending
+       */
+      break;
+      
+    default:
+      break;
+    }  
+    
+    call SendState.toIdle();
+    call SendDoneTimer.stop();
+    startOffTimer();
+    signal Send.sendDone(msg, error);
+  }
+  
+  /***************** SubReceive Events ***************/
+  /**
+   * If the received message is new, we signal the receive event and
+   * start the off timer.  If the last message we received had the same
+   * DSN as this message, then the chances are pretty good
+   * that this message should be ignored, especially if the destination address
+   * as the broadcast address
+   */
+  event message_t *SubReceive.receive(message_t* msg, void* payload, 
+      uint8_t len) {
+    incRecvPackets();
+    startOffTimer();
+    return signal Receive.receive(msg, payload, len);
+  }
+  
+  /***************** Timer Events ****************/
+  event void OffTimer.fired() {    
+    /*
+     * Only stop the radio if the radio is supposed to be off permanently
+     * or if the duty cycle is on and our sleep interval is not 0
+     */
+    if(call SplitControlState.getState() == S_OFF
+        || (call PowerCycle.getSleepInterval() > 0
+            && call SplitControlState.getState() != S_OFF
+                && call SendState.getState() == S_LPL_NOT_SENDING)) { 
+      post stopRadio();
+    }
+  }
+  
+  /**
+   * When this timer is running, that means we're sending repeating messages
+   * to a node that is receive check duty cycling.
+   */
+  event void SendDoneTimer.fired() {
+    if(call SendState.getState() == S_LPL_SENDING) {
+      // The next time SubSend.sendDone is signaled, send is complete.
+      call SendState.forceState(S_LPL_CLEAN_UP);
+    }
+  }
+  
+  /***************** Resend Events ****************/
+  /**
+   * Signal that a message has been sent
+   *
+   * @param p_msg message to send.
+   * @param error notifaction of how the operation went.
+   */
+  async event void Resend.sendDone( message_t* p_msg, error_t error ) {
+    // This is actually caught by SubSend.sendDone
+  }
+  
+  
+  /***************** Tasks ***************/
+  task void send() {
+    incSentPackets();
+    if(call SubSend.send(currentSendMsg, currentSendLen) != SUCCESS) {
+      post send();
+    }
+  }
+  
+  task void resend() {
+    incSentPackets();
+    if(call Resend.resend(TRUE) != SUCCESS) {
+      post resend();
+    }
+  }
+  
+  task void startRadio() {
+    if(call SubControl.start() != SUCCESS) {
+      post startRadio();
+    }
+  }
+  
+  task void stopRadio() {
+    if(call SendState.getState() == S_LPL_NOT_SENDING) {
+      if(call SubControl.stop() != SUCCESS) {
+        post stopRadio();
+      }
+    }
+  }
+  
+  /***************** Functions ***************/
+  void initializeSend() {
+    if(call LowPowerListening.getRxSleepInterval(currentSendMsg) 
+      > ONE_MESSAGE) {
+    
+      if(call AMPacket.destination(currentSendMsg) == AM_BROADCAST_ADDR) {
+        call PacketAcknowledgements.noAck(currentSendMsg);
+      } else {
+        // Send it repetitively within our transmit window
+        call PacketAcknowledgements.requestAck(currentSendMsg);
+      }
+
+      call SendDoneTimer.startOneShot(
+          call LowPowerListening.getRxSleepInterval(currentSendMsg) + 20);
+    }
+        
+    post send();
+  }
+  
+  
+  void startOffTimer() {
+    call OffTimer.startOneShot(DELAY_AFTER_RECEIVE);
+  }
+  
+}
+
diff --git a/apps/tests/TestNetworkLpl/Globals.h b/apps/tests/TestNetworkLpl/Globals.h
new file mode 100644
index 0000000..b6352d8
--- /dev/null
+++ b/apps/tests/TestNetworkLpl/Globals.h
@@ -0,0 +1,41 @@
+#ifndef GLOBALS_H
+#define GLOBALS_H
+
+nx_struct Globals {
+  nx_uint32_t RadioOn;
+  nx_uint32_t RadioOnStart;
+  nx_uint32_t Wakeups;
+  nx_uint32_t CcaChecks;
+  nx_uint32_t SentPackets;
+  nx_uint32_t RecvPackets;
+};
+
+  nx_struct Globals __g;
+
+  uint16_t getGlobalsSize() { return sizeof(__g); }
+  void *getGlobals() { return (void*)&__g; }
+  uint32_t getLocalTime();
+
+#define GLOBALS_GET(name) typeof(__g.name) get##name() { return __g.name; }
+#define GLOBALS_SET(name) void set##name(typeof(__g.name) val) { __g.name = val; }
+#define GLOBALS_INC(name) void inc##name() { __g.name++; }
+#define GLOBALS_ADD(name) void add##name(typeof(__g.name) val) { __g.name += val; }
+
+  GLOBALS_GET(RadioOn);
+  GLOBALS_ADD(RadioOn);
+
+  GLOBALS_GET(RadioOnStart);
+  GLOBALS_SET(RadioOnStart);
+
+  GLOBALS_GET(Wakeups);
+  GLOBALS_INC(Wakeups);
+
+  GLOBALS_GET(CcaChecks);
+  GLOBALS_INC(CcaChecks);
+
+  GLOBALS_GET(SentPackets);
+  GLOBALS_INC(SentPackets);
+
+  GLOBALS_GET(RecvPackets);
+  GLOBALS_INC(RecvPackets);
+#endif
diff --git a/apps/tests/TestNetworkLpl/GlobalsC.nc b/apps/tests/TestNetworkLpl/GlobalsC.nc
new file mode 100644
index 0000000..911ec22
--- /dev/null
+++ b/apps/tests/TestNetworkLpl/GlobalsC.nc
@@ -0,0 +1,13 @@
+configuration GlobalsC { }
+
+implementation
+{
+  components GlobalsP;
+  components new CounterToLocalTimeC(T32khz);
+  components new TransformCounterC(T32khz, uint32_t, T32khz, uint16_t, 0, uint32_t) as Transform;
+  components Msp430Counter32khzC;
+
+  CounterToLocalTimeC.Counter -> Transform;
+  Transform.CounterFrom -> Msp430Counter32khzC;
+  GlobalsP.LocalTime -> CounterToLocalTimeC;
+}
diff --git a/apps/tests/TestNetworkLpl/GlobalsP.nc b/apps/tests/TestNetworkLpl/GlobalsP.nc
new file mode 100644
index 0000000..1512175
--- /dev/null
+++ b/apps/tests/TestNetworkLpl/GlobalsP.nc
@@ -0,0 +1,11 @@
+#include "Globals.h"
+
+module GlobalsP
+{
+  uses interface LocalTime<T32khz> as LocalTime;
+}
+
+implementation
+{
+  uint32_t getLocalTime() @C() { return call LocalTime.get(); }
+}
diff --git a/apps/tests/TestNetworkLpl/GlobalsPrintfC.nc b/apps/tests/TestNetworkLpl/GlobalsPrintfC.nc
new file mode 100644
index 0000000..e39f75d
--- /dev/null
+++ b/apps/tests/TestNetworkLpl/GlobalsPrintfC.nc
@@ -0,0 +1,12 @@
+configuration GlobalsPrintfC { }
+
+implementation
+{
+  components GlobalsPrintfP;
+  components new TimerMilliC();
+  components MainC, LedsC, NoLedsC;
+
+  GlobalsPrintfP.Boot -> MainC;
+  GlobalsPrintfP.Timer -> TimerMilliC;
+  GlobalsPrintfP.Leds -> NoLedsC;
+}
diff --git a/apps/tests/TestNetworkLpl/GlobalsPrintfP.nc b/apps/tests/TestNetworkLpl/GlobalsPrintfP.nc
new file mode 100644
index 0000000..88333c0
--- /dev/null
+++ b/apps/tests/TestNetworkLpl/GlobalsPrintfP.nc
@@ -0,0 +1,30 @@
+module GlobalsPrintfP
+{
+  uses {
+    interface Boot;
+    interface Timer<TMilli>;
+    interface Leds;
+  }
+}
+
+implementation
+{
+  event void Boot.booted()
+  {
+    call Timer.startPeriodic(1024);
+    call Leds.led2On();
+  }
+
+  event void Timer.fired()
+  {
+#ifdef LOW_POWER_LISTENING
+    call Leds.led2Toggle();
+    printf("g: %ld r %ld w %ld c %ld tx %ld rx %ld\n", getLocalTime(), getRadioOn(), getWakeups(), getCcaChecks(), getSentPackets(), getRecvPackets());
+    printfflush();
+#elif LOW_POWER_PROBING
+    call Leds.led2Toggle();
+    printf("g: %ld r %ld w %ld tx %ld rx %ld\n", getLocalTime(), getRadioOn(), getWakeups(), getSentPackets(), getRecvPackets());
+    printfflush();
+#endif
+  }
+}
diff --git a/apps/tests/TestNetworkLpl/Makefile b/apps/tests/TestNetworkLpl/Makefile
new file mode 100644
index 0000000..171df63
--- /dev/null
+++ b/apps/tests/TestNetworkLpl/Makefile
@@ -0,0 +1,82 @@
+COMPONENT=TestNetworkAppC
+
+CFLAGS += -DLOW_POWER_LISTENING
+CFLAGS += -DLPL_DEF_LOCAL_SLEEP=512
+CFLAGS += -DLPL_DEF_RX_SLEEP=512
+CFLAGS += -DDELAY_AFTER_RECEIVE=20
+
+#CFLAGS += -I$(TOSDIR)/lib/lpp
+#CFLAGS += -DLOW_POWER_PROBING
+#CFLAGS += -DLPL_DEF_LOCAL_SLEEP=2048
+#CFLAGS += -DLPL_DEF_RX_SLEEP=2048
+#CFLAGS += -DDELAY_AFTER_RECEIVE=100
+
+CFLAGS += -DCC2420_DEF_CHANNEL=12
+CFLAGS += -I.
+CFLAGS += -I$(TOSDIR)/lib/printf
+CFLAGS += -I../TestNetwork
+CFLAGS += -DTOSH_DATA_LENGTH=114
+CFLAGS += -DPRINTF_MSG_LENGTH=70
+
+CFLAGS += -I$(TOSDIR)/lib/net \
+          -I$(TOSDIR)/lib/net/drip \
+          -I$(TOSDIR)/lib/net/4bitle \
+          -I$(TOSDIR)/lib/net/ctp -DNO_DEBUG
+
+TFLAGS += -I$(TOSDIR)/../apps/tests/TestDissemination \
+          -I$(TOSDIR)/../support/sdk/c \
+          -I$(TOSDIR)/types \
+          -I.
+
+LIBMOTE = $(TOSDIR)/../support/sdk/c/libmote.a
+#BUILD_EXTRA_DEPS += tn-injector #tn-listener
+LISTEN_OBJS = collection_msg.o test_network_msg.o tn-listener.o $(LIBMOTE)
+INJECT_OBJS = set_rate_msg.o tn-injector.o collection_debug_msg.o $(LIBMOTE)
+
+# arguments: output filename stem, input filename, struct name
+define mig_templ
+MIGFILES += $(1).c $(1).h $(1).java $(1).o
+$(1).c:
+	mig -o $(1).h c -target=$$(PLATFORM) $$(CFLAGS) $$(TFLAGS) $(2) $(3)
+$(1).java:
+	mig -o $(1).java java -target=$$(PLATFORM) $$(CFLAGS) $$(TFLAGS) $(2) $(3)
+endef
+
+$(eval $(call mig_templ,test_network_msg,TestNetwork.h,TestNetworkMsg))
+$(eval $(call mig_templ,set_rate_msg,$(TOSDIR)/lib/net/DisseminationEngine.h,dissemination_message))
+$(eval $(call mig_templ,collection_debug_msg,$(TOSDIR)/lib/net/collection/CollectionDebugMsg.h,CollectionDebugMsg))
+
+%.o: %.c
+	gcc -v  $(TFLAGS) $(CFLAGS) -c -o $@ $<
+
+tn-listener: $(LISTEN_OBJS)
+	gcc -v $(TFLAGS) $(CFLAGS) -o $@ $(LISTEN_OBJS)
+
+tn-injector: $(INJECT_OBJS)
+	gcc -v $(TFLAGS) $(CFLAGS) -o $@ $(INJECT_OBJS)
+
+#tn-listener.o: tn-listener.c
+#	gcc $(TFLAGS) $(CFLAGS) -c -o $@ $<
+
+tn-injector.o: tn-injector.c test_network_msg.c
+	gcc $(TFLAGS) $(CFLAGS) -c -o $@ $<
+
+#test_network_msg.c:
+#	mig -o test_network_msg.h c -target=$(PLATFORM) $(CFLAGS) $(TFLAGS) TestNetwork.h TestNetworkMsg 
+
+#set_rate_msg.c:
+#	mig -o set_rate_msg.h c -target=$(PLATFORM) $(CFLAGS) $(TFLAGS) $(TOSDIR)/lib/net/DisseminationEngine.h dissemination_message
+
+#set_rate_msg.o: set_rate_msg.c
+#	gcc $(CFLAGS) $(TFLAGS) -c -o $@ $<
+
+#test_network_msg.o: test_network_msg.c
+#	gcc $(CFLAGS) $(TFLAGS) -c -o $@ $<
+
+#collection_msg.c:
+#	mig -o collection_msg.h c -target=$(PLATFORM) $(CFLAGS) $(TFLAGS) $(TOSDIR)/lib/net/collection/ForwardingEngine.h collection_header
+
+include $(MAKERULES)
+
+migclean:
+	rm -rf $(MIGFILES)
diff --git a/apps/tests/TestNetworkLpl/PowerCycleP.nc b/apps/tests/TestNetworkLpl/PowerCycleP.nc
new file mode 100644
index 0000000..bb376dd
--- /dev/null
+++ b/apps/tests/TestNetworkLpl/PowerCycleP.nc
@@ -0,0 +1,317 @@
+/*
+ * Copyright (c) 2005-2006 Rincon Research Corporation
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - 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.
+ * - Neither the name of the Rincon Research Corporation nor the names of
+ *   its contributors may be used to endorse or promote products derived
+ *   from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``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 THE
+ * RINCON RESEARCH OR ITS CONTRIBUTORS 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
+ */
+ 
+/** 
+ * Module to duty cycle the radio on and off, performing CCA receive checks.
+ * When a carrier is sensed, this will leave the radio on. It is then up
+ * to higher layers to turn the radio off again.  Once the radio is turned
+ * off, this module will automatically continue duty cycling and looking for
+ * a modulated signal.
+ *
+ * Suggested TODO's:
+ *  > TransmitC and ReceiveC provide Energy, Byte, and Packet indicators.
+ *    Tap into those to add more detection levels and granularity. Only let
+ *    the radio turn off when we're not actively receiving bytes.  Right now
+ *    the packet indicator is a little backwards.
+ *  > Let one component be in charge of maintaining State information about
+ *    the power of the radio, probably lower in the stack.
+ *  > Wire SplitControl, Send, and Receive through this component.  Make it
+ *    responsible for packet-level detections and being completely responsible
+ *    for controlling the power of the radio without the use of upper layers
+ *  > Remove unnecessary State components and Timers.
+ *
+ * @author David Moss
+ */
+
+#include "DefaultLpl.h"
+#include "Globals.h"
+
+module PowerCycleP {
+  provides {
+    interface PowerCycle;
+    interface SplitControl;
+  }
+
+  uses {
+    interface Timer<TMilli> as OnTimer;
+    interface SplitControl as SubControl;
+    interface State as RadioPowerState;
+    interface State as SplitControlState;
+    interface State as SendState;
+    interface Leds;
+    interface ReceiveIndicator as EnergyIndicator;
+    interface ReceiveIndicator as ByteIndicator;
+    interface ReceiveIndicator as PacketIndicator;
+  }
+}
+
+implementation {
+  
+  /** The current period of the duty cycle, equivalent of wakeup interval */
+  uint16_t sleepInterval = LPL_DEF_LOCAL_SLEEP;
+  
+  /** The number of times the CCA has been sampled in this wakeup period */
+  uint16_t ccaChecks;
+  
+  /**
+   * Radio Power, Check State, and Duty Cycling State
+   */
+  enum {
+    S_OFF, // off by default
+    S_TURNING_ON,
+    S_ON,
+    S_TURNING_OFF,
+  };
+  
+  
+  /***************** Prototypes ****************/
+  task void stopRadio();
+  task void startRadio();
+  task void getCca();
+  
+  bool finishSplitControlRequests();
+  bool isDutyCycling();
+  
+  
+  /***************** PowerCycle Commands ****************/
+  /**
+   * Set the sleep interval, in binary milliseconds
+   * @param sleepIntervalMs the sleep interval in [ms]
+   */
+  command void PowerCycle.setSleepInterval(uint16_t sleepIntervalMs) {
+    if (!sleepInterval && sleepIntervalMs) {
+      // We were always on, now lets duty cycle
+      post stopRadio();  // Might want to delay turning off the radio
+    }
+    
+    sleepInterval = sleepIntervalMs;
+    
+    if(sleepInterval == 0 && call SplitControlState.isState(S_ON)) {
+      /*
+       * Leave the radio on permanently if sleepInterval == 0 and the radio is 
+       * supposed to be enabled
+       */
+      if(call RadioPowerState.getState() == S_OFF) {
+        call SubControl.start();
+      }
+    }
+  }
+  
+  /**
+   * @return the sleep interval in [ms]
+   */
+  command uint16_t PowerCycle.getSleepInterval() {
+    return sleepInterval;
+  }
+  
+  
+  /***************** SplitControl Commands ****************/
+  command error_t SplitControl.start() {
+    if(call SplitControlState.isState(S_ON)) {
+      return EALREADY;
+      
+    } else if(call SplitControlState.isState(S_TURNING_ON)) {
+      return SUCCESS;
+    
+    } else if(!call SplitControlState.isState(S_OFF)) {
+      return EBUSY;
+    }
+    
+    // Radio was off, now has been told to turn on or duty cycle.
+    call SplitControlState.forceState(S_TURNING_ON);
+    
+    printf("start: sleepInterval %d\n", sleepInterval);
+    if(sleepInterval > 0) {
+      // Begin duty cycling
+      post stopRadio();
+      return SUCCESS;
+      
+    } else {
+      post startRadio();
+      return SUCCESS;
+    }
+  }
+  
+  command error_t SplitControl.stop() {
+    if(call SplitControlState.isState(S_OFF)) {
+      return EALREADY;
+      
+    } else if(call SplitControlState.isState(S_TURNING_OFF)) {
+      return SUCCESS;
+    
+    } else if(!call SplitControlState.isState(S_ON)) {
+      return EBUSY;
+    }
+    
+    call SplitControlState.forceState(S_TURNING_OFF);
+    post stopRadio();
+    return SUCCESS;
+  }
+  
+  /***************** Timer Events ****************/
+  event void OnTimer.fired() {
+    if(isDutyCycling()) {
+      if(call RadioPowerState.getState() == S_OFF) {
+        ccaChecks = 0;
+        
+         /*
+          * Turn on the radio only after the uC is fully awake.  ATmega128's 
+          * have this issue when running on an external crystal.
+          */
+         post getCca();
+        
+      } else {
+        // Someone else turned on the radio, try again in awhile
+        call OnTimer.startOneShot(sleepInterval);
+      }
+    }
+  }
+  
+  /***************** SubControl Events ****************/
+  event void SubControl.startDone(error_t error) {
+    call RadioPowerState.forceState(S_ON);
+    //call Leds.led2On();
+    
+    if(finishSplitControlRequests()) {
+      return;
+      
+    } else if(isDutyCycling()) {
+      incWakeups();
+      post getCca();
+    }
+  }
+  
+  event void SubControl.stopDone(error_t error) {
+    call RadioPowerState.forceState(S_OFF);
+    //call Leds.led2Off();
+    
+    if(finishSplitControlRequests()) {
+      return;
+      
+    } else if(isDutyCycling()) {
+      call OnTimer.startOneShot(sleepInterval);
+    }
+    
+  }
+  
+  
+  /***************** Tasks ****************/
+  task void stopRadio() {
+    error_t error = call SubControl.stop();
+    if(error != SUCCESS) {
+      // Already stopped?
+      finishSplitControlRequests();
+      call OnTimer.startOneShot(sleepInterval);
+    }
+  }
+  
+  task void startRadio() {
+    if(call SubControl.start() != SUCCESS) {
+      post startRadio();
+    }
+  }
+  
+  task void getCca() {
+    uint8_t detects = 0;
+    if(isDutyCycling()) {
+      
+      ccaChecks++;
+      if(ccaChecks == 1) {
+        // Microcontroller is ready, turn on the radio and sample a few times
+        post startRadio();
+        return;
+      }
+
+      atomic {
+        for( ; ccaChecks < MAX_LPL_CCA_CHECKS && call SendState.isIdle(); ccaChecks++) {
+	  incCcaChecks();
+          if(call PacketIndicator.isReceiving()) {
+            signal PowerCycle.detected();
+            return;
+          }
+          
+          if(call EnergyIndicator.isReceiving()) {
+            detects++;
+            if(detects > MIN_SAMPLES_BEFORE_DETECT) {
+              signal PowerCycle.detected(); 
+              return;
+            }
+            // Leave the radio on for upper layers to perform some transaction
+          }
+        }
+      }
+      
+      if(call SendState.isIdle()) {
+        post stopRadio();
+      }
+    }  
+  }
+  
+  /**
+   * @return TRUE if the radio should be actively duty cycling
+   */
+  bool isDutyCycling() {
+    return sleepInterval > 0 && call SplitControlState.isState(S_ON);
+  }
+  
+  
+  /**
+   * @return TRUE if we successfully handled a SplitControl request
+   */
+  bool finishSplitControlRequests() {
+    if(call SplitControlState.isState(S_TURNING_OFF)) {
+      call SplitControlState.forceState(S_OFF);
+      signal SplitControl.stopDone(SUCCESS);
+      return TRUE;
+      
+    } else if(call SplitControlState.isState(S_TURNING_ON)) {
+      // Starting while we're duty cycling first turns off the radio
+      call SplitControlState.forceState(S_ON);
+      signal SplitControl.startDone(SUCCESS);
+      return TRUE;
+    }
+    
+    return FALSE;
+  }
+  
+  /**************** Defaults ****************/
+  default event void PowerCycle.detected() {
+  }
+
+
+  default event void SplitControl.startDone(error_t error) {
+  }
+  
+  default event void SplitControl.stopDone(error_t error) {
+  }
+}
+
+
diff --git a/apps/tests/TestNetworkLpl/TestNetworkAppC.nc b/apps/tests/TestNetworkLpl/TestNetworkAppC.nc
new file mode 100644
index 0000000..cac615b
--- /dev/null
+++ b/apps/tests/TestNetworkLpl/TestNetworkAppC.nc
@@ -0,0 +1,71 @@
+/**
+ * TestNetworkC exercises the basic networking layers, collection and
+ * dissemination. The application samples DemoSensorC at a basic rate
+ * and sends packets up a collection tree. The rate is configurable
+ * through dissemination.
+ *
+ * See TEP118: Dissemination, TEP 119: Collection, and TEP 123: The
+ * Collection Tree Protocol for details.
+ * 
+ * @author Philip Levis
+ * @version $Revision$ $Date$
+ */
+#include "TestNetwork.h"
+#include "Ctp.h"
+
+configuration TestNetworkAppC {}
+implementation {
+  components TestNetworkC, MainC, LedsC, ActiveMessageC;
+  components DisseminationC;
+  components new DisseminatorC(uint16_t, SAMPLE_RATE_KEY) as Object16C;
+  components CollectionC as Collector;
+  components new CollectionSenderC(CL_TEST);
+  components new TimerMilliC();
+  components new DemoSensorC();
+  components new SerialAMSenderC(CL_TEST);
+  components SerialActiveMessageC;
+#ifndef NO_DEBUG
+  components new SerialAMSenderC(AM_COLLECTION_DEBUG) as UARTSender;
+  components UARTDebugSenderP as DebugSender;
+#endif
+  components RandomC;
+  components new QueueC(message_t*, 12);
+  components new PoolC(message_t, 12);
+
+  TestNetworkC.Boot -> MainC;
+  TestNetworkC.RadioControl -> ActiveMessageC;
+  TestNetworkC.SerialControl -> SerialActiveMessageC;
+  TestNetworkC.RoutingControl -> Collector;
+  TestNetworkC.DisseminationControl -> DisseminationC;
+  TestNetworkC.Leds -> LedsC;
+  TestNetworkC.Timer -> TimerMilliC;
+  TestNetworkC.DisseminationPeriod -> Object16C;
+  TestNetworkC.Send -> CollectionSenderC;
+  TestNetworkC.ReadSensor -> DemoSensorC;
+  TestNetworkC.RootControl -> Collector;
+  TestNetworkC.Receive -> Collector.Receive[CL_TEST];
+  TestNetworkC.UARTSend -> SerialAMSenderC.AMSend;
+  TestNetworkC.CollectionPacket -> Collector;
+  TestNetworkC.CtpInfo -> Collector;
+  TestNetworkC.CtpCongestion -> Collector;
+  TestNetworkC.Random -> RandomC;
+  TestNetworkC.Pool -> PoolC;
+  TestNetworkC.Queue -> QueueC;
+  TestNetworkC.RadioPacket -> ActiveMessageC;
+  TestNetworkC.Lpl -> ActiveMessageC;
+  
+#ifndef NO_DEBUG
+  components new PoolC(message_t, 10) as DebugMessagePool;
+  components new QueueC(message_t*, 10) as DebugSendQueue;
+  DebugSender.Boot -> MainC;
+  DebugSender.UARTSend -> UARTSender;
+  DebugSender.MessagePool -> DebugMessagePool;
+  DebugSender.SendQueue -> DebugSendQueue;
+  Collector.CollectionDebug -> DebugSender;
+  TestNetworkC.CollectionDebug -> DebugSender;
+#endif
+  TestNetworkC.AMPacket -> ActiveMessageC;
+
+  components GlobalsC;
+  components GlobalsPrintfC;
+}
diff --git a/apps/tests/TestNetworkLpl/TestNetworkC.nc b/apps/tests/TestNetworkLpl/TestNetworkC.nc
new file mode 100644
index 0000000..6bb407e
--- /dev/null
+++ b/apps/tests/TestNetworkLpl/TestNetworkC.nc
@@ -0,0 +1,214 @@
+/**
+ * TestNetworkC exercises the basic networking layers, collection and
+ * dissemination. The application samples DemoSensorC at a basic rate
+ * and sends packets up a collection tree. The rate is configurable
+ * through dissemination. The default send rate is every 10s.
+ *
+ * See TEP118: Dissemination and TEP 119: Collection for details.
+ * 
+ * @author Philip Levis
+ * @version $Revision$ $Date$
+ */
+
+#include <Timer.h>
+#include "TestNetwork.h"
+#include "CtpDebugMsg.h"
+
+module TestNetworkC {
+  uses interface Boot;
+  uses interface SplitControl as RadioControl;
+  uses interface SplitControl as SerialControl;
+  uses interface StdControl as RoutingControl;
+  uses interface StdControl as DisseminationControl;
+  uses interface DisseminationValue<uint16_t> as DisseminationPeriod;
+  uses interface Send;
+  uses interface Leds;
+  uses interface Read<uint16_t> as ReadSensor;
+  uses interface Timer<TMilli>;
+  uses interface RootControl;
+  uses interface Receive;
+  uses interface AMSend as UARTSend;
+  uses interface CollectionPacket;
+  uses interface CtpInfo;
+  uses interface CtpCongestion;
+  uses interface Random;
+  uses interface Queue<message_t*>;
+  uses interface Pool<message_t>;
+  uses interface CollectionDebug;
+  uses interface AMPacket;
+  uses interface Packet as RadioPacket;
+  uses interface LowPowerListening as Lpl;
+}
+implementation {
+  task void uartEchoTask();
+  message_t packet;
+  message_t uartpacket;
+  message_t* recvPtr = &uartpacket;
+  uint8_t msglen;
+  bool sendBusy = FALSE;
+  bool uartbusy = FALSE;
+  bool firstTimer = TRUE;
+  uint16_t seqno;
+  enum {
+    SEND_INTERVAL = 60*1024U
+  };
+
+  event void ReadSensor.readDone(error_t err, uint16_t val) { }  
+
+  event void Boot.booted() {
+    call SerialControl.start();
+  }
+  event void SerialControl.startDone(error_t err) {
+    if (TOS_NODE_ID % 500 == 0) {
+      call Lpl.setLocalSleepInterval(0);
+    }
+    call RadioControl.start();
+  }
+  event void RadioControl.startDone(error_t err) {
+    if (err != SUCCESS) {
+      call RadioControl.start();
+    }
+    else {
+      //call DisseminationControl.start();
+      call RoutingControl.start();
+      if (TOS_NODE_ID % 500 == 0) {
+	call RootControl.setRoot();
+      }
+      seqno = 0;
+        call Timer.startOneShot(call Random.rand32() % SEND_INTERVAL);
+    }
+  }
+
+  event void RadioControl.stopDone(error_t err) {}
+  event void SerialControl.stopDone(error_t err) {}	
+
+  void failedSend() {
+    dbg("App", "%s: Send failed.\n", __FUNCTION__);
+    call CollectionDebug.logEvent(NET_C_DBG_1);
+  }
+
+   
+  void sendMessage() {
+    TestNetworkMsg* msg = (TestNetworkMsg*)call Send.getPayload(&packet, sizeof(TestNetworkMsg));
+    uint16_t metric;
+    am_addr_t parent;
+
+    call CtpInfo.getParent(&parent);
+    call CtpInfo.getEtx(&metric);
+
+    msg->source = TOS_NODE_ID;
+    msg->seqno = seqno;
+    msg->data = 0xCAFE;
+    msg->parent = parent;
+    msg->hopcount = 0;
+    msg->metric = metric;
+
+    if (call Send.send(&packet, sizeof(TestNetworkMsg)) != SUCCESS) {
+      failedSend();
+      call Leds.led0On();
+      dbg("TestNetworkC", "%s: Transmission failed.\n", __FUNCTION__);
+    }
+    else {
+      sendBusy = TRUE;
+      seqno++; 
+      dbg("TestNetworkC", "%s: Transmission succeeded.\n", __FUNCTION__);
+    }
+  }
+
+ 
+  event void Timer.fired() {
+    uint32_t nextInt;
+    call Leds.led0Toggle();
+    dbg("TestNetworkC", "TestNetworkC: Timer fired.\n");
+    nextInt = call Random.rand32() % SEND_INTERVAL;
+    nextInt += SEND_INTERVAL >> 1;
+    call Timer.startOneShot(nextInt);
+    if (!sendBusy)
+	sendMessage();
+  }
+
+  event void Send.sendDone(message_t* m, error_t err) {
+    if (err != SUCCESS) {
+	//      call Leds.led0On();
+    }
+    sendBusy = FALSE;
+    dbg("TestNetworkC", "Send completed.\n");
+  }
+  
+  event void DisseminationPeriod.changed() {
+    const uint16_t* newVal = call DisseminationPeriod.get();
+    call Timer.stop();
+    call Timer.startPeriodic(*newVal);
+  }
+
+  event message_t* 
+  Receive.receive(message_t* msg, void* payload, uint8_t len) {
+    dbg("TestNetworkC", "Received packet at %s from node %hhu.\n", sim_time_string(), call CollectionPacket.getOrigin(msg));
+    call Leds.led1Toggle();    
+    if (!call Pool.size() <= (TEST_NETWORK_QUEUE_SIZE < 4)? 1:3)  {
+      //      call CtpCongestion.setClientCongested(TRUE);
+    }
+    if (!call Pool.empty() && call Queue.size() < call Queue.maxSize()) {
+      message_t* tmp = call Pool.get();
+      call Queue.enqueue(msg);
+      if (!uartbusy) {
+        post uartEchoTask();
+      }
+      return tmp;
+    }
+    return msg;
+ }
+
+ task void uartEchoTask() {
+    dbg("Traffic", "Sending packet to UART.\n");
+   if (call Queue.empty()) {
+     return;
+   }
+   else if (!uartbusy) {
+     message_t* msg = call Queue.dequeue();
+     dbg("Traffic", "Sending packet to UART.\n");
+     if (call UARTSend.send(0xffff, msg, call RadioPacket.payloadLength(msg)) == SUCCESS) {
+       uartbusy = TRUE;
+     }
+     else {
+      call CollectionDebug.logEventMsg(NET_C_DBG_2,
+				       call CollectionPacket.getSequenceNumber(msg),
+				       call CollectionPacket.getOrigin(msg),
+				       call AMPacket.destination(msg));
+     }
+   }
+ }
+
+  event void UARTSend.sendDone(message_t *msg, error_t error) {
+    dbg("Traffic", "UART send done.\n");
+    uartbusy = FALSE;
+    call Pool.put(msg);
+    if (!call Queue.empty()) {
+      post uartEchoTask();
+    } 
+    else {
+      //        call CtpCongestion.setClientCongested(FALSE);
+    }
+  }
+
+  /* Default implementations for CollectionDebug calls.
+   * These allow CollectionDebug not to be wired to anything if debugging
+   * is not desired. */
+
+    default command error_t CollectionDebug.logEvent(uint8_t type) {
+        return SUCCESS;
+    }
+    default command error_t CollectionDebug.logEventSimple(uint8_t type, uint16_t arg) {
+        return SUCCESS;
+    }
+    default command error_t CollectionDebug.logEventDbg(uint8_t type, uint16_t arg1, uint16_t arg2, uint16_t arg3) {
+        return SUCCESS;
+    }
+    default command error_t CollectionDebug.logEventMsg(uint8_t type, uint16_t msg, am_addr_t origin, am_addr_t node) {
+        return SUCCESS;
+    }
+    default command error_t CollectionDebug.logEventRoute(uint8_t type, am_addr_t parent, uint8_t hopcount, uint16_t metric) {
+        return SUCCESS;
+    }
+ 
+}
diff --git a/apps/tests/TestNetworkLpl/tos-dump.py b/apps/tests/TestNetworkLpl/tos-dump.py
new file mode 100755
index 0000000..6fdef40
--- /dev/null
+++ b/apps/tests/TestNetworkLpl/tos-dump.py
@@ -0,0 +1,49 @@
+#!/usr/bin/env python
+
+import sys, time
+import tos
+
+class Test(tos.Packet):
+    def __init__(self, payload = None):
+        tos.Packet.__init__(self,
+                               [('source',   'int', 2),
+                                ('seqno',    'int', 2),
+                                ('parent',   'int', 2),
+                                ('metric',   'int', 2),
+                                ('data',     'int', 2),
+                                ('hopcount', 'int', 1),
+                                ('sendCount','int', 2),
+                                ('sendSuccessCount','int', 2)],
+                               payload)
+
+class CtpData(tos.Packet):
+    def __init__(self, payload = None):
+        tos.Packet.__init__(self,
+                               [('options',     'int', 1),
+                                ('thl',         'int', 1),
+                                ('etx',         'int', 2),
+                                ('origin',      'int', 2),
+                                ('originSeqNo', 'int', 1),
+                                ('collectionId','int', 1),
+                                ('data',  'blob', None)],
+                               payload)
+
+if len(sys.argv) < 2:
+    print "Usage:", sys.argv[0], "serial@/dev/ttyUSB0:57600"
+    sys.exit()
+
+#s = tos.Serial(sys.argv[1], int(sys.argv[2]), debug=False)
+am = tos.AM()
+
+while True:
+    p = am.read()
+    if p:
+        if p.type == 238:
+            ts = "%.4f" % time.time()
+            ctp = CtpData(p.data)
+            test = Test(ctp.data)
+            print ts, '\t', ctp
+            print ts, '\t', test
+        else:
+            print p
+
diff --git a/support/sdk/python/tos.py b/support/sdk/python/tos.py
index 83c08ad..280d1ce 100644
--- a/support/sdk/python/tos.py
+++ b/support/sdk/python/tos.py
@@ -406,8 +406,9 @@ def printfHook(packet):
     if packet.type == 100:
         s = "".join([chr(i) for i in packet.data]).strip('\0')
         lines = s.split('\n')
+        ts = time.time()
         for line in lines:
-            if line: print "PRINTF:", line
+            if line: print "%.4f PRINTF: %s" % (ts, line)
         packet = None # No further processing for the printf packet
     return packet    
 
diff --git a/tos/chips/cc2420/CC2420.h b/tos/chips/cc2420/CC2420.h
index 798107f..4beba0b 100644
--- a/tos/chips/cc2420/CC2420.h
+++ b/tos/chips/cc2420/CC2420.h
@@ -166,6 +166,17 @@ typedef nx_struct cc2420_packet_t {
 #endif
 
 
+/**
+ * The LPL defaults to stay-on.
+ */
+#ifndef LPL_DEF_LOCAL_SLEEP
+#define LPL_DEF_LOCAL_SLEEP 0
+#endif
+
+#ifndef LPL_DEF_RX_SLEEP
+#define LPL_DEF_RX_SLEEP 0
+#endif
+
 enum {
   // size of the header not including the length byte
   MAC_HEADER_SIZE = sizeof( cc2420_header_t ) - 1,
diff --git a/tos/chips/cc2420/lowpan/CC2420TinyosNetworkP.nc b/tos/chips/cc2420/lowpan/CC2420TinyosNetworkP.nc
index e490f37..00c9db6 100644
--- a/tos/chips/cc2420/lowpan/CC2420TinyosNetworkP.nc
+++ b/tos/chips/cc2420/lowpan/CC2420TinyosNetworkP.nc
@@ -39,6 +39,7 @@
  */
  
 #include "CC2420.h"
+#include "Globals.h"
 
 module CC2420TinyosNetworkP @safe() {
   provides {
@@ -77,11 +78,13 @@ implementation {
   
   /***************** SubSend Events *****************/
   event void SubSend.sendDone(message_t* msg, error_t error) {
+    incSentPackets();
     signal Send.sendDone(msg, error);
   }
   
   /***************** SubReceive Events ***************/
   event message_t *SubReceive.receive(message_t *msg, void *payload, uint8_t len) {
+    incRecvPackets();
     if((call CC2420PacketBody.getHeader(msg))->network == TINYOS_6LOWPAN_NETWORK_ID) {
       return signal Receive.receive(msg, payload, len);
       
diff --git a/tos/chips/cc2420/lpl/DefaultLplP.nc b/tos/chips/cc2420/lpl/DefaultLplP.nc
index 3ada9db..bb99ebd 100644
--- a/tos/chips/cc2420/lpl/DefaultLplP.nc
+++ b/tos/chips/cc2420/lpl/DefaultLplP.nc
@@ -112,7 +112,6 @@ implementation {
   
   void initializeSend();
   void startOffTimer();
-  uint16_t getActualDutyCycle(uint16_t dutyCycle);
   
   /***************** Init Commands ***************/
   command error_t Init.init() {
@@ -144,36 +143,6 @@ implementation {
   }
   
   /**
-   * Set this node's radio duty cycle rate, in units of [percentage*100].
-   * For example, to get a 0.05% duty cycle,
-   * <code>
-   *   call LowPowerListening.setDutyCycle(5);
-   * </code>
-   *
-   * For a 100% duty cycle (always on),
-   * <code>
-   *   call LowPowerListening.setDutyCycle(10000);
-   * </code>
-   *
-   * This is the equivalent of setting the local sleep interval explicitly.
-   * 
-   * @param dutyCycle The duty cycle percentage, in units of [percentage*100]
-   */
-  command void LowPowerListening.setLocalDutyCycle(uint16_t dutyCycle) {
-    call PowerCycle.setSleepInterval(
-        call LowPowerListening.dutyCycleToSleepInterval(dutyCycle));
-  }
-  
-  /**
-   * @return this node's radio duty cycle rate, in units of [percentage*100]
-   */
-  command uint16_t LowPowerListening.getLocalDutyCycle() {
-    return call LowPowerListening.sleepIntervalToDutyCycle(
-        call PowerCycle.getSleepInterval());
-  }
-  
-  
-  /**
    * Configure this outgoing message so it can be transmitted to a neighbor mote
    * with the specified Rx sleep interval.
    * @param msg Pointer to the message that will be sent
@@ -191,65 +160,6 @@ implementation {
     return (call CC2420PacketBody.getMetadata(msg))->rxInterval;
   }
   
-  /**
-   * Configure this outgoing message so it can be transmitted to a neighbor mote
-   * with the specified Rx duty cycle rate.
-   * Duty cycle is in units of [percentage*100], i.e. 0.25% duty cycle = 25.
-   * 
-   * @param msg Pointer to the message that will be sent
-   * @param dutyCycle The duty cycle of the receiving mote, in units of 
-   *     [percentage*100]
-   */
-  command void LowPowerListening.setRxDutyCycle(message_t *msg, 
-      uint16_t dutyCycle) {
-    (call CC2420PacketBody.getMetadata(msg))->rxInterval =
-        call LowPowerListening.dutyCycleToSleepInterval(dutyCycle);
-  }
-  
-    
-  /**
-   * @return the destination node's duty cycle configured in this message
-   *     in units of [percentage*100]
-   */
-  command uint16_t LowPowerListening.getRxDutyCycle(message_t *msg) {
-    return call LowPowerListening.sleepIntervalToDutyCycle(
-        (call CC2420PacketBody.getMetadata(msg))->rxInterval);
-  }
-  
-  /**
-   * Convert a duty cycle, in units of [percentage*100], to
-   * the sleep interval of the mote in milliseconds
-   * @param dutyCycle The duty cycle in units of [percentage*100]
-   * @return The equivalent sleep interval, in units of [ms]
-   */
-  command uint16_t LowPowerListening.dutyCycleToSleepInterval(
-      uint16_t dutyCycle) {
-    dutyCycle = getActualDutyCycle(dutyCycle);
-    
-    if(dutyCycle == 10000) {
-      return 0;
-    }
-    
-    return ((uint32_t)DUTY_ON_TIME * (10000 - dutyCycle)) / dutyCycle;
-  }
-  
-  /**
-   * Convert a sleep interval, in units of [ms], to a duty cycle
-   * in units of [percentage*100]
-   * @param sleepInterval The sleep interval in units of [ms]
-   * @return The duty cycle in units of [percentage*100]
-   */
-  command uint16_t LowPowerListening.sleepIntervalToDutyCycle(
-      uint16_t sleepInterval) {
-    if(sleepInterval == 0) {
-      return 10000;
-    }
-    
-    return getActualDutyCycle(((uint32_t)DUTY_ON_TIME * 10000) 
-        / (sleepInterval + DUTY_ON_TIME));
-  }
-
-  
   /***************** Send Commands ***************/
   /**
    * Each call to this send command gives the message a single
@@ -503,18 +413,5 @@ implementation {
     call OffTimer.startOneShot(DELAY_AFTER_RECEIVE);
   }
   
-  /**
-   * Check the bounds on a given duty cycle
-   * We're never over 100%, and we're never at 0%
-   */
-  uint16_t getActualDutyCycle(uint16_t dutyCycle) {
-    if(dutyCycle > 10000) {
-      return 10000;
-    } else if(dutyCycle == 0) {
-      return 1;
-    }
-    
-    return dutyCycle;
-  }  
 }
 
diff --git a/tos/chips/cc2420/lpl/DummyLplP.nc b/tos/chips/cc2420/lpl/DummyLplP.nc
index dbc6bb5..defce61 100644
--- a/tos/chips/cc2420/lpl/DummyLplP.nc
+++ b/tos/chips/cc2420/lpl/DummyLplP.nc
@@ -51,13 +51,6 @@ implementation {
     return 0;
   }
   
-  command void LowPowerListening.setLocalDutyCycle(uint16_t dutyCycle) {
-  }
-  
-  command uint16_t LowPowerListening.getLocalDutyCycle() {
-    return 10000;
-  }
-  
   command void LowPowerListening.setRxSleepInterval(message_t *msg, uint16_t sleepIntervalMs) {
   }
   
@@ -65,20 +58,5 @@ implementation {
     return 0;
   }
   
-  command void LowPowerListening.setRxDutyCycle(message_t *msg, uint16_t dutyCycle) {
-  }
-  
-  command uint16_t LowPowerListening.getRxDutyCycle(message_t *msg) {
-    return 10000;
-  }
-  
-  command uint16_t LowPowerListening.dutyCycleToSleepInterval(uint16_t dutyCycle) {
-    return 0;
-  }
-  
-  command uint16_t LowPowerListening.sleepIntervalToDutyCycle(uint16_t sleepInterval) {
-    return 10000;
-  }
-  
 }
 
diff --git a/tos/chips/cc2420/lpl/PowerCycleP.nc b/tos/chips/cc2420/lpl/PowerCycleP.nc
index 3409924..f86c7b5 100644
--- a/tos/chips/cc2420/lpl/PowerCycleP.nc
+++ b/tos/chips/cc2420/lpl/PowerCycleP.nc
@@ -75,7 +75,7 @@ module PowerCycleP {
 implementation {
   
   /** The current period of the duty cycle, equivalent of wakeup interval */
-  uint16_t sleepInterval = 0;
+  uint16_t sleepInterval = LPL_DEF_LOCAL_SLEEP;
   
   /** The number of times the CCA has been sampled in this wakeup period */
   uint16_t ccaChecks;
@@ -106,6 +106,7 @@ implementation {
    * @param sleepIntervalMs the sleep interval in [ms]
    */
   command void PowerCycle.setSleepInterval(uint16_t sleepIntervalMs) {
+    printf("set: sleepInterval %d\n", sleepIntervalMs);
     if (!sleepInterval && sleepIntervalMs) {
       // We were always on, now lets duty cycle
       post stopRadio();  // Might want to delay turning off the radio
diff --git a/tos/interfaces/LowPowerListening.nc b/tos/interfaces/LowPowerListening.nc
index b5225c4..60b49fc 100644
--- a/tos/interfaces/LowPowerListening.nc
+++ b/tos/interfaces/LowPowerListening.nc
@@ -35,18 +35,17 @@
  * @author Jonathan Hui
  * @author David Moss
  */
- 
+
 #include "message.h"
- 
-interface LowPowerListening {
 
+interface LowPowerListening {
   /**
-   * Set this this node's radio sleep interval, in milliseconds.
-   * Once every interval, the node will sleep and perform an Rx check 
-   * on the radio.  Setting the sleep interval to 0 will keep the radio
-   * always on.
+   * Set this this node's radio sleep interval, in milliseconds. After
+   * each interval, the node will wakeup and check for radio activity.
    *
-   * This is the equivalent of setting the local duty cycle rate.
+   * Note: The sleep interval can be set to 0 to indicate that the radio
+   * should stay on all the time but in order to get a startDone this
+   * should only be done when the duty-cycling is off (after a stopDone).
    *
    * @param sleepIntervalMs the length of this node's Rx check interval, in [ms]
    */
@@ -56,31 +55,7 @@ interface LowPowerListening {
    * @return the local node's sleep interval, in [ms]
    */
   command uint16_t getLocalSleepInterval();
-  
-  /**
-   * Set this node's radio duty cycle rate, in units of [percentage*100].
-   * For example, to get a 0.05% duty cycle,
-   * <code>
-   *   call LowPowerListening.setDutyCycle(5);
-   * </code>
-   *
-   * For a 100% duty cycle (always on),
-   * <code>
-   *   call LowPowerListening.setDutyCycle(10000);
-   * </code>
-   *
-   * This is the equivalent of setting the local sleep interval explicitly.
-   * 
-   * @param dutyCycle The duty cycle percentage, in units of [percentage*100]
-   */
-  command void setLocalDutyCycle(uint16_t dutyCycle);
-  
-  /**
-   * @return this node's radio duty cycle rate, in units of [percentage*100]
-   */
-  command uint16_t getLocalDutyCycle();
-  
-  
+
   /**
    * Configure this outgoing message so it can be transmitted to a neighbor mote
    * with the specified Rx sleep interval.
@@ -88,45 +63,10 @@ interface LowPowerListening {
    * @param sleepInterval The receiving node's sleep interval, in [ms]
    */
   command void setRxSleepInterval(message_t *msg, uint16_t sleepIntervalMs);
-  
+
   /**
    * @param 'message_t* ONE msg'
    * @return the destination node's sleep interval configured in this message
    */
   command uint16_t getRxSleepInterval(message_t *msg);
-  
-  /**
-   * Configure this outgoing message so it can be transmitted to a neighbor mote
-   * with the specified Rx duty cycle rate.
-   * Duty cycle is in units of [percentage*100], i.e. 0.25% duty cycle = 25.
-   * 
-   * @param 'message_t* ONE msg' Pointer to the message that will be sent
-   * @param dutyCycle The duty cycle of the receiving mote, in units of 
-   *     [percentage*100]
-   */
-  command void setRxDutyCycle(message_t *msg, uint16_t dutyCycle);
-  
-  /**
-   * @param 'message_t* ONE msg'
-   * @return the destination node's duty cycle configured in this message
-   *     in units of [percentage*100]
-   */
-  command uint16_t getRxDutyCycle(message_t *msg);
-  
-  /**
-   * Convert a duty cycle, in units of [percentage*100], to
-   * the sleep interval of the mote in milliseconds
-   * @param dutyCycle The duty cycle in units of [percentage*100]
-   * @return The equivalent sleep interval, in units of [ms]
-   */
-  command uint16_t dutyCycleToSleepInterval(uint16_t dutyCycle);
-  
-  /**
-   * Convert a sleep interval, in units of [ms], to a duty cycle
-   * in units of [percentage*100]
-   * @param sleepInterval The sleep interval in units of [ms]
-   * @return The duty cycle in units of [percentage*100]
-   */
-  command uint16_t sleepIntervalToDutyCycle(uint16_t sleepInterval);
-  
 }
diff --git a/tos/interfaces/LowPowerListeningControl.nc b/tos/interfaces/LowPowerListeningControl.nc
new file mode 100644
index 0000000..18b3e6d
--- /dev/null
+++ b/tos/interfaces/LowPowerListeningControl.nc
@@ -0,0 +1,42 @@
+#include "AM.h"
+
+/**
+ * Low Power Listening Control interface
+ */
+
+interface LowPowerListeningControl
+{
+  /**
+   * Start the duty-cycling of the radio. Once every sleep interval, the node
+   * will turn the radio on check for activity. Setting the sleep interval to 0
+   * (default value) will keep the radio always on.
+   *
+   * @param sleepIntervalMs the length of this node's Rx check interval, in [ms]
+   */
+  command void sleep(uint16_t sleepIntervalMs);
+
+  /**
+   * Resume the duty-cycling of the radio.
+   */
+  command void resumeSleep();
+
+  /**
+   * Terminate the duty-cycling and turn the radio on. The awake signal will
+   * indicate when the operation is completed.
+   */
+  command void wakeup();
+
+  /**
+   * The upper layers can use this to manage what to do when the LPL detects
+   * network activity. The default implementation for non-zero sleep intervals
+   * is to go to sleep immediately.
+   */
+  event void awake();
+
+  event void probeFrom(am_addr_t addr);
+
+  command void incUsers();
+  command void decUsers();
+  command bool isIdle();
+  command bool isAwake();
+}
diff --git a/tos/lib/net/ctp/CtpForwardingEngineP.nc b/tos/lib/net/ctp/CtpForwardingEngineP.nc
index 538fb64..ff77b95 100644
--- a/tos/lib/net/ctp/CtpForwardingEngineP.nc
+++ b/tos/lib/net/ctp/CtpForwardingEngineP.nc
@@ -416,6 +416,7 @@ implementation {
       dbg("Forwarder", "%s: no route, don't send, try again in %i.\n", __FUNCTION__, NO_ROUTE_RETRY);
       call RetxmitTimer.startOneShot(NO_ROUTE_RETRY);
       call CollectionDebug.logEvent(NET_C_FE_NO_ROUTE);
+      printf("ctp: no route\n");
       return;
     }
     else {
diff --git a/tos/lib/net/ctp/CtpRoutingEngineP.nc b/tos/lib/net/ctp/CtpRoutingEngineP.nc
index ec0d0ac..b00389c 100644
--- a/tos/lib/net/ctp/CtpRoutingEngineP.nc
+++ b/tos/lib/net/ctp/CtpRoutingEngineP.nc
@@ -344,6 +344,7 @@ implementation {
 
                 dbg("TreeRouting","Changed parent. from %d to %d\n", routeInfo.parent, best->neighbor);
                 call CollectionDebug.logEventDbg(NET_C_TREE_NEW_PARENT, best->neighbor, best->info.etx, minEtx);
+                printf("ctp: %u -> %u (%lu)\n", routeInfo.parent, best->neighbor, parentChanges);
                 call LinkEstimator.unpinNeighbor(routeInfo.parent);
                 call LinkEstimator.pinNeighbor(best->neighbor);
                 call LinkEstimator.clearDLQ(best->neighbor);
@@ -409,6 +410,7 @@ implementation {
                   beaconMsg->parent, 
                   beaconMsg->etx);
         call CollectionDebug.logEventRoute(NET_C_TREE_SENT_BEACON, beaconMsg->parent, 0, beaconMsg->etx);
+	printf("ctp: ping %u etx %u\n", beaconMsg->parent, beaconMsg->etx);
 
         eval = call BeaconSend.send(AM_BROADCAST_ADDR, 
                                     &beaconMsgBuffer, 
@@ -510,6 +512,7 @@ implementation {
     event void LinkEstimator.evicted(am_addr_t neighbor) {
         routingTableEvict(neighbor);
         dbg("TreeRouting","%s\n",__FUNCTION__);
+        printf("ctp: %u evicted\n", neighbor);
         if (routeInfo.parent == neighbor) {
             routeInfoInit(&routeInfo);
             justEvicted = TRUE;
diff --git a/tos/platforms/telosa/ActiveMessageC.nc b/tos/platforms/telosa/ActiveMessageC.nc
index 0e65149..aaf2bff 100644
--- a/tos/platforms/telosa/ActiveMessageC.nc
+++ b/tos/platforms/telosa/ActiveMessageC.nc
@@ -58,6 +58,7 @@ configuration ActiveMessageC {
     interface PacketAcknowledgements;
     interface PacketTimeStamp<T32khz, uint32_t> as PacketTimeStamp32khz;
     interface PacketTimeStamp<TMilli, uint32_t> as PacketTimeStampMilli;
+    interface LowPowerListening;
   }
 }
 implementation {
@@ -71,6 +72,7 @@ implementation {
   Packet       = AM;
   AMPacket     = AM;
   PacketAcknowledgements = AM;
+  LowPowerListening = AM;
 
   components CC2420PacketC;
   PacketTimeStamp32khz = CC2420PacketC;
diff --git a/tos/platforms/telosa/GlobalsC.nc b/tos/platforms/telosa/GlobalsC.nc
new file mode 100644
index 0000000..911ec22
--- /dev/null
+++ b/tos/platforms/telosa/GlobalsC.nc
@@ -0,0 +1,13 @@
+configuration GlobalsC { }
+
+implementation
+{
+  components GlobalsP;
+  components new CounterToLocalTimeC(T32khz);
+  components new TransformCounterC(T32khz, uint32_t, T32khz, uint16_t, 0, uint32_t) as Transform;
+  components Msp430Counter32khzC;
+
+  CounterToLocalTimeC.Counter -> Transform;
+  Transform.CounterFrom -> Msp430Counter32khzC;
+  GlobalsP.LocalTime -> CounterToLocalTimeC;
+}
diff --git a/tos/platforms/telosa/GlobalsP.nc b/tos/platforms/telosa/GlobalsP.nc
new file mode 100644
index 0000000..1512175
--- /dev/null
+++ b/tos/platforms/telosa/GlobalsP.nc
@@ -0,0 +1,11 @@
+#include "Globals.h"
+
+module GlobalsP
+{
+  uses interface LocalTime<T32khz> as LocalTime;
+}
+
+implementation
+{
+  uint32_t getLocalTime() @C() { return call LocalTime.get(); }
+}
diff --git a/tos/system/AMQueueImplP.nc b/tos/system/AMQueueImplP.nc
index a4c4eaf..9f79d93 100644
--- a/tos/system/AMQueueImplP.nc
+++ b/tos/system/AMQueueImplP.nc
@@ -35,8 +35,11 @@
 #include "AM.h"
 
 generic module AMQueueImplP(int numClients) @safe() {
-    provides interface Send[uint8_t client];
-    uses{
+    provides {
+        interface Send[uint8_t client];
+        command uint8_t getNumClients();
+    }
+    uses {
         interface AMSend[am_id_t id];
         interface AMPacket;
         interface Packet;
@@ -52,6 +55,8 @@ implementation {
     queue_entry_t queue[numClients];
     uint8_t cancelMask[numClients/8 + 1];
 
+    command uint8_t getNumClients() { return current == numClients ? 0 : current; }
+
     void tryToSend();
   
     void nextPacket() {
diff --git a/tos/system/AMQueueP.nc b/tos/system/AMQueueP.nc
index 415168e..2094057 100644
--- a/tos/system/AMQueueP.nc
+++ b/tos/system/AMQueueP.nc
@@ -32,7 +32,10 @@
 #include "AM.h"
 
 configuration AMQueueP {
-  provides interface Send[uint8_t client];
+  provides {
+    interface Send[uint8_t client];
+    command uint8_t getNumClients();
+  }
 }
 
 implementation {
@@ -43,6 +46,7 @@ implementation {
   components new AMQueueImplP(NUM_CLIENTS), ActiveMessageC;
 
   Send = AMQueueImplP;
+  getNumClients = AMQueueImplP;
   AMQueueImplP.AMSend -> ActiveMessageC;
   AMQueueImplP.AMPacket -> ActiveMessageC;
   AMQueueImplP.Packet -> ActiveMessageC;
diff --git a/tos/system/AMSenderC.nc b/tos/system/AMSenderC.nc
index eba1753..182151a 100644
--- a/tos/system/AMSenderC.nc
+++ b/tos/system/AMSenderC.nc
@@ -46,14 +46,15 @@ generic configuration AMSenderC(am_id_t AMId) {
 }
 
 implementation {
-  components new AMQueueEntryP(AMId) as AMQueueEntryP;
-  components AMQueueP, ActiveMessageC;
 
-  AMQueueEntryP.Send -> AMQueueP.Send[unique(UQ_AMQUEUE_SEND)];
-  AMQueueEntryP.AMPacket -> ActiveMessageC;
-  
-  AMSend = AMQueueEntryP;
-  Packet = ActiveMessageC;
-  AMPacket = ActiveMessageC;
-  Acks = ActiveMessageC;
+#if defined(LOW_POWER_LISTENING) || defined(LOW_POWER_PROBING)
+  components new LplAMSenderC(AMId) as SenderC;
+#else
+  components new DirectAMSenderC(AMId) as SenderC;
+#endif
+
+  AMSend = SenderC;
+  Packet = SenderC;
+  AMPacket = SenderC;
+  Acks = SenderC;
 }
diff --git a/tos/system/DirectAMSenderC.nc b/tos/system/DirectAMSenderC.nc
new file mode 100644
index 0000000..cb4fe07
--- /dev/null
+++ b/tos/system/DirectAMSenderC.nc
@@ -0,0 +1,59 @@
+// $Id$
+/*
+ * "Copyright (c) 2006 Stanford University. All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose, without fee, and without written
+ * agreement is hereby granted, provided that the above copyright
+ * notice, the following two paragraphs and the author appear in all
+ * copies of this software.
+ * 
+ * IN NO EVENT SHALL STANFORD UNIVERSITY BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF STANFORD UNIVERSITY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ * 
+ * STANFORD UNIVERSITY SPECIFICALLY DISCLAIMS ANY WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE
+ * PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND STANFORD UNIVERSITY
+ * HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES,
+ * ENHANCEMENTS, OR MODIFICATIONS."
+ */
+
+/**
+ * The virtualized active message send abstraction. Each instantiation
+ * of AMSenderC has its own queue of depth one. Therefore, it does not
+ * have to contend with other AMSenderC instantiations for queue space.
+ * The underlying implementation schedules the packets in these queues
+ * using some form of fair-share queueing.
+ *
+ * @author Philip Levis
+ * @date   Jan 16 2006
+ * @see    TEP 116: Packet Protocols
+ */ 
+
+#include "AM.h"
+
+generic configuration DirectAMSenderC(am_id_t AMId) {
+  provides {
+    interface AMSend;
+    interface Packet;
+    interface AMPacket;
+    interface PacketAcknowledgements as Acks;
+  }
+}
+
+implementation {
+  components new AMQueueEntryP(AMId) as AMQueueEntryP;
+  components AMQueueP, ActiveMessageC;
+
+  AMQueueEntryP.Send -> AMQueueP.Send[unique(UQ_AMQUEUE_SEND)];
+  AMQueueEntryP.AMPacket -> ActiveMessageC;
+  
+  AMSend = AMQueueEntryP;
+  Packet = ActiveMessageC;
+  AMPacket = ActiveMessageC;
+  Acks = ActiveMessageC;
+}
diff --git a/tos/system/LplAMSenderC.nc b/tos/system/LplAMSenderC.nc
new file mode 100644
index 0000000..52be108
--- /dev/null
+++ b/tos/system/LplAMSenderC.nc
@@ -0,0 +1,26 @@
+#include "AM.h"
+
+generic configuration LplAMSenderC(am_id_t AMId)
+{
+  provides {
+    interface AMSend;
+    interface Packet;
+    interface AMPacket;
+    interface PacketAcknowledgements as Acks;
+  }
+}
+
+implementation
+{
+  components new DirectAMSenderC(AMId);
+  components new LplAMSenderP();
+  components ActiveMessageC;
+
+  AMSend = LplAMSenderP;
+  Packet = DirectAMSenderC;
+  AMPacket = DirectAMSenderC;
+  Acks = DirectAMSenderC;
+
+  LplAMSenderP.SubAMSend -> DirectAMSenderC;
+  LplAMSenderP.Lpl -> ActiveMessageC;
+}
diff --git a/tos/system/LplAMSenderP.nc b/tos/system/LplAMSenderP.nc
new file mode 100644
index 0000000..d493241
--- /dev/null
+++ b/tos/system/LplAMSenderP.nc
@@ -0,0 +1,22 @@
+generic module LplAMSenderP()
+{
+  provides interface AMSend;
+  uses {
+    interface AMSend as SubAMSend;
+    interface LowPowerListening as Lpl;
+  }
+}
+
+implementation
+{
+  event void SubAMSend.sendDone(message_t* msg, error_t error)
+  {
+    call Lpl.setRxSleepInterval(msg, LPL_DEF_RX_SLEEP);
+    signal AMSend.sendDone(msg, error);
+  }
+
+  command error_t AMSend.send(am_addr_t addr, message_t* msg, uint8_t len) { return call SubAMSend.send(addr, msg, len); }
+  command error_t AMSend.cancel(message_t* msg) { return call SubAMSend.cancel(msg); }
+  command uint8_t AMSend.maxPayloadLength() { return call SubAMSend.maxPayloadLength(); }
+  command void* AMSend.getPayload(message_t* msg, uint8_t len) { return call SubAMSend.getPayload(msg, len); }
+}
diff --git a/tos/types/Globals.h b/tos/types/Globals.h
new file mode 100644
index 0000000..4bfc972
--- /dev/null
+++ b/tos/types/Globals.h
@@ -0,0 +1,25 @@
+#ifndef GLOBALS_H
+#define GLOBALS_H
+
+nx_struct Globals {
+  nx_uint32_t SentPackets;
+  nx_uint32_t RecvPackets;
+};
+
+  nx_struct Globals __g;
+
+  uint16_t getGlobalsSize() { return sizeof(__g); }
+  void *getGlobals() { return (void*)&__g; }
+  uint32_t getLocalTime();
+
+#define GLOBALS_GET(name) typeof(__g.name) get##name() { return __g.name; }
+#define GLOBALS_SET(name) void set##name(typeof(__g.name) val) { __g.name = val; }
+#define GLOBALS_INC(name) void inc##name() { __g.name++; }
+#define GLOBALS_ADD(name) void add##name(typeof(__g.name) val) { __g.name += val; }
+
+  GLOBALS_GET(SentPackets);
+  GLOBALS_INC(SentPackets);
+
+  GLOBALS_GET(RecvPackets);
+  GLOBALS_INC(RecvPackets);
+#endif


More information about the net2-wg mailing list