Merge pull request #804 from atiselsts/feature/application-level-timesync

Application level time synchronization example with TSCH
This commit is contained in:
George Oikonomou 2019-01-10 23:32:22 +02:00 committed by GitHub
commit fe07e6dd79
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 321 additions and 7 deletions

View File

@ -0,0 +1,24 @@
CONTIKI_PROJECT = node
all: $(CONTIKI_PROJECT)
CONTIKI=../../..
PLATFORMS_EXCLUDE = sky nrf52dk native
# force Orchestra from command line
MAKE_WITH_ORCHESTRA ?= 0
# force Security from command line
MAKE_WITH_SECURITY ?= 0
MAKE_MAC = MAKE_MAC_TSCH
MODULES += os/services/shell
ifeq ($(MAKE_WITH_ORCHESTRA),1)
MODULES += os/services/orchestra
endif
ifeq ($(MAKE_WITH_SECURITY),1)
CFLAGS += -DWITH_SECURITY=1
endif
include $(CONTIKI)/Makefile.include

View File

@ -0,0 +1,18 @@
This is a demonstration of application-level time synchronization using TSCH.
In a TSCH network, all nodes are synchronized to a global time counter maintained
by the coordinator. This fact can be exploited to measure properties such as latency.
The modes periodically send their detected network uptime to the coordinator.
The coordinator receives these packets, prints its local network uptime
and the time difference. This time difference is equal to the end-to-end latency,
which includes both the time to prepare the packet, the time until an appropriate
slot in the TSCH schedule, the over-the-air time (negligible). If the packet
does not arrive with the first attempt, it also includes the retransmission time.
The nodes in this example do not have any notion of the wall-clock time.
That would need additional synchronization between the TSCH network and an external clock source.
For example, one can periodically distribute UNIX timestamps over the UART interface
on the border router to implement this feature. Alternatively, the data collected from
the TSCH network can be timestamped with just TSCH timestamps, and them on the external gateway
these timestamps could be converted to wall-clock time.

View File

@ -0,0 +1,139 @@
/*
* Copyright (c) 2015, SICS Swedish ICT.
* Copyright (c) 2018, University of Bristol - http://www.bristol.ac.uk
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the Institute nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
*/
/**
* \file
* A RPL+TSCH node demonstrating application-level time syncrhonization.
*
* \author Atis Elsts <atis.elsts@bristol.ac.uk>
* Simon Duquennoy <simonduq@sics.se>
*/
#include "contiki.h"
#include "net/routing/routing.h"
#include "net/netstack.h"
#include "net/ipv6/simple-udp.h"
#include "net/mac/tsch/tsch.h"
#include "lib/random.h"
#include "sys/node-id.h"
#include "sys/log.h"
#define LOG_MODULE "App"
#define LOG_LEVEL LOG_LEVEL_INFO
#define UDP_CLIENT_PORT 8765
#define UDP_SERVER_PORT 5678
#define SEND_INTERVAL (60 * CLOCK_SECOND)
/*---------------------------------------------------------------------------*/
static struct simple_udp_connection client_conn, server_conn;
PROCESS(node_process, "RPL Node");
AUTOSTART_PROCESSES(&node_process);
/*---------------------------------------------------------------------------*/
static void
udp_rx_callback(struct simple_udp_connection *c,
const uip_ipaddr_t *sender_addr,
uint16_t sender_port,
const uip_ipaddr_t *receiver_addr,
uint16_t receiver_port,
const uint8_t *data,
uint16_t datalen)
{
uint64_t local_time_clock_ticks = tsch_get_network_uptime_ticks();
uint64_t remote_time_clock_ticks;
if(datalen >= sizeof(remote_time_clock_ticks)) {
memcpy(&remote_time_clock_ticks, data, sizeof(remote_time_clock_ticks));
LOG_INFO("Received from ");
LOG_INFO_6ADDR(sender_addr);
LOG_INFO_(", created at %lu, now %lu, latency %lu clock ticks\n",
(unsigned long)remote_time_clock_ticks,
(unsigned long)local_time_clock_ticks,
(unsigned long)(local_time_clock_ticks - remote_time_clock_ticks));
}
}
/*---------------------------------------------------------------------------*/
PROCESS_THREAD(node_process, ev, data)
{
static struct etimer periodic_timer;
int is_coordinator;
uip_ipaddr_t dest_ipaddr;
PROCESS_BEGIN();
is_coordinator = 0;
#if CONTIKI_TARGET_COOJA
is_coordinator = (node_id == 1);
#endif
if(is_coordinator) {
NETSTACK_ROUTING.root_start();
}
/* Initialize UDP connections */
simple_udp_register(&server_conn, UDP_SERVER_PORT, NULL,
UDP_CLIENT_PORT, udp_rx_callback);
simple_udp_register(&client_conn, UDP_CLIENT_PORT, NULL,
UDP_SERVER_PORT, NULL);
NETSTACK_MAC.on();
etimer_set(&periodic_timer, random_rand() % SEND_INTERVAL);
while(1) {
PROCESS_WAIT_EVENT_UNTIL(etimer_expired(&periodic_timer));
if(tsch_is_coordinator) {
break;
}
if(NETSTACK_ROUTING.node_is_reachable() && NETSTACK_ROUTING.get_root_ipaddr(&dest_ipaddr)) {
/* Send network uptime timestamp to the DAG root */
uint64_t network_uptime;
network_uptime = tsch_get_network_uptime_ticks();
simple_udp_sendto(&client_conn, &network_uptime, sizeof(network_uptime), &dest_ipaddr);
LOG_INFO("Sent network uptime timestamp %lu to ", (unsigned long)network_uptime);
LOG_INFO_6ADDR(&dest_ipaddr);
LOG_INFO_("\n");
} else {
LOG_INFO("Not reachable yet\n");
}
/* Add some jitter */
etimer_set(&periodic_timer, SEND_INTERVAL
- CLOCK_SECOND + (random_rand() % (2 * CLOCK_SECOND)));
}
PROCESS_END();
}
/*---------------------------------------------------------------------------*/

