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:
parent
d51e99a3dd
commit
09c223f549
1 changed files with 70 additions and 13 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.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
|
||||||
|
|
Loading…
Reference in a new issue