Improved phase lock behaviour when neighbor is not duty cycling: senders notify receivers via the 802.15.4 pending bit that they are not duty cycling. Neighbors then will start sending packets immediately and not wait for neighbors' phase. Tweaking of ContikiMAC timers to make it more reliable.

This commit is contained in:
adamdunkels 2010-04-03 13:28:30 +00:00
parent 3a286c4f93
commit 121ca946e1
4 changed files with 170 additions and 126 deletions

View file

@ -28,7 +28,7 @@
*
* This file is part of the Contiki operating system.
*
* $Id: phase.c,v 1.7 2010/03/31 20:27:15 adamdunkels Exp $
* $Id: phase.c,v 1.8 2010/04/03 13:28:30 adamdunkels Exp $
*/
/**
@ -54,10 +54,10 @@ struct phase_queueitem {
struct queuebuf *q;
};
#define PHASE_DEFER_THRESHOLD 2
#define PHASE_DEFER_THRESHOLD 1
#define PHASE_QUEUESIZE 8
#define MAX_NOACKS 3
#define MAX_NOACKS 2
MEMB(phase_memb, struct phase_queueitem, PHASE_QUEUESIZE);
@ -71,6 +71,30 @@ MEMB(phase_memb, struct phase_queueitem, PHASE_QUEUESIZE);
#define PRINTDEBUG(...)
#endif
/*---------------------------------------------------------------------------*/
struct phase *
find_neighbor(const struct phase_list *list, const rimeaddr_t *addr)
{
struct phase *e;
for(e = list_head(*list->list); e != NULL; e = e->next) {
if(rimeaddr_cmp(addr, &e->neighbor)) {
return e;
}
}
return NULL;
}
/*---------------------------------------------------------------------------*/
void
phase_remove(const struct phase_list *list, const rimeaddr_t *neighbor)
{
struct phase *e;
e = find_neighbor(list, neighbor);
if(e != NULL) {
list_remove(*list->list, e);
memb_free(&phase_memb, e);
}
}
/*---------------------------------------------------------------------------*/
void
phase_update(const struct phase_list *list,
const rimeaddr_t *neighbor, rtimer_clock_t time,
@ -79,46 +103,45 @@ phase_update(const struct phase_list *list,
struct phase *e;
/* If we have an entry for this neighbor already, we renew it. */
for(e = list_head(*list->list); e != NULL; e = e->next) {
if(rimeaddr_cmp(neighbor, &e->neighbor)) {
if(mac_status == MAC_TX_OK) {
e->time = time;
e = find_neighbor(list, neighbor);
if(e != NULL) {
if(mac_status == MAC_TX_OK) {
e->time = time;
}
/* If the neighbor didn't reply to us, it may have switched
phase (rebooted). We try a number of transmissions to it
before we drop it from the phase list. */
if(mac_status == MAC_TX_NOACK) {
PRINTF("phase noacks %d to %d.%d\n", e->noacks, neighbor->u8[0], neighbor->u8[1]);
e->noacks++;
if(e->noacks >= MAX_NOACKS) {
list_remove(*list->list, e);
memb_free(&phase_memb, e);
return;
}
/* If the neighbor didn't reply to us, it may have switched
phase (rebooted). We try a number of transmissions to it
before we drop it from the phase list. */
if(mac_status == MAC_TX_NOACK) {
PRINTF("phase noacks %d to %d.%d\n", e->noacks, neighbor->u8[0], neighbor->u8[1]);
e->noacks++;
if(e->noacks >= MAX_NOACKS) {
list_remove(*list->list, e);
memb_free(&phase_memb, e);
return;
}
} else if(mac_status == MAC_TX_OK) {
e->noacks = 0;
}
/* Make sure this entry is first on the list so subsequent
} else if(mac_status == MAC_TX_OK) {
e->noacks = 0;
}
/* Make sure this entry is first on the list so subsequent
searches are faster. */
list_remove(*list->list, e);
list_push(*list->list, e);
break;
}
}
/* No matching phase was found, so we allocate a new one. */
if(mac_status == MAC_TX_OK && e == NULL) {
e = memb_alloc(list->memb);
if(e == NULL) {
/* We could not allocate memory for this phase, so we drop
the last item on the list and reuse it for our phase. */
e = list_chop(*list->list);
}
rimeaddr_copy(&e->neighbor, neighbor);
e->time = time;
e->noacks = 0;
list_remove(*list->list, e);
list_push(*list->list, e);
} else {
/* No matching phase was found, so we allocate a new one. */
if(mac_status == MAC_TX_OK && e == NULL) {
e = memb_alloc(list->memb);
if(e == NULL) {
/* We could not allocate memory for this phase, so we drop
the last item on the list and reuse it for our phase. */
e = list_chop(*list->list);
}
rimeaddr_copy(&e->neighbor, neighbor);
e->time = time;
e->noacks = 0;
list_push(*list->list, e);
}
}
}
/*---------------------------------------------------------------------------*/
@ -140,68 +163,65 @@ phase_wait(struct phase_list *list,
mac_callback_t mac_callback, void *mac_callback_ptr)
{
struct phase *e;
// const rimeaddr_t *neighbor = packetbuf_addr(PACKETBUF_ADDR_RECEIVER);
/* We go through the list of phases to find if we have recorded a
phase for this particular neighbor. If so, we can compute the
time for the next expected phase and setup a ctimer to switch on
the radio just before the phase. */
for(e = list_head(*list->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, additional_wait;
clock_time_t ctimewait;
/* We expect phases to happen every CYCLE_TIME time
units. The next expected phase is at time e->time +
CYCLE_TIME. To compute a relative offset, we subtract
with clock_time(). Because we are only interested in turning
on the radio within the CYCLE_TIME period, we compute the
waiting time with modulo CYCLE_TIME. */
/* printf("neighbor phase 0x%02x (cycle 0x%02x)\n", e->time & (cycle_time - 1),
cycle_time);*/
additional_wait = 2 * e->noacks * wait_before;
/* if(e->noacks > 0) {
printf("additional wait %d\n", additional_wait);
}*/
now = RTIMER_NOW();
wait = (rtimer_clock_t)((e->time - now) &
(cycle_time - 1));
if(wait < wait_before + additional_wait) {
wait += cycle_time;
}
ctimewait = (CLOCK_SECOND * (wait - wait_before - additional_wait)) / RTIMER_ARCH_SECOND;
if(ctimewait > PHASE_DEFER_THRESHOLD) {
struct phase_queueitem *p;
p = memb_alloc(&phase_memb);
if(p != NULL) {
p->q = queuebuf_new_from_packetbuf();
if(p->q != NULL) {
p->mac_callback = mac_callback;
p->mac_callback_ptr = mac_callback_ptr;
ctimer_set(&p->timer, ctimewait, send_packet, p);
return PHASE_DEFERRED;
} else {
memb_free(&phase_memb, p);
}
}
}
expected = now + wait - wait_before - additional_wait;
if(!RTIMER_CLOCK_LT(expected, now)) {
/* Wait until the receiver is expected to be awake */
while(RTIMER_CLOCK_LT(RTIMER_NOW(), expected)) {
}
}
return PHASE_SEND_NOW;
e = find_neighbor(list, neighbor);
if(e != NULL) {
rtimer_clock_t wait, now, expected, additional_wait;
clock_time_t ctimewait;
/* We expect phases to happen every CYCLE_TIME time
units. The next expected phase is at time e->time +
CYCLE_TIME. To compute a relative offset, we subtract
with clock_time(). Because we are only interested in turning
on the radio within the CYCLE_TIME period, we compute the
waiting time with modulo CYCLE_TIME. */
/* printf("neighbor phase 0x%02x (cycle 0x%02x)\n", e->time & (cycle_time - 1),
cycle_time);*/
additional_wait = 2 * e->noacks * wait_before;
/* if(e->noacks > 0) {
printf("additional wait %d\n", additional_wait);
}*/
now = RTIMER_NOW();
wait = (rtimer_clock_t)((e->time - now) &
(cycle_time - 1));
if(wait < wait_before + additional_wait) {
wait += cycle_time;
}
ctimewait = (CLOCK_SECOND * (wait - wait_before - additional_wait)) / RTIMER_ARCH_SECOND;
if(ctimewait > PHASE_DEFER_THRESHOLD) {
struct phase_queueitem *p;
p = memb_alloc(&phase_memb);
if(p != NULL) {
p->q = queuebuf_new_from_packetbuf();
if(p->q != NULL) {
p->mac_callback = mac_callback;
p->mac_callback_ptr = mac_callback_ptr;
ctimer_set(&p->timer, ctimewait, send_packet, p);
return PHASE_DEFERRED;
} else {
memb_free(&phase_memb, p);
}
}
}
expected = now + wait - wait_before - additional_wait;
if(!RTIMER_CLOCK_LT(expected, now)) {
/* Wait until the receiver is expected to be awake */
while(RTIMER_CLOCK_LT(RTIMER_NOW(), expected)) {
}
}
return PHASE_SEND_NOW;
}
return PHASE_UNKNOWN;
}