2013-03-03 17:47:11 +01:00
|
|
|
/*
|
|
|
|
* etx_beaconing.c
|
|
|
|
*
|
|
|
|
* Created on: Feb 26, 2013
|
|
|
|
* Author: stephan
|
|
|
|
*/
|
|
|
|
#include "etx_beaconing.h"
|
|
|
|
|
|
|
|
#include <string.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
|
|
|
|
#include <hwtimer.h>
|
2013-03-15 17:48:13 +01:00
|
|
|
#include <vtimer.h>
|
2013-03-03 17:47:11 +01:00
|
|
|
#include <thread.h>
|
|
|
|
#include <transceiver.h>
|
|
|
|
|
2013-03-15 17:48:13 +01:00
|
|
|
#include "sys/net/sixlowpan/sixlowmac.h"
|
|
|
|
#include "sys/net/sixlowpan/ieee802154_frame.h"
|
|
|
|
#include "rpl_structs.h"
|
|
|
|
#include "rpl_dodag.h"
|
|
|
|
|
|
|
|
int etx_beacon_pid = 0;
|
|
|
|
int etx_radio_pid = 0;
|
|
|
|
int etx_update_pid = 0;
|
2013-03-03 17:47:11 +01:00
|
|
|
|
|
|
|
//Buffer
|
2013-03-15 17:48:13 +01:00
|
|
|
char etx_beacon_buf[ETX_BEACON_STACKSIZE] = { 0 };
|
|
|
|
char etx_radio_buf[ETX_RADIO_STACKSIZE] = { 0 };
|
|
|
|
char etx_update_buf[ETX_UPDT_STACKSIZE] = { 0 };
|
|
|
|
|
|
|
|
uint8_t etx_send_buf[ETX_BUF_SIZE] = { 0 };
|
|
|
|
uint8_t etx_rec_buf[ETX_BUF_SIZE] = { 0 };
|
2013-03-03 17:47:11 +01:00
|
|
|
|
2013-03-15 17:48:13 +01:00
|
|
|
/*
|
|
|
|
* The counter for the current 'round'. An ETX beacon is sent every ETX_INTERVAL
|
|
|
|
* u-seconds and a node computes the ETX value by comparing the the received
|
|
|
|
* probes vs the expected probes from a neighbor every ETX_ROUND intervals.
|
|
|
|
*/
|
|
|
|
static uint8_t rounds = 0;
|
2013-03-03 17:47:11 +01:00
|
|
|
|
|
|
|
//Message queue for radio
|
2013-03-24 15:57:52 +01:00
|
|
|
msg_t msg_que[ETX_RCV_QUEUE_SIZE] = { 0 };
|
2013-03-15 17:48:13 +01:00
|
|
|
|
|
|
|
/*
|
|
|
|
* This could (and should) be done differently, once the RPL implementation
|
|
|
|
* deals with candidate neighbors in another way than just defining that every
|
|
|
|
* possible neighbor we hear from is a parent.
|
|
|
|
* Right now, we need to keep track of the ETX values of other nodes without
|
|
|
|
* needing them to be in our parent array, so we have another array here in
|
|
|
|
* which we put all necessary info for up to RPL_MAX_CANDIDATE_NEIGHBORS
|
|
|
|
* candidates.
|
|
|
|
*/
|
|
|
|
//Candidate array for candidate<==>parent matching
|
|
|
|
//Throws a warning, but this is the correct way to initialize!
|
|
|
|
static rpl_candidate_neighbor_t candidates[RPL_MAX_CANDIDATE_NEIGHBORS] = { 0 };
|
2013-03-03 17:47:11 +01:00
|
|
|
|
|
|
|
//Transceiver command for sending ETX probes
|
|
|
|
transceiver_command_t tcmd;
|
|
|
|
|
|
|
|
//Message to send probes with
|
|
|
|
msg_t mesg;
|
|
|
|
|
2013-03-15 17:48:13 +01:00
|
|
|
//RPL-address
|
|
|
|
static ipv6_addr_t * own_address;
|
|
|
|
|
2013-03-03 17:47:11 +01:00
|
|
|
static etx_probe_t * get_etx_send_buf(void) {
|
|
|
|
return ((etx_probe_t *) &(etx_send_buf[0]));
|
|
|
|
}
|
|
|
|
static etx_probe_t * get_etx_rec_buf(void) {
|
|
|
|
return ((etx_probe_t *) &(etx_rec_buf[0]));
|
|
|
|
}
|
|
|
|
|
2013-03-15 17:48:13 +01:00
|
|
|
void show_candidates(void) {
|
|
|
|
rpl_candidate_neighbor_t * candidate;
|
|
|
|
rpl_candidate_neighbor_t *end;
|
|
|
|
|
|
|
|
for (candidate = &candidates[0], end = candidates
|
|
|
|
+ RPL_MAX_CANDIDATE_NEIGHBORS; candidate < end;
|
|
|
|
candidate++) {
|
|
|
|
printf("Candidates Addr:%d\n"
|
|
|
|
"\t cur_etx:%f\n"
|
|
|
|
"\t packets_rx:%d\n"
|
2013-03-19 14:04:23 +01:00
|
|
|
"\t used:%d\n", candidate->addr.uint8[ETX_IPV6_LAST_BYTE],
|
2013-03-15 17:48:13 +01:00
|
|
|
candidate->cur_etx, candidate->packets_rx,
|
|
|
|
candidate->used);
|
|
|
|
}
|
|
|
|
}
|
2013-03-03 17:47:11 +01:00
|
|
|
|
2013-03-15 17:48:13 +01:00
|
|
|
void etx_init_beaconing(ipv6_addr_t * address) {
|
|
|
|
own_address = address;
|
2013-03-18 19:22:07 +01:00
|
|
|
//set code
|
2013-03-19 14:07:31 +01:00
|
|
|
puts("ETX BEACON INIT");
|
2013-03-03 17:47:11 +01:00
|
|
|
etx_send_buf[0] = ETX_BEACON;
|
|
|
|
|
2013-03-15 17:48:13 +01:00
|
|
|
thread_print_all();
|
2013-03-18 19:22:07 +01:00
|
|
|
etx_beacon_pid = thread_create(etx_beacon_buf, ETX_BEACON_STACKSIZE,
|
|
|
|
PRIORITY_MAIN - 1, CREATE_STACKTEST,
|
|
|
|
etx_beacon, "etx_beacon");
|
2013-03-03 17:47:11 +01:00
|
|
|
|
2013-03-15 17:48:13 +01:00
|
|
|
etx_radio_pid = thread_create(etx_radio_buf, ETX_RADIO_STACKSIZE,
|
2013-03-03 17:47:11 +01:00
|
|
|
PRIORITY_MAIN - 1, CREATE_STACKTEST,
|
|
|
|
etx_radio, "etx_radio");
|
|
|
|
|
2013-03-15 17:48:13 +01:00
|
|
|
/*
|
|
|
|
* Maybe this should not be in a seperate thread resource-wise, but the
|
|
|
|
* motive was to delay beacon-sending as little as possible, as this would
|
|
|
|
* derail beaconing from the intended ETX_INTERVAL.
|
|
|
|
*/
|
|
|
|
|
|
|
|
etx_update_pid = thread_create(etx_update_buf, ETX_UPDT_STACKSIZE,
|
|
|
|
PRIORITY_MAIN - 1, CREATE_STACKTEST,
|
|
|
|
etx_update, "etx_update");
|
|
|
|
|
2013-03-19 14:07:31 +01:00
|
|
|
//register at transceiver
|
2013-03-03 17:47:11 +01:00
|
|
|
transceiver_register(TRANSCEIVER_CC1100, etx_radio_pid);
|
2013-03-19 14:07:31 +01:00
|
|
|
puts("...[DONE]");
|
2013-03-15 17:48:13 +01:00
|
|
|
}
|
|
|
|
|
2013-03-03 17:47:11 +01:00
|
|
|
void etx_beacon(void) {
|
|
|
|
/*
|
2013-03-22 17:40:36 +01:00
|
|
|
* Sends a message every ETX_INTERVAL +/- a jitter-value (default is 10%) .
|
2013-03-15 17:48:13 +01:00
|
|
|
* A correcting variable is needed to stay at a base interval of
|
|
|
|
* ETX_INTERVAL between the wakeups. It takes the old jittervalue in account
|
|
|
|
* and modifies the time to wait accordingly.
|
2013-03-03 17:47:11 +01:00
|
|
|
*/
|
2013-03-15 17:48:13 +01:00
|
|
|
etx_probe_t *etx_p = get_etx_send_buf();
|
2013-03-22 17:40:36 +01:00
|
|
|
|
|
|
|
/*
|
|
|
|
* The jittercorrection and jitter variables keep usecond values divided
|
|
|
|
* through 1000 to fit into uint8 variables.
|
|
|
|
*
|
|
|
|
* That is why they are multiplied by 1000 when used for hwtimer_wait.
|
|
|
|
*/
|
2013-03-19 14:04:23 +01:00
|
|
|
uint8_t jittercorrection = ETX_DEF_JIT_CORRECT;
|
|
|
|
uint8_t jitter = (uint8_t) (rand() % ETX_JITTER_MOD);
|
2013-03-03 17:47:11 +01:00
|
|
|
|
2013-03-15 17:48:13 +01:00
|
|
|
uint8_t p_length = 0;
|
2013-03-03 17:47:11 +01:00
|
|
|
|
2013-03-15 17:48:13 +01:00
|
|
|
ieee_802154_long_t empty_addr = { 0 };
|
2013-03-03 17:47:11 +01:00
|
|
|
|
2013-03-15 17:48:13 +01:00
|
|
|
while (true) {
|
2013-03-03 17:47:11 +01:00
|
|
|
|
2013-03-15 17:48:13 +01:00
|
|
|
if (rounds == ETX_ROUNDS) {
|
|
|
|
//calculate the ETX values and update the parents
|
|
|
|
thread_wakeup(etx_update_pid);
|
|
|
|
rounds = 1;
|
|
|
|
} else {
|
|
|
|
rounds++;
|
|
|
|
}
|
|
|
|
|
|
|
|
//Build etx packet
|
|
|
|
p_length = 0;
|
|
|
|
for (uint8_t i = 0; i < RPL_MAX_CANDIDATE_NEIGHBORS; i++) {
|
|
|
|
if (candidates[i].used != 0) {
|
2013-03-22 17:40:36 +01:00
|
|
|
etx_p->data[i * ETX_TUPLE_SIZE] =
|
|
|
|
candidates[i].addr.uint8[ETX_IPV6_LAST_BYTE];
|
|
|
|
etx_p->data[i * ETX_TUPLE_SIZE + ETX_PKT_REC_OFFSET] =
|
|
|
|
candidates[i].packets_rx;
|
2013-03-19 14:04:23 +01:00
|
|
|
p_length = p_length + ETX_PKT_HDR_LEN;
|
2013-03-15 17:48:13 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
etx_p->length = p_length;
|
2013-03-03 17:47:11 +01:00
|
|
|
|
2013-03-15 17:48:13 +01:00
|
|
|
send_ieee802154_frame(&empty_addr, &etx_send_buf[0],
|
2013-03-19 14:04:23 +01:00
|
|
|
get_etx_send_buf()->length + ETX_PKT_HDR_LEN, 1);
|
2013-03-15 17:48:13 +01:00
|
|
|
puts("sent beacon!");
|
2013-03-22 18:37:00 +01:00
|
|
|
//vtimer_usleep(((ETX_INTERVAL - ETX_MAX_JITTER)*MS) + jittercorrection*MS + jitter*MS);
|
2013-03-15 17:48:13 +01:00
|
|
|
/// TODO once vtimer works as intended, replace the hwtimer here with
|
|
|
|
/// the vtimer. Right now vtimer bugs, so we have hwtimer here.
|
2013-03-22 18:37:00 +01:00
|
|
|
hwtimer_wait(HWTIMER_TICKS(((ETX_INTERVAL - ETX_MAX_JITTER)*MS) + jittercorrection*MS + jitter*MS));
|
2013-03-03 17:47:11 +01:00
|
|
|
|
2013-03-22 18:37:00 +01:00
|
|
|
jittercorrection = (ETX_MAX_JITTER) - jitter;
|
2013-03-03 17:47:11 +01:00
|
|
|
|
2013-03-22 17:40:36 +01:00
|
|
|
//the jitter is a value between 0 and ETX_MAX_JITTER
|
2013-03-19 14:04:23 +01:00
|
|
|
jitter = (uint8_t) (rand() % ETX_JITTER_MOD);
|
2013-03-15 17:48:13 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
rpl_candidate_neighbor_t * etx_find_candidate(ipv6_addr_t * address) {
|
|
|
|
/*
|
|
|
|
* find the candidate with address address and returns it, or returns NULL
|
|
|
|
* if no candidate having this address was found.
|
|
|
|
*/
|
2013-03-22 17:40:36 +01:00
|
|
|
for (uint8_t i = 0; i < RPL_MAX_CANDIDATE_NEIGHBORS; i++) {
|
|
|
|
if (candidates[i].used
|
|
|
|
&& (rpl_equal_id(&candidates[i].addr, address))) {
|
|
|
|
return &candidates[i];
|
|
|
|
}
|
|
|
|
}
|
2013-03-15 17:48:13 +01:00
|
|
|
return NULL ;
|
|
|
|
}
|
2013-03-03 17:47:11 +01:00
|
|
|
|
2013-03-15 17:48:13 +01:00
|
|
|
double etx_get_metric(ipv6_addr_t * address) {
|
|
|
|
rpl_candidate_neighbor_t * candidate = etx_find_candidate(address);
|
|
|
|
if (candidate != NULL ) {
|
|
|
|
return candidate->packets_rx / (double) ETX_ROUNDS;
|
2013-03-03 17:47:11 +01:00
|
|
|
}
|
2013-03-15 17:48:13 +01:00
|
|
|
return 0;
|
2013-03-03 17:47:11 +01:00
|
|
|
}
|
|
|
|
|
2013-03-22 17:40:36 +01:00
|
|
|
uint8_t etx_add_candidate(ipv6_addr_t * address, uint8_t * pkt_rcvd) {
|
|
|
|
/*
|
|
|
|
* Pre-Condition: Add etx_add_candidate should only be called when the
|
|
|
|
* candidate is not yet in the list.
|
|
|
|
* Otherwise the candidate will be added a second time,
|
|
|
|
* leading to unknown behavior.
|
|
|
|
*
|
|
|
|
* Check if there is still enough space to add this candidate
|
|
|
|
*
|
|
|
|
* a)
|
|
|
|
* Space is available:
|
|
|
|
* Add candidate
|
|
|
|
*
|
|
|
|
* b)
|
|
|
|
* Space is not available:
|
|
|
|
* ignore new candidate
|
|
|
|
* This shouldn't really happen though, since we have enough
|
|
|
|
* place in the array.
|
|
|
|
*
|
|
|
|
* Returns if the candidate was added (1) or not (0).
|
|
|
|
*/
|
|
|
|
rpl_candidate_neighbor_t * candidate;
|
|
|
|
rpl_candidate_neighbor_t * end;
|
|
|
|
uint8_t added = 0;
|
|
|
|
|
|
|
|
for (candidate = &candidates[0], end = candidates
|
|
|
|
+ RPL_MAX_CANDIDATE_NEIGHBORS; candidate < end;
|
|
|
|
candidate++) {
|
|
|
|
if (candidate->used) {
|
|
|
|
//skip
|
|
|
|
continue;
|
|
|
|
} else {
|
|
|
|
//We still have a free place add the new candidate
|
2013-03-24 15:57:52 +01:00
|
|
|
candidate->addr = * address;
|
2013-03-22 17:40:36 +01:00
|
|
|
candidate->cur_etx = 0;
|
|
|
|
candidate->packets_rx = *pkt_rcvd;
|
|
|
|
candidate->used = 1;
|
|
|
|
|
|
|
|
added = 1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return added;
|
|
|
|
}
|
|
|
|
|
2013-03-15 17:48:13 +01:00
|
|
|
void etx_handle_beacon(ipv6_addr_t * candidate_address) {
|
2013-03-03 17:47:11 +01:00
|
|
|
/*
|
|
|
|
* Handle the ETX probe that has been received and update all infos.
|
2013-03-22 17:40:36 +01:00
|
|
|
* If the candidate address is unknown, try to add it to my struct.
|
2013-03-03 17:47:11 +01:00
|
|
|
*/
|
|
|
|
|
|
|
|
etx_probe_t * probe = get_etx_rec_buf();
|
|
|
|
|
2013-03-18 19:22:07 +01:00
|
|
|
//Todo delete once everything works well
|
2013-03-03 17:47:11 +01:00
|
|
|
printf("ETX beacon package received with following values:\n"
|
|
|
|
"\tPackage Option:%x\n"
|
2013-03-15 17:48:13 +01:00
|
|
|
"\t Data Length:%u\n"
|
|
|
|
"\tSource Address:%d\n\n", probe->code, probe->length,
|
2013-03-19 14:04:23 +01:00
|
|
|
candidate_address->uint8[ETX_IPV6_LAST_BYTE]);
|
2013-03-15 17:48:13 +01:00
|
|
|
|
|
|
|
rpl_candidate_neighbor_t* candidate = NULL;
|
|
|
|
|
2013-03-19 14:04:23 +01:00
|
|
|
for (uint8_t i = 0; i < probe->length / ETX_TUPLE_SIZE; i++) {
|
2013-03-15 17:48:13 +01:00
|
|
|
//todo delete once everything works well
|
|
|
|
printf("\tIPv6 short Addr:%u\n"
|
2013-03-19 14:04:23 +01:00
|
|
|
"\tPackets f. Addr:%u\n\n", probe->data[i * ETX_TUPLE_SIZE],
|
|
|
|
probe->data[i * ETX_TUPLE_SIZE + ETX_PKT_REC_OFFSET]);
|
2013-03-15 17:48:13 +01:00
|
|
|
|
|
|
|
// If i find my address in this probe, update the packet_rx value for
|
|
|
|
// this candidate, if he is in my candidate list.
|
2013-03-22 17:40:36 +01:00
|
|
|
if (probe->data[i * ETX_TUPLE_SIZE]
|
|
|
|
== own_address->uint8[ETX_IPV6_LAST_BYTE]) {
|
|
|
|
candidate = etx_find_candidate(candidate_address);
|
2013-03-15 17:48:13 +01:00
|
|
|
if (candidate != NULL ) {
|
2013-03-22 17:40:36 +01:00
|
|
|
candidate->packets_rx = probe->data[i * ETX_TUPLE_SIZE
|
|
|
|
+ ETX_PKT_REC_OFFSET];
|
|
|
|
} else {
|
|
|
|
//Candidate was not found in my list, maybe I should add it
|
|
|
|
uint8_t addresult = 0;
|
|
|
|
addresult = etx_add_candidate(candidate_address,
|
2013-03-24 15:57:52 +01:00
|
|
|
&(probe->data[i * ETX_TUPLE_SIZE + ETX_PKT_REC_OFFSET]));
|
2013-03-22 17:40:36 +01:00
|
|
|
|
|
|
|
if(addresult == 1){
|
|
|
|
puts("[WARNING] Candidate could not get added");
|
|
|
|
}
|
2013-03-15 17:48:13 +01:00
|
|
|
}
|
|
|
|
}
|
2013-03-03 17:47:11 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
void etx_radio(void) {
|
|
|
|
msg_t m;
|
|
|
|
radio_packet_t *p;
|
2013-03-15 17:48:13 +01:00
|
|
|
|
|
|
|
ieee802154_frame_t frame;
|
|
|
|
|
2013-03-24 15:57:52 +01:00
|
|
|
msg_init_queue(msg_que, ETX_RCV_QUEUE_SIZE);
|
2013-03-15 17:48:13 +01:00
|
|
|
|
2013-03-18 19:22:07 +01:00
|
|
|
ipv6_addr_t ll_address;
|
|
|
|
ipv6_addr_t candidate_addr;
|
2013-03-15 17:48:13 +01:00
|
|
|
|
2013-03-18 19:22:07 +01:00
|
|
|
ipv6_set_ll_prefix(&ll_address);
|
|
|
|
ipv6_get_saddr(&candidate_addr, &ll_address);
|
2013-03-03 17:47:11 +01:00
|
|
|
|
|
|
|
while (1) {
|
|
|
|
msg_receive(&m);
|
|
|
|
if (m.type == PKT_PENDING) {
|
|
|
|
p = (radio_packet_t*) m.content.ptr;
|
|
|
|
|
2013-03-15 17:48:13 +01:00
|
|
|
read_802154_frame(p->data, &frame, p->length);
|
|
|
|
|
|
|
|
if (frame.payload[0] == ETX_BEACON) {
|
2013-03-03 17:47:11 +01:00
|
|
|
//copy to receive buffer
|
2013-03-15 17:48:13 +01:00
|
|
|
memcpy(etx_rec_buf, &frame.payload[0], frame.payload_len);
|
|
|
|
|
|
|
|
//create IPv6 address from radio packet
|
|
|
|
//we can do the cast here since rpl nodes can only have addr
|
|
|
|
//up to 8 bits
|
2013-03-19 14:04:23 +01:00
|
|
|
candidate_addr.uint8[ETX_IPV6_LAST_BYTE] = (uint8_t) p->src;
|
2013-03-15 17:48:13 +01:00
|
|
|
//handle the beacon
|
2013-03-18 19:22:07 +01:00
|
|
|
etx_handle_beacon(&candidate_addr);
|
2013-03-03 17:47:11 +01:00
|
|
|
}
|
2013-03-15 17:48:13 +01:00
|
|
|
|
2013-03-03 17:47:11 +01:00
|
|
|
p->processing--;
|
|
|
|
}
|
|
|
|
else if (m.type == ENOBUFFER) {
|
|
|
|
puts("Transceiver buffer full");
|
|
|
|
}
|
|
|
|
else {
|
2013-03-15 17:48:13 +01:00
|
|
|
//packet is not for me, whatever
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void etx_update(void) {
|
|
|
|
/*
|
|
|
|
* Update the current ETX values, then
|
|
|
|
* reset the received packet count for the candidates.
|
|
|
|
*
|
|
|
|
* This thread should never take very long, as it has to be done in less
|
|
|
|
* than (ETX_INTERVAL - maximum jitter), otherwise the ETX calculation might
|
|
|
|
* be faulty. TODO delete this once mutexes are in place
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
rpl_candidate_neighbor_t * candidate;
|
2013-03-18 19:22:07 +01:00
|
|
|
rpl_candidate_neighbor_t * end;
|
2013-03-15 17:48:13 +01:00
|
|
|
|
|
|
|
while (true) {
|
|
|
|
//good night
|
|
|
|
thread_sleep();
|
|
|
|
//Wait a tiny bit to allow for the last packet to be processed
|
|
|
|
hwtimer_wait(HWTIMER_TICKS(200));
|
|
|
|
|
|
|
|
for (candidate = &candidates[0], end = candidates
|
|
|
|
+ RPL_MAX_CANDIDATE_NEIGHBORS; candidate < end;
|
|
|
|
candidate++) {
|
|
|
|
|
|
|
|
if (candidate->used != 0) {
|
|
|
|
//update its ETX-metric and packet count TODO mutex this
|
2013-03-22 17:40:36 +01:00
|
|
|
//TODO probably better to change floating point-math to integer
|
|
|
|
//based math
|
|
|
|
candidate->cur_etx =
|
|
|
|
candidate->packets_rx / (double) ETX_ROUNDS;
|
2013-03-15 17:48:13 +01:00
|
|
|
candidate->packets_rx = 0;
|
|
|
|
printf(
|
|
|
|
"Updated ETX Metric to %f for candidate used was on %d",
|
|
|
|
candidate->cur_etx, candidate->used);
|
|
|
|
}
|
2013-03-03 17:47:11 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|