📄 timesyncp.nc
字号:
/*
* Copyright (c) 2002, Vanderbilt University
* All rights reserved.
*
* Permission to use, copy, modify, and distribute this software and its
* documentation for any purpose, without fee, and without written agreement is
* hereby granted, provided that the above copyright notice, the following
* two paragraphs and the author appear in all copies of this software.
*
* IN NO EVENT SHALL THE VANDERBILT UNIVERSITY BE LIABLE TO ANY PARTY FOR
* DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
* OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE VANDERBILT
* UNIVERSITY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* THE VANDERBILT UNIVERSITY SPECIFICALLY DISCLAIMS ANY WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
* ON AN "AS IS" BASIS, AND THE VANDERBILT UNIVERSITY HAS NO OBLIGATION TO
* PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
*
* @author: Miklos Maroti, Brano Kusy (kusy@isis.vanderbilt.edu), Janos Sallai
* Ported to T2: 3/17/08 by Brano Kusy (branislav.kusy@gmail.com)
*/
#include "TimeSyncMsg.h"
generic module TimeSyncP(typedef precision_tag)
{
provides
{
interface Init;
interface StdControl;
interface GlobalTime<precision_tag>;
//interfaces for extra functionality: need not to be wired
interface TimeSyncInfo;
interface TimeSyncMode;
interface TimeSyncNotify;
}
uses
{
interface Boot;
interface SplitControl as RadioControl;
interface TimeSyncAMSend<precision_tag,uint32_t> as Send;
interface Receive;
interface Timer<TMilli>;
interface Random;
interface Leds;
interface TimeSyncPacket<precision_tag,uint32_t>;
interface LocalTime<precision_tag> as LocalTime;
#ifdef LOW_POWER_LISTENING
interface LowPowerListening;
#endif
}
}
implementation
{
#ifndef TIMESYNC_RATE
#define TIMESYNC_RATE 10
#endif
enum {
MAX_ENTRIES = 8, // number of entries in the table
BEACON_RATE = TIMESYNC_RATE, // how often send the beacon msg (in seconds)
ROOT_TIMEOUT = 5, //time to declare itself the root if no msg was received (in sync periods)
IGNORE_ROOT_MSG = 4, // after becoming the root ignore other roots messages (in send period)
ENTRY_VALID_LIMIT = 4, // number of entries to become synchronized
ENTRY_SEND_LIMIT = 3, // number of entries to send sync messages
ENTRY_THROWOUT_LIMIT = 500, // if time sync error is bigger than this clear the table
};
typedef struct TableItem
{
uint8_t state;
uint32_t localTime;
int32_t timeOffset; // globalTime - localTime
} TableItem;
enum {
ENTRY_EMPTY = 0,
ENTRY_FULL = 1,
};
TableItem table[MAX_ENTRIES];
uint8_t tableEntries;
enum {
STATE_IDLE = 0x00,
STATE_PROCESSING = 0x01,
STATE_SENDING = 0x02,
STATE_INIT = 0x04,
};
uint8_t state, mode;
/*
We do linear regression from localTime to timeOffset (globalTime - localTime).
This way we can keep the slope close to zero (ideally) and represent it
as a float with high precision.
timeOffset - offsetAverage = skew * (localTime - localAverage)
timeOffset = offsetAverage + skew * (localTime - localAverage)
globalTime = localTime + offsetAverage + skew * (localTime - localAverage)
*/
float skew;
uint32_t localAverage;
int32_t offsetAverage;
uint8_t numEntries; // the number of full entries in the table
message_t processedMsgBuffer;
message_t* processedMsg;
message_t outgoingMsgBuffer;
TimeSyncMsg* outgoingMsg;
uint8_t heartBeats; // the number of sucessfully sent messages
// since adding a new entry with lower beacon id than ours
async command uint32_t GlobalTime.getLocalTime()
{
return call LocalTime.get();
}
async command error_t GlobalTime.getGlobalTime(uint32_t *time)
{
*time = call GlobalTime.getLocalTime();
return call GlobalTime.local2Global(time);
}
error_t is_synced()
{
if (numEntries>=ENTRY_VALID_LIMIT || outgoingMsg->rootID==TOS_NODE_ID)
return SUCCESS;
else
return FAIL;
}
async command error_t GlobalTime.local2Global(uint32_t *time)
{
*time += offsetAverage + (int32_t)(skew * (int32_t)(*time - localAverage));
return is_synced();
}
async command error_t GlobalTime.global2Local(uint32_t *time)
{
uint32_t approxLocalTime = *time - offsetAverage;
*time = approxLocalTime - (int32_t)(skew * (int32_t)(approxLocalTime - localAverage));
return is_synced();
}
void calculateConversion()
{
float newSkew = skew;
uint32_t newLocalAverage;
int32_t newOffsetAverage;
int32_t localAverageRest;
int32_t offsetAverageRest;
int64_t localSum;
int64_t offsetSum;
int8_t i;
for(i = 0; i < MAX_ENTRIES && table[i].state != ENTRY_FULL; ++i)
;
if( i >= MAX_ENTRIES ) // table is empty
return;
/*
We use a rough approximation first to avoid time overflow errors. The idea
is that all times in the table should be relatively close to each other.
*/
newLocalAverage = table[i].localTime;
newOffsetAverage = table[i].timeOffset;
localSum = 0;
localAverageRest = 0;
offsetSum = 0;
offsetAverageRest = 0;
while( ++i < MAX_ENTRIES )
if( table[i].state == ENTRY_FULL ) {
/*
This only works because C ISO 1999 defines the signe for modulo the same as for the Dividend!
*/
localSum += (int32_t)(table[i].localTime - newLocalAverage) / tableEntries;
localAverageRest += (table[i].localTime - newLocalAverage) % tableEntries;
offsetSum += (int32_t)(table[i].timeOffset - newOffsetAverage) / tableEntries;
offsetAverageRest += (table[i].timeOffset - newOffsetAverage) % tableEntries;
}
newLocalAverage += localSum + localAverageRest / tableEntries;
newOffsetAverage += offsetSum + offsetAverageRest / tableEntries;
localSum = offsetSum = 0;
for(i = 0; i < MAX_ENTRIES; ++i)
if( table[i].state == ENTRY_FULL ) {
int32_t a = table[i].localTime - newLocalAverage;
int32_t b = table[i].timeOffset - newOffsetAverage;
localSum += (int64_t)a * a;
offsetSum += (int64_t)a * b;
}
if( localSum != 0 )
newSkew = (float)offsetSum / (float)localSum;
atomic
{
skew = newSkew;
offsetAverage = newOffsetAverage;
localAverage = newLocalAverage;
numEntries = tableEntries;
}
}
void clearTable()
{
int8_t i;
for(i = 0; i < MAX_ENTRIES; ++i)
table[i].state = ENTRY_EMPTY;
atomic numEntries = 0;
}
uint8_t numErrors=0;
void addNewEntry(TimeSyncMsg *msg)
{
int8_t i, freeItem = -1, oldestItem = 0;
uint32_t age, oldestTime = 0;
int32_t timeError;
// clear table if the received entry's been inconsistent for some time
timeError = msg->localTime;
call GlobalTime.local2Global((uint32_t*)(&timeError));
timeError -= msg->globalTime;
if( (is_synced() == SUCCESS) &&
(timeError > ENTRY_THROWOUT_LIMIT || timeError < -ENTRY_THROWOUT_LIMIT))
{
if (++numErrors>3)
clearTable();
return; // don't incorporate a bad reading
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -