datactl/timesync.h
datactl/timesync.h
Namespaces
Name |
---|
Syntalos |
Classes
Name | |
---|---|
class | Syntalos::FreqCounterSynchronizer Synchronizer for a monotonic counter, given a frequency. |
class | Syntalos::SecondaryClockSynchronizer Synchronizer for an external steady monotonic clock. |
Source code
/*
* Copyright (C) 2019-2024 Matthias Klumpp <matthias@tenstral.net>
*
* Licensed under the GNU Lesser General Public License Version 3
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the license, or
* (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this software. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <QDataStream>
#include <QLoggingCategory>
#include <QMetaType>
#include <QString>
#include <QUuid>
#include <fstream>
#include <memory>
#include "datactl/eigenaux.h"
#include "datactl/syclock.h"
#include "datactl/tsyncfile.h"
class QFile;
namespace Syntalos
{
Q_DECLARE_LOGGING_CATEGORY(logTimeSync)
static constexpr auto SECONDARY_CLOCK_TOLERANCE = microseconds_t(1000);
static constexpr auto DEFAULT_CLOCKSYNC_CHECK_INTERVAL = milliseconds_t(4000);
enum class TimeSyncStrategy {
NONE = 0,
SHIFT_TIMESTAMPS_FWD = 1 << 0,
SHIFT_TIMESTAMPS_BWD = 1 << 1,
ADJUST_CLOCK = 1 << 2,
WRITE_TSYNCFILE =
1 << 3
};
Q_DECLARE_FLAGS(TimeSyncStrategies, TimeSyncStrategy)
Q_DECLARE_OPERATORS_FOR_FLAGS(TimeSyncStrategies)
const QString timeSyncStrategyToHString(const TimeSyncStrategy &strategy);
const QString timeSyncStrategiesToHString(const TimeSyncStrategies &strategies);
} // namespace Syntalos
Q_DECLARE_METATYPE(Syntalos::TimeSyncStrategies);
namespace Syntalos
{
using SyncDetailsChangeNotifyFn =
std::function<void(const QString &id, const TimeSyncStrategies &strategies, const microseconds_t &tolerance)>;
using OffsetChangeNotifyFn = std::function<void(const QString &id, const microseconds_t ¤tOffset)>;
class FreqCounterSynchronizer
{
public:
explicit FreqCounterSynchronizer(
std::shared_ptr<SyncTimer> masterTimer,
const QString &modName,
double frequencyHz,
const QString &id = nullptr);
~FreqCounterSynchronizer();
void setNotifyCallbacks(
const SyncDetailsChangeNotifyFn &detailsChangeNotifyFn,
const OffsetChangeNotifyFn &offsetChangeNotifyFn);
void setCalibrationBlocksCount(int count);
void setStrategies(const TimeSyncStrategies &strategies);
void setTolerance(const std::chrono::microseconds &tolerance);
void setTimeSyncBasename(const QString &fname, const QUuid &collectionId);
void setLastValidMasterTimestamp(microseconds_t masterTimestamp);
microseconds_t lastMasterAssumedAcqTS() const;
bool isCalibrated() const;
int indexOffset() const;
bool start();
void stop();
void processTimestamps(
const microseconds_t &blocksRecvTimestamp,
int blockIndex,
int blockCount,
VectorXul &idxTimestamps);
private:
Q_DISABLE_COPY(FreqCounterSynchronizer)
QString m_modName;
QUuid m_collectionId;
QString m_id;
TimeSyncStrategies m_strategies;
microseconds_t m_lastOffsetEmission;
std::shared_ptr<SyncTimer> m_syTimer;
SyncDetailsChangeNotifyFn m_detailsChangeNotifyFn;
OffsetChangeNotifyFn m_offsetChangeNotifyFn;
uint m_toleranceUsec;
bool m_lastOffsetWithinTolerance;
uint m_calibrationMaxBlockN;
uint m_calibrationIdx;
VectorXsl m_tsOffsetsUsec;
bool m_haveExpectedOffset;
uint m_expectedOffsetCalCount;
microseconds_t m_expectedOffset;
double m_expectedSD;
uint m_offsetChangeWaitBlocks;
microseconds_t m_timeCorrectionOffset;
uint m_lastTimeIndex;
double m_freq;
double m_timePerPointUs;
int m_indexOffset;
bool m_applyIndexOffset;
uint m_lastSecondaryIdxUnandjusted;
microseconds_t m_lastMasterAssumedAcqTS;
microseconds_t m_lastValidMasterTimestamp;
std::unique_ptr<TimeSyncFileWriter> m_tswriter;
};
class SecondaryClockSynchronizer
{
public:
explicit SecondaryClockSynchronizer(
std::shared_ptr<SyncTimer> masterTimer,
const QString &modName,
const QString &id = QString());
~SecondaryClockSynchronizer();
void setNotifyCallbacks(
const SyncDetailsChangeNotifyFn &detailsChangeNotifyFn,
const OffsetChangeNotifyFn &offsetChangeNotifyFn);
microseconds_t clockCorrectionOffset() const;
void setCalibrationPointsCount(int timepointCount);
void setExpectedClockFrequencyHz(double frequency);
void setStrategies(const TimeSyncStrategies &strategies);
void setTolerance(const microseconds_t &tolerance);
void setTimeSyncBasename(const QString &fname, const QUuid &collectionId);
bool isCalibrated() const;
microseconds_t expectedOffsetToMaster() const;
bool start();
void stop();
void processTimestamp(microseconds_t &masterTimestamp, const microseconds_t &secondaryAcqTimestamp);
private:
Q_DISABLE_COPY(SecondaryClockSynchronizer)
void emitSyncDetailsChanged();
QString m_modName;
QUuid m_collectionId;
QString m_id;
TimeSyncStrategies m_strategies;
microseconds_t m_lastOffsetEmission;
std::shared_ptr<SyncTimer> m_syTimer;
SyncDetailsChangeNotifyFn m_detailsChangeNotifyFn;
OffsetChangeNotifyFn m_offsetChangeNotifyFn;
uint m_toleranceUsec;
bool m_lastOffsetWithinTolerance;
uint m_calibrationMaxN;
uint m_calibrationIdx;
VectorXsl m_clockOffsetsUsec;
bool m_haveExpectedOffset;
uint m_expectedOffsetCalCount;
microseconds_t m_expectedOffset;
double m_expectedSD;
microseconds_t m_clockCorrectionOffset;
microseconds_t m_lastMasterTS;
microseconds_t m_lastSecondaryAcqTS;
uint m_clockUpdateWaitPoints;
std::unique_ptr<TimeSyncFileWriter> m_tswriter;
};
template<typename T>
void safeStopSynchronizer(const T &synchronizerSmartPtr)
{
static_assert(
std::is_base_of<SecondaryClockSynchronizer, typename T::element_type>::value
|| std::is_base_of<FreqCounterSynchronizer, typename T::element_type>::value,
"This function requires a smart pointer to a clock synchronizer.");
if (synchronizerSmartPtr.get() != nullptr)
synchronizerSmartPtr->stop();
}
template<typename T>
void safeStopSynchronizer(const T &synchronizer, const microseconds_t &lastValidMasterTimestamp)
{
static_assert(
std::is_base_of<FreqCounterSynchronizer, typename T::element_type>::value,
"This function requires a smart pointer to a FreqCounter clock synchronizer.");
if (synchronizer) {
synchronizer->setLastValidMasterTimestamp(lastValidMasterTimestamp);
safeStopSynchronizer(synchronizer);
}
}
} // namespace Syntalos
Updated on 2024-11-06 at 17:10:29 +0000