summaryrefslogtreecommitdiff
path: root/lora_pkt_fwd/src/timersync.c
blob: 3dd919b0eb033db6b05570aa27940ca326beb774 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
/*
 / _____)             _              | |
( (____  _____ ____ _| |_ _____  ____| |__
 \____ \| ___ |    (_   _) ___ |/ ___)  _ \
 _____) ) ____| | | || |_| ____( (___| | | |
(______/|_____)_|_|_| \__)_____)\____)_| |_|
  (C)2013 Semtech-Cycleo

Description:
    LoRa concentrator : Timer synchronization
        Provides synchronization between unix, concentrator and gps clocks

License: Revised BSD License, see LICENSE.TXT file include in the project
Maintainer: Michael Coracin
*/

/* -------------------------------------------------------------------------- */
/* --- DEPENDANCIES --------------------------------------------------------- */

#include <stdio.h>        /* printf, fprintf, snprintf, fopen, fputs */
#include <stdint.h>        /* C99 types */
#include <pthread.h>

#include "trace.h"
#include "timersync.h"
#include "loragw_hal.h"
#include "loragw_reg.h"
#include "loragw_aux.h"

/* -------------------------------------------------------------------------- */
/* --- PRIVATE CONSTANTS & TYPES -------------------------------------------- */

/* -------------------------------------------------------------------------- */
/* --- PRIVATE MACROS ------------------------------------------------------- */

#define timersub(a, b, result)                                                \
  do {                                                                        \
    (result)->tv_sec = (a)->tv_sec - (b)->tv_sec;                             \
    (result)->tv_usec = (a)->tv_usec - (b)->tv_usec;                          \
    if ((result)->tv_usec < 0) {                                              \
      --(result)->tv_sec;                                                     \
      (result)->tv_usec += 1000000;                                           \
    }                                                                         \
  } while (0)

/* -------------------------------------------------------------------------- */
/* --- PRIVATE VARIABLES (GLOBAL) ------------------------------------------- */

static pthread_mutex_t mx_timersync = PTHREAD_MUTEX_INITIALIZER; /* control access to timer sync offsets */
static struct timeval offset_unix_concent = {0,0}; /* timer offset between unix host and concentrator */

/* -------------------------------------------------------------------------- */
/* --- PRIVATE SHARED VARIABLES (GLOBAL) ------------------------------------ */
extern bool exit_sig;
extern bool quit_sig;
extern pthread_mutex_t mx_concent;

/* -------------------------------------------------------------------------- */
/* --- PUBLIC FUNCTIONS DEFINITION ------------------------------------------ */

int get_concentrator_time(struct timeval *concent_time, struct timeval unix_time) {
    struct timeval local_timeval;

    if (concent_time == NULL) {
        MSG("ERROR: %s invalid parameter\n", __FUNCTION__);
        return -1;
    }

    pthread_mutex_lock(&mx_timersync); /* protect global variable access */
    timersub(&unix_time, &offset_unix_concent, &local_timeval);
    pthread_mutex_unlock(&mx_timersync);

    /* TODO: handle sx1301 coutner wrap-up !! */
    concent_time->tv_sec = local_timeval.tv_sec;
    concent_time->tv_usec = local_timeval.tv_usec;

    MSG_DEBUG(DEBUG_TIMERSYNC, " --> TIME: unix current time is   %ld,%ld\n", unix_time.tv_sec, unix_time.tv_usec);
    MSG_DEBUG(DEBUG_TIMERSYNC, "           offset is              %ld,%ld\n", offset_unix_concent.tv_sec, offset_unix_concent.tv_usec);
    MSG_DEBUG(DEBUG_TIMERSYNC, "           sx1301 current time is %ld,%ld\n", local_timeval.tv_sec, local_timeval.tv_usec);

    return 0;
}

/* ---------------------------------------------------------------------------------------------- */
/* --- THREAD 6: REGULARLAY MONITOR THE OFFSET BETWEEN UNIX CLOCK AND CONCENTRATOR CLOCK -------- */

void thread_timersync(void) {
    struct timeval unix_timeval;
    struct timeval concentrator_timeval;
    uint32_t sx1301_timecount = 0;
    struct timeval offset_previous = {0,0};
    struct timeval offset_drift = {0,0}; /* delta between current and previous offset */

    while (!exit_sig && !quit_sig) {
        /* Regularly disable GPS mode of concentrator's counter, in order to get
            real timer value for synchronizing with host's unix timer */
        MSG("\nINFO: Disabling GPS mode for concentrator's counter...\n");
        pthread_mutex_lock(&mx_concent);
        lgw_reg_w(LGW_GPS_EN, 0);
        pthread_mutex_unlock(&mx_concent);

        /* Get current unix time */
        gettimeofday(&unix_timeval, NULL);

        /* Get current concentrator counter value (1MHz) */
        pthread_mutex_lock(&mx_concent);
        lgw_get_trigcnt(&sx1301_timecount);
        pthread_mutex_unlock(&mx_concent);
        concentrator_timeval.tv_sec = sx1301_timecount / 1000000UL;
        concentrator_timeval.tv_usec = sx1301_timecount - (concentrator_timeval.tv_sec * 1000000UL);

        /* Compute offset between unix and concentrator timers, with microsecond precision */
        offset_previous.tv_sec = offset_unix_concent.tv_sec;
        offset_previous.tv_usec = offset_unix_concent.tv_usec;

        /* TODO: handle sx1301 coutner wrap-up */
        pthread_mutex_lock(&mx_timersync); /* protect global variable access */
        timersub(&unix_timeval, &concentrator_timeval, &offset_unix_concent);
        pthread_mutex_unlock(&mx_timersync);

        timersub(&offset_unix_concent, &offset_previous, &offset_drift);

        MSG_DEBUG(DEBUG_TIMERSYNC, "  sx1301    = %u (µs) - timeval (%ld,%ld)\n",
            sx1301_timecount,
            concentrator_timeval.tv_sec,
            concentrator_timeval.tv_usec);
        MSG_DEBUG(DEBUG_TIMERSYNC, "  unix_timeval = %ld,%ld\n", unix_timeval.tv_sec, unix_timeval.tv_usec);

        MSG("INFO: host/sx1301 time offset=(%lds:%ldµs) - drift=%ldµs\n",
            offset_unix_concent.tv_sec,
            offset_unix_concent.tv_usec,
            offset_drift.tv_sec * 1000000UL + offset_drift.tv_usec);
        MSG("INFO: Enabling GPS mode for concentrator's counter.\n\n");
        pthread_mutex_lock(&mx_concent); /* TODO: Is it necessary to protect here? */
        lgw_reg_w(LGW_GPS_EN, 1);
        pthread_mutex_unlock(&mx_concent);

        /* delay next sync */
        /* If we consider a crystal oscillator precision of about 20ppm worst case, and a clock
            running at 1MHz, this would mean 1µs drift every 50000µs (10000000/20).
            As here the time precision is not critical, we should be able to cope with at least 1ms drift,
            which should occur after 50s (50000µs * 1000).
            Let's set the thread sleep to 1 minute for now */
        wait_ms(60000);
    }
}