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:
adamdunkels 2009-10-18 13:19:25 +00:00
parent 63f927fc8d
commit 9d26bd663e

View File

@ -28,7 +28,7 @@
*
* 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_TIMESYNCH 0
#define WITH_QUEUE 0
#define WITH_ACK_OPTIMIZATION 0
#define WITH_ACK_OPTIMIZATION 1
#define WITH_RANDOM_WAIT_BEFORE_SEND 0
#define WITH_ENCOUNTER_OPTIMIZATION 1
struct announcement_data {
uint16_t id;
@ -102,6 +103,8 @@ struct xmac_hdr {
#define DEFAULT_OFF_TIME (RTIMER_ARCH_SECOND / 2 - DEFAULT_ON_TIME)
#endif
#define DEFAULT_PERIOD (DEFAULT_OFF_TIME + DEFAULT_ON_TIME)
/* The cycle time for announcements. */
#define ANNOUNCEMENT_PERIOD 4 * CLOCK_SECOND
@ -169,6 +172,23 @@ static void (* receiver_callback)(const struct mac_driver *);
static struct compower_activity current_packet;
#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
set_receive_function(void (* recv)(const struct mac_driver *))
@ -214,26 +234,26 @@ powercycle(struct rtimer *t, void *ptr)
}
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();
#if XMAC_CONF_COMPOWER
compower_accumulate(&compower_idle_activity);
#endif /* XMAC_CONF_COMPOWER */
}
} else {
waiting_for_packet++;
if(waiting_for_packet > 2) {
/* We should not be awake for more than two consecutive
power cycles without having heard a packet, so we turn off
the radio. */
waiting_for_packet = 0;
if(we_are_sending == 0) {
off();
}
#endif /* XMAC_CONF_COMPOWER */
} else {
waiting_for_packet++;
if(waiting_for_packet >= 2) {
/* We should not be awake for more than two consecutive
power cycles without having heard a packet, so we turn off
the radio. */
waiting_for_packet = 0;
if(we_are_sending == 0) {
off();
}
#if XMAC_CONF_COMPOWER
compower_accumulate(&compower_idle_activity);
compower_accumulate(&compower_idle_activity);
#endif /* XMAC_CONF_COMPOWER */
}
}
}
@ -342,11 +362,51 @@ format_announcement(char *hdr)
}
#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
send_packet(void)
{
rtimer_clock_t t0;
rtimer_clock_t t;
rtimer_clock_t encounter_time;
int strobes;
struct xmac_hdr hdr;
int got_strobe_ack = 0;
@ -356,6 +416,7 @@ send_packet(void)
} strobe;
int len;
int is_broadcast = 0;
struct encounter *e;
#if WITH_RANDOM_WAIT_BEFORE_SEND
{
@ -388,6 +449,45 @@ send_packet(void)
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
in-place in the packet buffer, because we cannot be sure of the
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
the packet. */
got_strobe_ack = 1;
encounter_time = RTIMER_NOW();
}
}
}
@ -479,9 +580,14 @@ send_packet(void)
/* Send the data packet. */
if(is_broadcast || got_strobe_ack) {
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();
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->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
announcement_register_listen_callback(listen_callback);
ctimer_set(&announcement_cycle_ctimer, ANNOUNCEMENT_TIME,