View File

@ -0,0 +1,83 @@
/*
* Copyright (c) 2015, SICS Swedish ICT.
* Copyright (c) 2018, University of Bristol - http://www.bristol.ac.uk
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the Institute nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
*/
/**
* \author Simon Duquennoy <simonduq@sics.se>
* Atis Elsts <atis.elsts@bristol.ac.uk>
*/
#ifndef __PROJECT_CONF_H__
#define __PROJECT_CONF_H__
/* Set to enable TSCH security */
#ifndef WITH_SECURITY
#define WITH_SECURITY 0
#endif /* WITH_SECURITY */
/* USB serial takes space, free more space elsewhere */
#define SICSLOWPAN_CONF_FRAG 0
#define UIP_CONF_BUFFER_SIZE 160
/*******************************************************/
/******************* Configure TSCH ********************/
/*******************************************************/
/* IEEE802.15.4 PANID */
#define IEEE802154_CONF_PANID 0x81a5
/* Do not start TSCH at init, wait for NETSTACK_MAC.on() */
#define TSCH_CONF_AUTOSTART 0
/* 6TiSCH minimal schedule length.
* Larger values result in less frequent active slots: reduces capacity and saves energy. */
#define TSCH_SCHEDULE_CONF_DEFAULT_LENGTH 3
#if WITH_SECURITY
/* Enable security */
#define LLSEC802154_CONF_ENABLED 1
#endif /* WITH_SECURITY */
/*******************************************************/
/************* Other system configuration **************/
/*******************************************************/
/* Logging */
#define LOG_CONF_LEVEL_RPL LOG_LEVEL_WARN
#define LOG_CONF_LEVEL_TCPIP LOG_LEVEL_WARN
#define LOG_CONF_LEVEL_IPV6 LOG_LEVEL_WARN
#define LOG_CONF_LEVEL_6LOWPAN LOG_LEVEL_WARN
#define LOG_CONF_LEVEL_MAC LOG_LEVEL_WARN
#define LOG_CONF_LEVEL_FRAMER LOG_LEVEL_WARN
#define TSCH_LOG_CONF_PER_SLOT 0
#endif /* __PROJECT_CONF_H__ */

View File

