forked from JS8Call-improved/JS8Call-improved
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathDriftingDateTime.h
More file actions
154 lines (133 loc) · 4.8 KB
/
DriftingDateTime.h
File metadata and controls
154 lines (133 loc) · 4.8 KB
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
147
148
149
150
151
152
153
154
#ifndef DRIFTINGDATETIME_H
#define DRIFTINGDATETIME_H
#include <QDateTime>
#include <QMutex>
#include <QPointer>
#include "TwoPhaseSignal.h"
/**
* JS8Call allows the user to manipulate the clock.
*
* One intention is: If a QSO partner's clock is off too much, we can
* set our clock to correspond to that partner's clock. This increases
* the probability of successful decodes on both sides.
*
* Alternatively, we may not be able to set our own computer's clock
* with appropriate precision. If so, we can manipulate our
* JS8Call-internal clock, e.g., based on incoming JS8 signals, to fit
* other folks' clocks.
*
* There is only one, central clock for the entire application.
*
* The manipulated clock is ubiquitously used throughout JS8Call. This
* includes timestamps logged to ALL.TXT or via Qt logging. This makes
* it easier to judge, especially when reading the Qt logs, whether
* JS8Call's timing is as it should be.
*
* There is exactly one object of class DriftingDateTimeObject
* (singleton pattern).
*
* Drift specified as a qint64 is always in ms.
* A positive drift means that the JS8Call internal clock
* is that many milliseconds later than the system clock,
* a negative drift that many milliseconds earlier.
*
* This functionality is (intended to be) thread-safe,
* with the exception of the setDrift(), which must be called
* by the same thread that originally constructed the object.
* If you want to make sure this is not violated,
* send the new drift value as a signal.
**/
class DriftingDateTimeSingleton: public TwoPhaseSignal
{
Q_OBJECT
private:
// As this is a subclass of QObject,
// it lives in a thread: Whatever thread
// first called getSingleton().
DriftingDateTimeSingleton();
qint64 driftMS;
mutable QMutex mutex;
static QPointer<DriftingDateTimeSingleton> singleton;
private:
/**
* This needs to be called by the same thread that constructed the object.
*/
void setDriftInner(qint64 ms);
public:
/**
* The first thread that calls this is the thread the singleton lives in.
*/
static DriftingDateTimeSingleton & getSingleton();
/**
* Retrive drift, in milliseconds.
*
* Positive values indicate the drifted clock is behind the system clock,
* negative, it is early.
*/
qint64 drift() const;
/** Retrieve drifted "now" as UTC. */
inline QDateTime currentDateTimeUtc() const {
return QDateTime::currentDateTimeUtc().addMSecs(drift());
}
/** Retrieve drifted "now" as local time. */
inline QDateTime currentDateTimeLocal() const {
return QDateTime::currentDateTime().addMSecs(drift());
}
/** Retrieve drifted "now" as milliseconds since epoch. */
inline qint64 currentMSecsSinceEpoch() const {
return QDateTime::currentMSecsSinceEpoch() + drift();
}
/**
* Retrieve drifted "now" as seconds since epoch.
*
* As we found out experimentally, QDateTime does not round,
* but truncates. So we do that, too. In other words,
* the number returned is the number of fully elapsed
* seconds since the epoch.
*/
inline qint64 currentSecsSinceEpoch() const {
return currentMSecsSinceEpoch() / 1000;
}
public slots:
/** Set the drift. */
void setDrift(qint64 ms);
/** Emits to the driftChanged signal (as per TwoPhaseSignal contract). */
void onPlumbingCompleted() const;
signals:
/**
* Communicate the drift change to anyone who want to know.
*
* This outgoing signal is the main motivation
* for introducing this class in the first place,
* rather than just using static methods.
*/
void driftChanged(qint64 new_drift) const;
};
/** This namespace contains some static convenience functions. */
namespace DriftingDateTime
{
inline qint64 drift() {
return DriftingDateTimeSingleton::getSingleton().drift();
}
/**
* This must only be called from whatever thread caused the first getSingleton
* to be called. If unsure, it is preferred to send a signal to
* object &DriftingDateTimeSingleton::getSingleton(), slot &DriftingDateTimeSingleton::setDrift .
*/
inline void setDrift(qint64 ms) {
DriftingDateTimeSingleton::getSingleton().setDrift(ms);
}
inline QDateTime currentDateTimeUtc() {
return DriftingDateTimeSingleton::getSingleton().currentDateTimeUtc();
}
inline QDateTime currentDateTimeLocal() {
return DriftingDateTimeSingleton::getSingleton().currentDateTimeLocal();
}
inline qint64 currentMSecsSinceEpoch() {
return DriftingDateTimeSingleton::getSingleton().currentMSecsSinceEpoch();
}
inline qint64 currentSecsSinceEpoch() {
return DriftingDateTimeSingleton::getSingleton().currentSecsSinceEpoch();
}
};
#endif // DRIFTINGDATETIME_H