A new optimization to X-MAC: senders now keep track of when neighbors
last were known to be awake. When sending a packet, a sender does not start to send strobes until the neighbor is expected to be awake. This reduces power consumption for senders and decreases the contention in the network as there are less packets in the air. Additionally, the ACK optimization was improved so that data/ack exchanges now are more efficient.
This commit is contained in:
parent
63f927fc8d
commit
9d26bd663e
1 changed files with 129 additions and 18 deletions
|
@ -28,7 +28,7 @@
|
||||||
*
|
*
|
||||||
* This file is part of the Contiki operating system.
|
* This file is part of the Contiki operating system.
|
||||||
*
|
*
|
||||||
* $Id: xmac.c,v 1.35 2009/08/20 18:59:17 adamdunkels Exp $
|
* $Id: xmac.c,v 1.36 2009/10/18 13:19:25 adamdunkels Exp $
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -57,8 +57,9 @@
|
||||||
#define WITH_CHANNEL_CHECK 0 /* Seems to work badly when enabled */
|
#define WITH_CHANNEL_CHECK 0 /* Seems to work badly when enabled */
|
||||||
#define WITH_TIMESYNCH 0
|
#define WITH_TIMESYNCH 0
|
||||||
#define WITH_QUEUE 0
|
#define WITH_QUEUE 0
|
||||||
#define WITH_ACK_OPTIMIZATION 0
|
#define WITH_ACK_OPTIMIZATION 1
|
||||||
#define WITH_RANDOM_WAIT_BEFORE_SEND 0
|
#define WITH_RANDOM_WAIT_BEFORE_SEND 0
|
||||||
|
#define WITH_ENCOUNTER_OPTIMIZATION 1
|
||||||
|
|
||||||
struct announcement_data {
|
struct announcement_data {
|
||||||
uint16_t id;
|
uint16_t id;
|
||||||
|
@ -102,6 +103,8 @@ struct xmac_hdr {
|
||||||
#define DEFAULT_OFF_TIME (RTIMER_ARCH_SECOND / 2 - DEFAULT_ON_TIME)
|
#define DEFAULT_OFF_TIME (RTIMER_ARCH_SECOND / 2 - DEFAULT_ON_TIME)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#define DEFAULT_PERIOD (DEFAULT_OFF_TIME + DEFAULT_ON_TIME)
|
||||||
|
|
||||||
/* The cycle time for announcements. */
|
/* The cycle time for announcements. */
|
||||||
#define ANNOUNCEMENT_PERIOD 4 * CLOCK_SECOND
|
#define ANNOUNCEMENT_PERIOD 4 * CLOCK_SECOND
|
||||||
|
|
||||||
|
@ -169,6 +172,23 @@ static void (* receiver_callback)(const struct mac_driver *);
|
||||||
static struct compower_activity current_packet;
|
static struct compower_activity current_packet;
|
||||||
#endif /* XMAC_CONF_COMPOWER */
|
#endif /* XMAC_CONF_COMPOWER */
|
||||||
|
|
||||||
|
#if WITH_ENCOUNTER_OPTIMIZATION
|
||||||
|
#define ENCOUNTER_LIFETIME (60 * CLOCK_SECOND)
|
||||||
|
|
||||||
|
#include "lib/list.h"
|
||||||
|
#include "lib/memb.h"
|
||||||
|
|
||||||
|
struct encounter {
|
||||||
|
struct encounter *next;
|
||||||
|
rimeaddr_t neighbor;
|
||||||
|
rtimer_clock_t time;
|
||||||
|
struct ctimer remove_timer;
|
||||||
|
};
|
||||||
|
|
||||||
|
#define MAX_ENCOUNTERS 4
|
||||||
|
LIST(encounter_list);
|
||||||
|
MEMB(encounter_memb, struct encounter, MAX_ENCOUNTERS);
|
||||||
|
#endif /* WITH_ENCOUNTER_OPTIMIZATION */
|
||||||
/*---------------------------------------------------------------------------*/
|
/*---------------------------------------------------------------------------*/
|
||||||
static void
|
static void
|
||||||
set_receive_function(void (* recv)(const struct mac_driver *))
|
set_receive_function(void (* recv)(const struct mac_driver *))
|
||||||
|
@ -214,16 +234,15 @@ powercycle(struct rtimer *t, void *ptr)
|
||||||
}
|
}
|
||||||
|
|
||||||
if(xmac_config.off_time > 0) {
|
if(xmac_config.off_time > 0) {
|
||||||
if(waiting_for_packet == 0) {
|
|
||||||
if(we_are_sending == 0) {
|
if(we_are_sending == 0) {
|
||||||
|
if(waiting_for_packet == 0) {
|
||||||
off();
|
off();
|
||||||
#if XMAC_CONF_COMPOWER
|
#if XMAC_CONF_COMPOWER
|
||||||
compower_accumulate(&compower_idle_activity);
|
compower_accumulate(&compower_idle_activity);
|
||||||
#endif /* XMAC_CONF_COMPOWER */
|
#endif /* XMAC_CONF_COMPOWER */
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
waiting_for_packet++;
|
waiting_for_packet++;
|
||||||
if(waiting_for_packet > 2) {
|
if(waiting_for_packet >= 2) {
|
||||||
/* We should not be awake for more than two consecutive
|
/* We should not be awake for more than two consecutive
|
||||||
power cycles without having heard a packet, so we turn off
|
power cycles without having heard a packet, so we turn off
|
||||||
the radio. */
|
the radio. */
|
||||||
|
@ -236,6 +255,7 @@ powercycle(struct rtimer *t, void *ptr)
|
||||||
#endif /* XMAC_CONF_COMPOWER */
|
#endif /* XMAC_CONF_COMPOWER */
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#if WITH_TIMESYNCH
|
#if WITH_TIMESYNCH
|
||||||
#define NUM_SLOTS 16
|
#define NUM_SLOTS 16
|
||||||
|
@ -342,11 +362,51 @@ format_announcement(char *hdr)
|
||||||
}
|
}
|
||||||
#endif /* XMAC_CONF_ANNOUNCEMENTS */
|
#endif /* XMAC_CONF_ANNOUNCEMENTS */
|
||||||
/*---------------------------------------------------------------------------*/
|
/*---------------------------------------------------------------------------*/
|
||||||
|
#if WITH_ENCOUNTER_OPTIMIZATION
|
||||||
|
static void
|
||||||
|
remove_encounter(void *encounter)
|
||||||
|
{
|
||||||
|
struct encounter *e = encounter;
|
||||||
|
|
||||||
|
ctimer_stop(&e->remove_timer);
|
||||||
|
list_remove(encounter_list, e);
|
||||||
|
memb_free(&encounter_memb, e);
|
||||||
|
}
|
||||||
|
/*---------------------------------------------------------------------------*/
|
||||||
|
static void
|
||||||
|
register_encounter(rimeaddr_t *neighbor, rtimer_clock_t time)
|
||||||
|
{
|
||||||
|
struct encounter *e;
|
||||||
|
|
||||||
|
/* If we have an entry for this neighbor already, we renew it. */
|
||||||
|
for(e = list_head(encounter_list); e != NULL; e = e->next) {
|
||||||
|
if(rimeaddr_cmp(neighbor, &e->neighbor)) {
|
||||||
|
e->time = time;
|
||||||
|
ctimer_set(&e->remove_timer, ENCOUNTER_LIFETIME, remove_encounter, e);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* No matchin encounter was found, so we allocate a new one. */
|
||||||
|
if(e == NULL) {
|
||||||
|
e = memb_alloc(&encounter_memb);
|
||||||
|
if(e == NULL) {
|
||||||
|
/* We could not allocate memory for this encounter, so we just drop it. */
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
rimeaddr_copy(&e->neighbor, neighbor);
|
||||||
|
e->time = time;
|
||||||
|
ctimer_set(&e->remove_timer, ENCOUNTER_LIFETIME, remove_encounter, e);
|
||||||
|
list_add(encounter_list, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif /* WITH_ENCOUNTER_OPTIMIZATION */
|
||||||
|
/*---------------------------------------------------------------------------*/
|
||||||
static int
|
static int
|
||||||
send_packet(void)
|
send_packet(void)
|
||||||
{
|
{
|
||||||
rtimer_clock_t t0;
|
rtimer_clock_t t0;
|
||||||
rtimer_clock_t t;
|
rtimer_clock_t t;
|
||||||
|
rtimer_clock_t encounter_time;
|
||||||
int strobes;
|
int strobes;
|
||||||
struct xmac_hdr hdr;
|
struct xmac_hdr hdr;
|
||||||
int got_strobe_ack = 0;
|
int got_strobe_ack = 0;
|
||||||
|
@ -356,6 +416,7 @@ send_packet(void)
|
||||||
} strobe;
|
} strobe;
|
||||||
int len;
|
int len;
|
||||||
int is_broadcast = 0;
|
int is_broadcast = 0;
|
||||||
|
struct encounter *e;
|
||||||
|
|
||||||
#if WITH_RANDOM_WAIT_BEFORE_SEND
|
#if WITH_RANDOM_WAIT_BEFORE_SEND
|
||||||
{
|
{
|
||||||
|
@ -388,6 +449,45 @@ send_packet(void)
|
||||||
|
|
||||||
off();
|
off();
|
||||||
|
|
||||||
|
#if WITH_ENCOUNTER_OPTIMIZATION
|
||||||
|
/* We go through the list of encounters to find if we have recorded
|
||||||
|
an encounter with this particular neighbor. If so, we can compute
|
||||||
|
the time for the next expected encounter and setup a ctimer to
|
||||||
|
switch on the radio just before the encounter. */
|
||||||
|
for(e = list_head(encounter_list); e != NULL; e = e->next) {
|
||||||
|
const rimeaddr_t *neighbor = packetbuf_addr(PACKETBUF_ADDR_RECEIVER);
|
||||||
|
|
||||||
|
if(rimeaddr_cmp(neighbor, &e->neighbor)) {
|
||||||
|
rtimer_clock_t wait, now, expected;
|
||||||
|
|
||||||
|
/* We expect encounters to happen every DEFAULT_PERIOD time
|
||||||
|
units. The next expected encounter is at time e->time +
|
||||||
|
DEFAULT_PERIOD. To compute a relative offset, we subtract
|
||||||
|
with clock_time(). Because we are only interested in turning
|
||||||
|
on the radio within the DEFAULT_PERIOD period, we compute the
|
||||||
|
waiting time with modulo DEFAULT_PERIOD. */
|
||||||
|
|
||||||
|
now = RTIMER_NOW();
|
||||||
|
wait = ((rtimer_clock_t)(e->time - now)) % (DEFAULT_PERIOD);
|
||||||
|
expected = now + wait - DEFAULT_ON_TIME * 2;
|
||||||
|
|
||||||
|
#if WITH_ACK_OPTIMIZATION
|
||||||
|
/* Wait until the receiver is expected to be awake */
|
||||||
|
if(packetbuf_attr(PACKETBUF_ATTR_PACKET_TYPE) !=
|
||||||
|
PACKETBUF_ATTR_PACKET_TYPE_ACK) {
|
||||||
|
/* Do not wait if we are sending an ACK, because then the
|
||||||
|
receiver will already be awake. */
|
||||||
|
while(RTIMER_CLOCK_LT(RTIMER_NOW(), expected));
|
||||||
|
}
|
||||||
|
|
||||||
|
#else /* WITH_ACK_OPTIMIZATION */
|
||||||
|
/* Wait until the receiver is expected to be awake */
|
||||||
|
while(RTIMER_CLOCK_LT(RTIMER_NOW(), expected));
|
||||||
|
#endif /* WITH_ACK_OPTIMIZATION */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif /* WITH_ENCOUNTER_OPTIMIZATION */
|
||||||
|
|
||||||
/* Create the X-MAC header for the data packet. We cannot do this
|
/* Create the X-MAC header for the data packet. We cannot do this
|
||||||
in-place in the packet buffer, because we cannot be sure of the
|
in-place in the packet buffer, because we cannot be sure of the
|
||||||
alignment of the header in the packet buffer. */
|
alignment of the header in the packet buffer. */
|
||||||
|
@ -443,6 +543,7 @@ send_packet(void)
|
||||||
/* We got an ACK from the receiver, so we can immediately send
|
/* We got an ACK from the receiver, so we can immediately send
|
||||||
the packet. */
|
the packet. */
|
||||||
got_strobe_ack = 1;
|
got_strobe_ack = 1;
|
||||||
|
encounter_time = RTIMER_NOW();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -479,9 +580,14 @@ send_packet(void)
|
||||||
|
|
||||||
/* Send the data packet. */
|
/* Send the data packet. */
|
||||||
if(is_broadcast || got_strobe_ack) {
|
if(is_broadcast || got_strobe_ack) {
|
||||||
|
|
||||||
radio->send(packetbuf_hdrptr(), packetbuf_totlen());
|
radio->send(packetbuf_hdrptr(), packetbuf_totlen());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if WITH_ENCOUNTER_OPTIMIZATION
|
||||||
|
if(got_strobe_ack) {
|
||||||
|
register_encounter(&hdr.receiver, encounter_time);
|
||||||
|
}
|
||||||
|
#endif /* WITH_ENCOUNTER_OPTIMIZATION */
|
||||||
watchdog_start();
|
watchdog_start();
|
||||||
|
|
||||||
PRINTF("xmac: send (strobes=%u,len=%u,%s), done\n", strobes,
|
PRINTF("xmac: send (strobes=%u,len=%u,%s), done\n", strobes,
|
||||||
|
@ -720,6 +826,11 @@ xmac_init(const struct radio_driver *d)
|
||||||
radio = d;
|
radio = d;
|
||||||
radio->set_receive_function(input_packet);
|
radio->set_receive_function(input_packet);
|
||||||
|
|
||||||
|
#if WITH_ENCOUNTER_OPTIMIZATION
|
||||||
|
list_init(encounter_list);
|
||||||
|
memb_init(&encounter_memb);
|
||||||
|
#endif /* WITH_ENCOUNTER_OPTIMIZATION */
|
||||||
|
|
||||||
#if XMAC_CONF_ANNOUNCEMENTS
|
#if XMAC_CONF_ANNOUNCEMENTS
|
||||||
announcement_register_listen_callback(listen_callback);
|
announcement_register_listen_callback(listen_callback);
|
||||||
ctimer_set(&announcement_cycle_ctimer, ANNOUNCEMENT_TIME,
|
ctimer_set(&announcement_cycle_ctimer, ANNOUNCEMENT_TIME,
|
||||||
|
|
Loading…
Reference in a new issue