@ -52,6 +52,7 @@
#include "net/queuebuf.h" #include "net/queuebuf.h"
#include "net/mac/framer/framer-802154.h" #include "net/mac/framer/framer-802154.h"
#include "net/mac/tsch/tsch.h" #include "net/mac/tsch/tsch.h"
#include "sys/critical.h"
#include "sys/log.h" #include "sys/log.h"
/* TSCH debug macros, i.e. to set LEDs or GPIOs on various TSCH /* TSCH debug macros, i.e. to set LEDs or GPIOs on various TSCH
@ -126,9 +127,10 @@ struct tsch_packet *dequeued_array[TSCH_DEQUEUED_ARRAY_SIZE];
struct ringbufindex input_ringbuf; struct ringbufindex input_ringbuf;
struct input_packet input_array[TSCH_MAX_INCOMING_PACKETS]; struct input_packet input_array[TSCH_MAX_INCOMING_PACKETS];
/* Updates and reads of the next two variables must be atomic (i.e. both together) */
/* Last time we received Sync-IE (ACK or data packet from a time source) */ /* Last time we received Sync-IE (ACK or data packet from a time source) */
static struct tsch_asn_t last_sync_asn; static struct tsch_asn_t last_sync_asn;
clock_time_t last_sync_time; /* Same info, in clock_time_t units */ clock_time_t tsch_last_sync_time; /* Same info, in clock_time_t units */
/* A global lock for manipulating data structures safely from outside of interrupt */ /* A global lock for manipulating data structures safely from outside of interrupt */
static volatile int tsch_locked = 0; static volatile int tsch_locked = 0;
@ -347,6 +349,33 @@ get_packet_and_neighbor_for_link(struct tsch_link *link, struct tsch_neighbor **
return p; return p;
} }
/*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/
uint64_t
tsch_get_network_uptime_ticks(void)
{
uint64_t uptime_asn;
uint64_t uptime_ticks;
int_master_status_t status;
if(!tsch_is_associated) {
/* not associated, network uptime is not known */
return (uint64_t)-1;
}
status = critical_enter();
uptime_asn = last_sync_asn.ls4b + ((uint64_t)last_sync_asn.ms1b << 32);
/* first calculate the at the uptime at the last sync in rtimer ticks */
uptime_ticks = uptime_asn * tsch_timing[tsch_ts_timeslot_length];
/* then convert to clock ticks (assume that CLOCK_SECOND divides RTIMER_ARCH_SECOND) */
uptime_ticks /= (RTIMER_ARCH_SECOND / CLOCK_SECOND);
/* then add the ticks passed since the last timesync */
uptime_ticks += (clock_time() - tsch_last_sync_time);
critical_exit(status);
return uptime_ticks;
}
/*---------------------------------------------------------------------------*/
/** /**
* This function turns on the radio. Its semantics is dependent on * This function turns on the radio. Its semantics is dependent on
* the value of TSCH_RADIO_ON_DURING_TIMESLOT constant: * the value of TSCH_RADIO_ON_DURING_TIMESLOT constant:
@ -623,7 +652,7 @@ PT_THREAD(tsch_tx_slot(struct pt *pt, struct rtimer *t))
tsch_timesync_update(current_neighbor, since_last_timesync, drift_correction); tsch_timesync_update(current_neighbor, since_last_timesync, drift_correction);
/* Keep track of sync time */ /* Keep track of sync time */
last_sync_asn = tsch_current_asn; last_sync_asn = tsch_current_asn;
last_sync_time = clock_time(); tsch_last_sync_time = clock_time();
tsch_schedule_keepalive(); tsch_schedule_keepalive();
} }
mac_tx_status = MAC_TX_OK; mac_tx_status = MAC_TX_OK;
@ -882,7 +911,7 @@ PT_THREAD(tsch_rx_slot(struct pt *pt, struct rtimer *t))
int32_t since_last_timesync = TSCH_ASN_DIFF(tsch_current_asn, last_sync_asn); int32_t since_last_timesync = TSCH_ASN_DIFF(tsch_current_asn, last_sync_asn);
/* Keep track of last sync time */ /* Keep track of last sync time */
last_sync_asn = tsch_current_asn; last_sync_asn = tsch_current_asn;
last_sync_time = clock_time(); tsch_last_sync_time = clock_time();
/* Save estimated drift */ /* Save estimated drift */
drift_correction = -estimated_drift; drift_correction = -estimated_drift;
is_drift_correction_used = 1; is_drift_correction_used = 1;
@ -1014,6 +1043,13 @@ PT_THREAD(tsch_slot_operation(struct rtimer *t, void *ptr))
/* End of slot operation, schedule next slot or resynchronize */ /* End of slot operation, schedule next slot or resynchronize */
if(tsch_is_coordinator) {
/* Update the `last_sync_*` variables to avoid large errors
* in the application-level time synchronization */
last_sync_asn = tsch_current_asn;
tsch_last_sync_time = clock_time();
}
/* Do we need to resynchronize? i.e., wait for EB again */ /* Do we need to resynchronize? i.e., wait for EB again */
if(!tsch_is_coordinator && (TSCH_ASN_DIFF(tsch_current_asn, last_sync_asn) > if(!tsch_is_coordinator && (TSCH_ASN_DIFF(tsch_current_asn, last_sync_asn) >
(100 * TSCH_CLOCK_TO_SLOTS(TSCH_DESYNC_THRESHOLD / 100, tsch_timing[tsch_ts_timeslot_length])))) { (100 * TSCH_CLOCK_TO_SLOTS(TSCH_DESYNC_THRESHOLD / 100, tsch_timing[tsch_ts_timeslot_length])))) {
@ -1115,10 +1151,14 @@ void
tsch_slot_operation_sync(rtimer_clock_t next_slot_start, tsch_slot_operation_sync(rtimer_clock_t next_slot_start,
struct tsch_asn_t *next_slot_asn) struct tsch_asn_t *next_slot_asn)
{ {
int_master_status_t status;
current_slot_start = next_slot_start; current_slot_start = next_slot_start;
tsch_current_asn = *next_slot_asn; tsch_current_asn = *next_slot_asn;
status = critical_enter();
last_sync_asn = tsch_current_asn; last_sync_asn = tsch_current_asn;
last_sync_time = clock_time(); tsch_last_sync_time = clock_time();
critical_exit(status);
current_link = NULL; current_link = NULL;
} }
/*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/

View File

@ -56,7 +56,7 @@ extern struct tsch_packet *dequeued_array[TSCH_DEQUEUED_ARRAY_SIZE];
extern struct ringbufindex input_ringbuf; extern struct ringbufindex input_ringbuf;
extern struct input_packet input_array[TSCH_MAX_INCOMING_PACKETS]; extern struct input_packet input_array[TSCH_MAX_INCOMING_PACKETS];
/* Last clock_time_t where synchronization happened */ /* Last clock_time_t where synchronization happened */
extern clock_time_t last_sync_time; extern clock_time_t tsch_last_sync_time;
/* Counts the length of the current burst */ /* Counts the length of the current burst */
extern int tsch_current_burst_count; extern int tsch_current_burst_count;

View File

@ -226,6 +226,12 @@ void tsch_schedule_keepalive(void);
* Schedule a keep-alive immediately * Schedule a keep-alive immediately
*/ */
void tsch_schedule_keepalive_immediately(void); void tsch_schedule_keepalive_immediately(void);
/**
* Get the time, in clock ticks, since the TSCH network was started.
*
* \return The network uptime, or -1 if the node is not part of a TSCH network.
*/
uint64_t tsch_get_network_uptime_ticks(void);
/** /**
* Leave the TSCH network we are currently in * Leave the TSCH network we are currently in
*/ */

View File

@ -584,8 +584,12 @@ PT_THREAD(cmd_tsch_status(struct pt *pt, shell_output_func output, char *args))
} else { } else {
SHELL_OUTPUT(output, "none\n"); SHELL_OUTPUT(output, "none\n");
} }
SHELL_OUTPUT(output, "-- Last synchronized: %lu seconds ago\n", (clock_time() - last_sync_time) / CLOCK_SECOND); SHELL_OUTPUT(output, "-- Last synchronized: %lu seconds ago\n",
SHELL_OUTPUT(output, "-- Drift w.r.t. coordinator: %ld ppm\n", tsch_adaptive_timesync_get_drift_ppm()); (clock_time() - tsch_last_sync_time) / CLOCK_SECOND);
SHELL_OUTPUT(output, "-- Drift w.r.t. coordinator: %ld ppm\n",
tsch_adaptive_timesync_get_drift_ppm());
SHELL_OUTPUT(output, "-- Network uptime: %lu seconds\n",
(unsigned long)(tsch_get_network_uptime_ticks() / CLOCK_SECOND));
} }
PT_END(pt); PT_END(pt);