datactl/timesync.h
datactl/timesync.h
Namespaces
| Name |
|---|
| Syntalos |
Classes
| Name | |
|---|---|
| class | Syntalos::TimeSyncStrategies |
Source code
/*
* Copyright (C) 2019-2026 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 <fstream>
#include <memory>
#include <type_traits>
#include "datactl/uuid.h"
#include "datactl/eigenaux.h"
#include "datactl/syclock.h"
#include "datactl/tsyncfile.h"
namespace Syntalos
{
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
};
class TimeSyncStrategies
{
using Int = std::underlying_type_t<TimeSyncStrategy>;
public:
constexpr TimeSyncStrategies() noexcept
: m_value(0)
{
}
constexpr TimeSyncStrategies(TimeSyncStrategy strategy) noexcept
: m_value(static_cast<Int>(strategy))
{
}
explicit constexpr TimeSyncStrategies(Int raw) noexcept
: m_value(raw)
{
}
constexpr TimeSyncStrategies operator|(TimeSyncStrategies rhs) const noexcept
{
return TimeSyncStrategies(m_value | rhs.m_value);
}
constexpr TimeSyncStrategies operator&(TimeSyncStrategies rhs) const noexcept
{
return TimeSyncStrategies(m_value & rhs.m_value);
}
constexpr TimeSyncStrategies &operator|=(TimeSyncStrategies rhs) noexcept
{
m_value |= rhs.m_value;
return *this;
}
constexpr bool testFlag(TimeSyncStrategy strategy) const noexcept
{
const auto bit = static_cast<Int>(strategy);
return bit != 0 && (m_value & bit) == bit;
}
constexpr TimeSyncStrategies &setFlag(TimeSyncStrategy strategy, bool enabled = true) noexcept
{
const auto bit = static_cast<Int>(strategy);
if (enabled)
m_value |= bit;
else
m_value &= ~bit;
return *this;
}
constexpr Int toInt() const noexcept
{
return m_value;
}
private:
Int m_value;
};
constexpr TimeSyncStrategies operator|(TimeSyncStrategy lhs, TimeSyncStrategy rhs) noexcept
{
return TimeSyncStrategies(lhs) | TimeSyncStrategies(rhs);
}
const std::string timeSyncStrategyToHString(const TimeSyncStrategy &strategy);
const std::string timeSyncStrategiesToHString(const TimeSyncStrategies &strategies);
} // namespace Syntalos
namespace Syntalos
{
using SyncDetailsChangeNotifyFn =
std::function<void(const std::string &id, const TimeSyncStrategies &strategies, const microseconds_t &tolerance)>;
using OffsetChangeNotifyFn = std::function<void(const std::string &id, const microseconds_t ¤tOffset)>;
class FreqCounterSynchronizer
{
public:
explicit FreqCounterSynchronizer(
std::shared_ptr<SyncTimer> masterTimer,
const std::string &modName,
double frequencyHz,
const std::string &id = {});
~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 std::string &fname, const Uuid &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);
FreqCounterSynchronizer(const FreqCounterSynchronizer &) = delete;
FreqCounterSynchronizer &operator=(const FreqCounterSynchronizer &) = delete;
private:
std::string m_modName;
Uuid m_collectionId;
std::string 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;
int64_t m_runningOffsetSum{0}; // running sum of 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 std::string &modName,
const std::string &id = {});
~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 std::string &fname, const Uuid &collectionId);
bool isCalibrated() const;
microseconds_t expectedOffsetToMaster() const;
bool start();
void stop();
void processTimestamp(microseconds_t &masterTimestamp, const microseconds_t &secondaryAcqTimestamp);
SecondaryClockSynchronizer(const SecondaryClockSynchronizer &) = delete;
SecondaryClockSynchronizer &operator=(const SecondaryClockSynchronizer &) = delete;
private:
void emitSyncDetailsChanged();
std::string m_modName;
Uuid m_collectionId;
std::string 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;
int64_t m_runningOffsetSum{0}; // running sum of 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 2026-04-24 at 23:36:58 +0000