Contiki 3.x
tsch-adaptive-timesync.c
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2015, SICS Swedish ICT.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  * notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  * notice, this list of conditions and the following disclaimer in the
12  * documentation and/or other materials provided with the distribution.
13  * 3. Neither the name of the Institute nor the names of its contributors
14  * may be used to endorse or promote products derived from this software
15  * without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  *
29  * This file is part of the Contiki operating system.
30  *
31  */
32 
33 /**
34  * \file
35  * TSCH adaptive time synchronization
36  * \author
37  * Atis Elsts <atis.elsts@sics.se>
38  *
39  */
40 
41 #include "tsch-adaptive-timesync.h"
42 #include "tsch-log.h"
43 #include <stdio.h>
44 
45 #if TSCH_ADAPTIVE_TIMESYNC
46 
47 /* Estimated drift of the time-source neighbor. Can be negative.
48  * Units used: ppm multiplied by 256. */
49 static int32_t drift_ppm;
50 /* Ticks compensated locally since the last timesync time */
51 static int32_t compensated_ticks;
52 /* Number of already recorded timesync history entries */
53 static uint8_t timesync_entry_count;
54 /* Since last learning of the drift; may be more than time since last timesync */
55 static uint32_t asn_since_last_learning;
56 
57 /* Units in which drift is stored: ppm * 256 */
58 #define TSCH_DRIFT_UNIT (1000L * 1000 * 256)
59 
60 /*---------------------------------------------------------------------------*/
61 /* Add a value to a moving average estimator */
62 static int32_t
63 timesync_entry_add(int32_t val, uint32_t time_delta)
64 {
65 #define NUM_TIMESYNC_ENTRIES 8
66  static int32_t buffer[NUM_TIMESYNC_ENTRIES];
67  static uint8_t pos;
68  int i;
69  if(timesync_entry_count == 0) {
70  pos = 0;
71  }
72  buffer[pos] = val;
73  if(timesync_entry_count < NUM_TIMESYNC_ENTRIES) {
74  timesync_entry_count++;
75  }
76  pos = (pos + 1) % NUM_TIMESYNC_ENTRIES;
77 
78  val = 0;
79  for(i = 0; i < timesync_entry_count; ++i) {
80  val += buffer[i];
81  }
82  return val / timesync_entry_count;
83 }
84 /*---------------------------------------------------------------------------*/
85 /* Learn the neighbor drift rate at ppm */
86 static void
87 timesync_learn_drift_ticks(uint32_t time_delta_asn, int32_t drift_ticks)
88 {
89  /* should fit in 32-bit unsigned integer */
90  uint32_t time_delta_ticks = time_delta_asn * tsch_timing[tsch_ts_timeslot_length];
91  int32_t real_drift_ticks = drift_ticks + compensated_ticks;
92  int32_t last_drift_ppm = (int32_t)((int64_t)real_drift_ticks * TSCH_DRIFT_UNIT / time_delta_ticks);
93 
94  drift_ppm = timesync_entry_add(last_drift_ppm, time_delta_ticks);
95 }
96 /*---------------------------------------------------------------------------*/
97 /* Either reset or update the neighbor's drift */
98 void
99 tsch_timesync_update(struct tsch_neighbor *n, uint16_t time_delta_asn, int32_t drift_correction)
100 {
101  /* Account the drift if either this is a new timesource,
102  * or the timedelta is not too small, as smaller timedelta
103  * means proportionally larger measurement error. */
104  if(last_timesource_neighbor != n) {
105  last_timesource_neighbor = n;
106  drift_ppm = 0;
107  timesync_entry_count = 0;
108  compensated_ticks = 0;
109  asn_since_last_learning = 0;
110  } else {
111  asn_since_last_learning += time_delta_asn;
112  if(asn_since_last_learning >= 4 * TSCH_SLOTS_PER_SECOND) {
113  timesync_learn_drift_ticks(asn_since_last_learning, drift_correction);
114  compensated_ticks = 0;
115  asn_since_last_learning = 0;
116  } else {
117  /* Too small timedelta, do not recalculate the drift to avoid introducing error. instead account for the corrected ticks */
118  compensated_ticks += drift_correction;
119  }
120  }
121 }
122 /*---------------------------------------------------------------------------*/
123 /* Error-accumulation free compensation algorithm */
124 static int32_t
125 compensate_internal(uint32_t time_delta_usec, int32_t drift_ppm, int32_t *remainder, int16_t *tick_conversion_error)
126 {
127  int64_t d = (int64_t)time_delta_usec * drift_ppm + *remainder;
128  int32_t amount = d / TSCH_DRIFT_UNIT;
129  int32_t amount_ticks;
130 
131  *remainder = (int32_t)(d - amount * TSCH_DRIFT_UNIT);
132 
133  amount += *tick_conversion_error;
134  amount_ticks = US_TO_RTIMERTICKS(amount);
135  *tick_conversion_error = amount - RTIMERTICKS_TO_US(amount_ticks);
136 
137  if(ABS(amount_ticks) > RTIMER_ARCH_SECOND / 128) {
138  TSCH_LOG_ADD(tsch_log_message,
139  snprintf(log->message, sizeof(log->message),
140  "!too big compensation %ld delta %ld", amount_ticks, time_delta_usec));
141  amount_ticks = (amount_ticks > 0 ? RTIMER_ARCH_SECOND : -RTIMER_ARCH_SECOND) / 128;
142  }
143 
144  return amount_ticks;
145 }
146 /*---------------------------------------------------------------------------*/
147 /* Do the compensation step before scheduling a new timeslot */
148 int32_t
149 tsch_timesync_adaptive_compensate(rtimer_clock_t time_delta_ticks)
150 {
151  int32_t result = 0;
152  uint32_t time_delta_usec = RTIMERTICKS_TO_US_64(time_delta_ticks);
153 
154  /* compensate, but not if the neighbor is not known */
155  if(drift_ppm && last_timesource_neighbor != NULL) {
156  static int32_t remainder;
157  static int16_t tick_conversion_error;
158  result = compensate_internal(time_delta_usec, drift_ppm,
159  &remainder, &tick_conversion_error);
160  compensated_ticks += result;
161  }
162 
163  if(TSCH_BASE_DRIFT_PPM) {
164  static int32_t base_drift_remainder;
165  static int16_t base_drift_tick_conversion_error;
166  result += compensate_internal(time_delta_usec, 256L * TSCH_BASE_DRIFT_PPM,
167  &base_drift_remainder, &base_drift_tick_conversion_error);
168  }
169 
170  return result;
171 }
172 /*---------------------------------------------------------------------------*/
173 #else /* TSCH_ADAPTIVE_TIMESYNC */
174 /*---------------------------------------------------------------------------*/
175 void
176 tsch_timesync_update(struct tsch_neighbor *n, uint16_t time_delta_asn, int32_t drift_correction)
177 {
178 }
179 /*---------------------------------------------------------------------------*/
180 int32_t
181 tsch_timesync_adaptive_compensate(rtimer_clock_t delta_ticks)
182 {
183  return 0;
184 }
185 /*---------------------------------------------------------------------------*/
186 #endif /* TSCH_ADAPTIVE_TIMESYNC */
#define NULL
The null pointer.