#ifndef TXLOOP_H #define TXLOOP_H #include "TwoPhaseSignal.h" #include "Varicode.h" #include #include class TxLoop : public TwoPhaseSignal { Q_OBJECT private: // Name, only used for logging: const QString m_name; // When do we need to start transmitting the next time? QDateTime m_next_activity; // How much tx_delay, in milliseconds, we need to be early. qint64 m_tx_delay_ms; // A timer that is supposed to wake us up when it is time. // This will be m_tx_delay_ms earlier than m_next_activity. QTimer m_next_activity_timer; // The currently active JS8 m_submode. Varicode::SubmodeType m_submode; // Whether this loop is active (or else does nothing). bool m_active; // The loop's period: After how many milliseconds to repeat sending. qint64 m_loop_period_ms; public: // Provide a "name" argument that can be used for logging. TxLoop(const QString &name); ~TxLoop(); // The TX delay cannot be set to any value larger than this: static constexpr qint64 MAX_TX_DELAY_MS = 1000; inline bool isActive() const { return m_active; } /** * Return the timestamp when the payload signal is supposed to start. * This timestamp will always point to a full second. * The actual triggering happens tx_delay earlier. * * The value this returns is undetermined (arbitrary) when called while * isActive() returns false. */ inline const QDateTime &nextActivity() const { return m_next_activity; } /** * Return the present period of the loop, in milliseconds, if isActive() * returns true. * * Not sure this is useful, but: If isActive() returns false, * this will hand out the previous period used, or, * if this loop object has never been active, some reasonable initial value. */ inline qint64 period_ms() const { return m_loop_period_ms; } signals: /** Tells whoever wants to know that now is the time to push the PTT for TX * delay: */ void triggerTxNow() const; /** * Tells whoever wants to know that our future plans have changed. Only an * active loop will emit this. * * This signal is emitted in conjunction with triggerTxNow and also each * time our onTxLoopPeriodChangeStart receives a signal. * * The QDateTime sent is the same that will be returned by nextActivity(). * * In addition, it gets fired when some other incoming onXXX signal caused * our next activity to change. */ void nextActivityChanged(const QDateTime &) const; /** Tells whoever wants to know that we have become inactive. */ void canceled() const; public slots: /** Asks us to emit initial signals, either nextActivityChanged or, more * likely, canceled. */ void onPlumbingCompleted() const; /** Tells us that the DriftingDateTime has a new drift value. We'll adjust * accordingly. Idempotent. */ void onDriftChange(qint64 new_drift); /** Tells us that the next transmission organized by this loop will be in * this new submode. Idempotent. */ void onModeChange(Varicode::SubmodeType new_submode); /** * Tells us that the desired TX delay * (switch TX on a bit before the full second that starts the mode period) * has changed. Idempotent. */ void onTxDelayChange(qint64 tx_delay_ms); /** * This starts or restarts the loop. * You need to provide the desired period. * * After this signal has come in, isActive() will return true * and triggerTxNow will fire every loop_period_ms milliseconds. * This continues until the onLoopCancel signal is received. * * Calling this on a loop that is active will cause the schedule * to be reset; i.e., this restarts the loop. */ void onTxLoopPeriodChangeStart(qint64 loop_period_ms); /** * Cancel the loop. After this signal has come in, isActive() will return * false and triggerTxNow will not fire any longer. Loop activity will * restart when a onTxLoopPeriodChangeStart signal is again received. * * This is idempotent, it does no harm to call this on an idle loop. */ void onLoopCancel(); private slots: void onTimer(); }; #endif