From 9d26bd663e329aa2ff181eccf9df3ce1e9e9bd53 Mon Sep 17 00:00:00 2001 From: adamdunkels Date: Sun, 18 Oct 2009 13:19:25 +0000 Subject: [PATCH] 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. --- core/net/mac/xmac.c | 147 ++++++++++++++++++++++++++++++++++++++------ 1 file changed, 129 insertions(+), 18 deletions(-) diff --git a/core/net/mac/xmac.c b/core/net/mac/xmac.c index fcc5d5e46..815dfe51d 100644 --- a/core/net/mac/xmac.c +++ b/core/net/mac/xmac.c @@ -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,