Updated Contiki X-MAC to use hardware acks, which allows for tighter timing and results in a lower duty cycle. Hardware acks also make X-MAC more robust against packet losses, as there is a direct way for the CSMA layer to detect loss of data packets. Before, Contiki X-MAC only detected loss of strobes.

This commit is contained in:
adamdunkels 2011-01-25 14:31:09 +00:00
parent d51e99a3dd
commit 09c223f549

View file

@ -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.59 2010/10/03 22:46:53 joxe Exp $ * $Id: xmac.c,v 1.60 2011/01/25 14:31:09 adamdunkels Exp $
*/ */
/** /**
@ -137,7 +137,7 @@ struct xmac_hdr {
cycle. */ cycle. */
#define ANNOUNCEMENT_TIME (random_rand() % (ANNOUNCEMENT_PERIOD)) #define ANNOUNCEMENT_TIME (random_rand() % (ANNOUNCEMENT_PERIOD))
#define DEFAULT_STROBE_WAIT_TIME (7 * DEFAULT_ON_TIME / 8) #define DEFAULT_STROBE_WAIT_TIME (5 * DEFAULT_ON_TIME / 8)
struct xmac_config xmac_config = { struct xmac_config xmac_config = {
DEFAULT_ON_TIME, DEFAULT_ON_TIME,
@ -415,6 +415,40 @@ register_encounter(const rimeaddr_t *neighbor, rtimer_clock_t time)
#endif /* WITH_ENCOUNTER_OPTIMIZATION */ #endif /* WITH_ENCOUNTER_OPTIMIZATION */
/*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/
static int static int
detect_ack(void)
{
#define INTER_PACKET_INTERVAL RTIMER_ARCH_SECOND / 5000
#define ACK_LEN 3
#define AFTER_ACK_DETECTECT_WAIT_TIME RTIMER_ARCH_SECOND / 1000
rtimer_clock_t wt;
uint8_t ack_received = 0;
wt = RTIMER_NOW();
leds_on(LEDS_GREEN);
while(RTIMER_CLOCK_LT(RTIMER_NOW(), wt + INTER_PACKET_INTERVAL)) { }
leds_off(LEDS_GREEN);
/* Check for incoming ACK. */
if((NETSTACK_RADIO.receiving_packet() ||
NETSTACK_RADIO.pending_packet() ||
NETSTACK_RADIO.channel_clear() == 0)) {
int len;
uint8_t ackbuf[ACK_LEN + 2];
wt = RTIMER_NOW();
while(RTIMER_CLOCK_LT(RTIMER_NOW(), wt + AFTER_ACK_DETECTECT_WAIT_TIME)) { }
len = NETSTACK_RADIO.read(ackbuf, ACK_LEN);
if(len == ACK_LEN) {
ack_received = 1;
}
}
if(ack_received) {
leds_toggle(LEDS_RED);
}
return ack_received;
}
/*---------------------------------------------------------------------------*/
static int
send_packet(void) send_packet(void)
{ {
rtimer_clock_t t0; rtimer_clock_t t0;
@ -422,7 +456,8 @@ send_packet(void)
rtimer_clock_t encounter_time = 0; rtimer_clock_t encounter_time = 0;
int strobes; int strobes;
struct xmac_hdr *hdr; struct xmac_hdr *hdr;
int got_strobe_ack = 0; uint8_t got_strobe_ack = 0;
uint8_t got_ack = 0;
uint8_t strobe[MAX_STROBE_SIZE]; uint8_t strobe[MAX_STROBE_SIZE];
int strobe_len, len; int strobe_len, len;
int is_broadcast = 0; int is_broadcast = 0;
@ -456,6 +491,8 @@ send_packet(void)
} }
is_reliable = packetbuf_attr(PACKETBUF_ATTR_RELIABLE) || is_reliable = packetbuf_attr(PACKETBUF_ATTR_RELIABLE) ||
packetbuf_attr(PACKETBUF_ATTR_ERELIABLE); packetbuf_attr(PACKETBUF_ATTR_ERELIABLE);
packetbuf_set_attr(PACKETBUF_ATTR_MAC_ACK, 1);
len = NETSTACK_FRAMER.create(); len = NETSTACK_FRAMER.create();
strobe_len = len + sizeof(struct xmac_hdr); strobe_len = len + sizeof(struct xmac_hdr);
if(len == 0 || strobe_len > sizeof(strobe)) { if(len == 0 || strobe_len > sizeof(strobe)) {
@ -518,6 +555,9 @@ send_packet(void)
now = RTIMER_NOW(); now = RTIMER_NOW();
wait = ((rtimer_clock_t)(e->time - now)) % (DEFAULT_PERIOD); wait = ((rtimer_clock_t)(e->time - now)) % (DEFAULT_PERIOD);
if(wait < 2 * DEFAULT_ON_TIME) {
wait = DEFAULT_PERIOD;
}
expected = now + wait - 2 * DEFAULT_ON_TIME; expected = now + wait - 2 * DEFAULT_ON_TIME;
#if WITH_ACK_OPTIMIZATION #if WITH_ACK_OPTIMIZATION
@ -549,7 +589,7 @@ send_packet(void)
/* Send a train of strobes until the receiver answers with an ACK. */ /* Send a train of strobes until the receiver answers with an ACK. */
/* Turn on the radio to listen for the strobe ACK. */ /* Turn on the radio to listen for the strobe ACK. */
on(); // on();
collisions = 0; collisions = 0;
if(!is_already_streaming) { if(!is_already_streaming) {
watchdog_stop(); watchdog_stop();
@ -563,6 +603,8 @@ send_packet(void)
while(got_strobe_ack == 0 && while(got_strobe_ack == 0 &&
RTIMER_CLOCK_LT(RTIMER_NOW(), t + xmac_config.strobe_wait_time)) { RTIMER_CLOCK_LT(RTIMER_NOW(), t + xmac_config.strobe_wait_time)) {
rtimer_clock_t now = RTIMER_NOW(); rtimer_clock_t now = RTIMER_NOW();
#if 0
/* See if we got an ACK */ /* See if we got an ACK */
packetbuf_clear(); packetbuf_clear();
len = NETSTACK_RADIO.read(packetbuf_dataptr(), PACKETBUF_SIZE); len = NETSTACK_RADIO.read(packetbuf_dataptr(), PACKETBUF_SIZE);
@ -588,6 +630,7 @@ send_packet(void)
PRINTF("xmac: send failed to parse %u\n", len); PRINTF("xmac: send failed to parse %u\n", len);
} }
} }
#endif /* 0 */
} }
t = RTIMER_NOW(); t = RTIMER_NOW();
@ -605,8 +648,9 @@ send_packet(void)
off(); off();
} else { } else {
rtimer_clock_t wt; rtimer_clock_t wt;
on();
NETSTACK_RADIO.send(strobe, strobe_len); NETSTACK_RADIO.send(strobe, strobe_len);
#if 1 #if 0
/* Turn off the radio for a while to let the other side /* Turn off the radio for a while to let the other side
respond. We don't need to keep our radio on when we know respond. We don't need to keep our radio on when we know
that the other side needs some time to produce a reply. */ that the other side needs some time to produce a reply. */
@ -614,7 +658,12 @@ send_packet(void)
wt = RTIMER_NOW(); wt = RTIMER_NOW();
while(RTIMER_CLOCK_LT(RTIMER_NOW(), wt + WAIT_TIME_BEFORE_STROBE_ACK)); while(RTIMER_CLOCK_LT(RTIMER_NOW(), wt + WAIT_TIME_BEFORE_STROBE_ACK));
#endif /* 0 */ #endif /* 0 */
on();
if(detect_ack()) {
got_strobe_ack = 1;
} else {
off();
}
} }
} }
} }
@ -633,8 +682,6 @@ send_packet(void)
} else { } else {
off(); off();
} }
#else /* WITH_ACK_OPTIMIZATION */
off();
#endif /* WITH_ACK_OPTIMIZATION */ #endif /* WITH_ACK_OPTIMIZATION */
/* restore the packet to send */ /* restore the packet to send */
@ -644,7 +691,14 @@ send_packet(void)
/* Send the data packet. */ /* Send the data packet. */
if((is_broadcast || got_strobe_ack || is_streaming) && collisions == 0) { if((is_broadcast || got_strobe_ack || is_streaming) && collisions == 0) {
NETSTACK_RADIO.send(packetbuf_hdrptr(), packetbuf_totlen()); NETSTACK_RADIO.send(packetbuf_hdrptr(), packetbuf_totlen());
if(!is_broadcast) {
if(detect_ack()) {
got_ack = 1;
} }
}
}
off();
#if WITH_ENCOUNTER_OPTIMIZATION #if WITH_ENCOUNTER_OPTIMIZATION
if(got_strobe_ack && !is_streaming) { if(got_strobe_ack && !is_streaming) {
@ -675,7 +729,7 @@ send_packet(void)
LEDS_OFF(LEDS_BLUE); LEDS_OFF(LEDS_BLUE);
if(collisions == 0) { if(collisions == 0) {
if(!is_broadcast && !got_strobe_ack) { if(is_broadcast == 0 && got_ack == 0) {
return MAC_TX_NOACK; return MAC_TX_NOACK;
} else { } else {
return MAC_TX_OK; return MAC_TX_OK;
@ -781,6 +835,8 @@ input_packet(void)
acknowledge the strobe and wait for the packet. By using acknowledge the strobe and wait for the packet. By using
the same address as both sender and receiver, we flag the the same address as both sender and receiver, we flag the
message is a strobe ack. */ message is a strobe ack. */
waiting_for_packet = 1;
#if 0
hdr->type = TYPE_STROBE_ACK; hdr->type = TYPE_STROBE_ACK;
packetbuf_set_addr(PACKETBUF_ADDR_RECEIVER, packetbuf_set_addr(PACKETBUF_ADDR_RECEIVER,
packetbuf_addr(PACKETBUF_ADDR_SENDER)); packetbuf_addr(PACKETBUF_ADDR_SENDER));
@ -797,6 +853,7 @@ input_packet(void)
} else { } else {
PRINTF("xmac: failed to send strobe ack\n"); PRINTF("xmac: failed to send strobe ack\n");
} }
#endif /* 0 */
} else if(rimeaddr_cmp(packetbuf_addr(PACKETBUF_ADDR_RECEIVER), } else if(rimeaddr_cmp(packetbuf_addr(PACKETBUF_ADDR_RECEIVER),
&rimeaddr_null)) { &rimeaddr_null)) {
/* If the receiver address is null, the strobe is sent to /* If the receiver address is null, the strobe is sent to