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.
*
* $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. */
#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 = {
DEFAULT_ON_TIME,
@ -415,6 +415,40 @@ register_encounter(const rimeaddr_t *neighbor, rtimer_clock_t time)
#endif /* WITH_ENCOUNTER_OPTIMIZATION */
/*---------------------------------------------------------------------------*/
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)
{
rtimer_clock_t t0;
@ -422,7 +456,8 @@ send_packet(void)
rtimer_clock_t encounter_time = 0;
int strobes;
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];
int strobe_len, len;
int is_broadcast = 0;
@ -456,6 +491,8 @@ send_packet(void)
}
is_reliable = packetbuf_attr(PACKETBUF_ATTR_RELIABLE) ||
packetbuf_attr(PACKETBUF_ATTR_ERELIABLE);
packetbuf_set_attr(PACKETBUF_ATTR_MAC_ACK, 1);
len = NETSTACK_FRAMER.create();
strobe_len = len + sizeof(struct xmac_hdr);
if(len == 0 || strobe_len > sizeof(strobe)) {
@ -518,6 +555,9 @@ send_packet(void)
now = RTIMER_NOW();
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;
#if WITH_ACK_OPTIMIZATION
@ -549,7 +589,7 @@ send_packet(void)
/* Send a train of strobes until the receiver answers with an ACK. */
/* Turn on the radio to listen for the strobe ACK. */
on();
// on();
collisions = 0;
if(!is_already_streaming) {
watchdog_stop();
@ -563,6 +603,8 @@ send_packet(void)
while(got_strobe_ack == 0 &&
RTIMER_CLOCK_LT(RTIMER_NOW(), t + xmac_config.strobe_wait_time)) {
rtimer_clock_t now = RTIMER_NOW();
#if 0
/* See if we got an ACK */
packetbuf_clear();
len = NETSTACK_RADIO.read(packetbuf_dataptr(), PACKETBUF_SIZE);
@ -588,8 +630,9 @@ send_packet(void)
PRINTF("xmac: send failed to parse %u\n", len);
}
}
#endif /* 0 */
}
t = RTIMER_NOW();
/* Send the strobe packet. */
if(got_strobe_ack == 0 && collisions == 0) {
@ -602,11 +645,12 @@ send_packet(void)
queuebuf_to_packetbuf(packet);
NETSTACK_RADIO.send(packetbuf_hdrptr(), packetbuf_totlen());
#endif
off();
off();
} else {
rtimer_clock_t wt;
on();
NETSTACK_RADIO.send(strobe, strobe_len);
#if 1
#if 0
/* 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
that the other side needs some time to produce a reply. */
@ -614,8 +658,13 @@ send_packet(void)
wt = RTIMER_NOW();
while(RTIMER_CLOCK_LT(RTIMER_NOW(), wt + WAIT_TIME_BEFORE_STROBE_ACK));
#endif /* 0 */
on();
}
if(detect_ack()) {
got_strobe_ack = 1;
} else {
off();
}
}
}
}
}
@ -633,8 +682,6 @@ send_packet(void)
} else {
off();
}
#else /* WITH_ACK_OPTIMIZATION */
off();
#endif /* WITH_ACK_OPTIMIZATION */
/* restore the packet to send */
@ -644,7 +691,14 @@ send_packet(void)
/* Send the data packet. */
if((is_broadcast || got_strobe_ack || is_streaming) && collisions == 0) {
NETSTACK_RADIO.send(packetbuf_hdrptr(), packetbuf_totlen());
if(!is_broadcast) {
if(detect_ack()) {
got_ack = 1;
}
}
}
off();
#if WITH_ENCOUNTER_OPTIMIZATION
if(got_strobe_ack && !is_streaming) {
@ -675,7 +729,7 @@ send_packet(void)
LEDS_OFF(LEDS_BLUE);
if(collisions == 0) {
if(!is_broadcast && !got_strobe_ack) {
if(is_broadcast == 0 && got_ack == 0) {
return MAC_TX_NOACK;
} else {
return MAC_TX_OK;
@ -781,6 +835,8 @@ input_packet(void)
acknowledge the strobe and wait for the packet. By using
the same address as both sender and receiver, we flag the
message is a strobe ack. */
waiting_for_packet = 1;
#if 0
hdr->type = TYPE_STROBE_ACK;
packetbuf_set_addr(PACKETBUF_ADDR_RECEIVER,
packetbuf_addr(PACKETBUF_ADDR_SENDER));
@ -797,6 +853,7 @@ input_packet(void)
} else {
PRINTF("xmac: failed to send strobe ack\n");
}
#endif /* 0 */
} else if(rimeaddr_cmp(packetbuf_addr(PACKETBUF_ADDR_RECEIVER),
&rimeaddr_null)) {
/* If the receiver address is null, the strobe is sent to
@ -808,7 +865,7 @@ input_packet(void)
} else {
PRINTDEBUG("xmac: strobe not for us\n");
}
/* We are done processing the strobe and we therefore return
to the caller. */
return;