diff --git a/src/Defines.h b/src/Defines.h
index 10608b10..92469319 100644
--- a/src/Defines.h
+++ b/src/Defines.h
@@ -109,7 +109,7 @@ typedef unsigned long long ulong64_t;
#define __PROG_NAME__ "Digital Voice Modem (DVM) Host"
#define __NET_NAME__ "DVM_DMR_P25"
#define __EXE_NAME__ "dvmhost"
-#define __VER__ "D03.50.00 (" __GIT_VER__ ")"
+#define __VER__ "D03.55.00 (" __GIT_VER__ ")"
#define __BUILD__ __DATE__ " " __TIME__
#define HOST_SW_API
diff --git a/src/Thread.cpp b/src/Thread.cpp
index a5bf9119..a7db173e 100644
--- a/src/Thread.cpp
+++ b/src/Thread.cpp
@@ -12,6 +12,7 @@
//
/*
* Copyright (C) 2015,2016 by Jonathan Naylor G4KLX
+* Copyright (C) 2023 by Bryan Biedenkapp N2PLL
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -39,7 +40,8 @@
/// Initializes a new instance of the Thread class.
///
Thread::Thread() :
- m_thread()
+ m_thread(),
+ m_started(false)
{
/* stub */
}
@@ -58,6 +60,10 @@ Thread::~Thread()
/// True, if thread started, otherwise false.
bool Thread::run()
{
+ if (m_started)
+ return m_started;
+
+ m_started = true;
return ::pthread_create(&m_thread, NULL, helper, this) == 0;
}
@@ -90,7 +96,6 @@ void Thread::sleep(uint32_t ms)
void* Thread::helper(void* arg)
{
Thread* p = (Thread*)arg;
-
p->entry();
return nullptr;
diff --git a/src/Thread.h b/src/Thread.h
index c58f3c32..593be795 100644
--- a/src/Thread.h
+++ b/src/Thread.h
@@ -12,6 +12,7 @@
//
/*
* Copyright (C) 2015,2016 by Jonathan Naylor G4KLX
+* Copyright (C) 2023 by Bryan Biedenkapp N2PLL
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -63,6 +64,10 @@ private:
///
static void* helper(void* arg);
+
+public:
+ /// Flag indicating if the thread was started.
+ __READONLY_PROPERTY_PLAIN(bool, started, started);
};
#endif // __THREAD_H__
diff --git a/src/ThreadFunc.h b/src/ThreadFunc.h
new file mode 100644
index 00000000..94960a12
--- /dev/null
+++ b/src/ThreadFunc.h
@@ -0,0 +1,64 @@
+/**
+* Digital Voice Modem - Host Software
+* GPLv2 Open Source. Use is subject to license terms.
+* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+*
+* @package DVM / Host Software
+*
+*/
+//
+// Based on code from the MMDVMHost project. (https://github.com/g4klx/MMDVMHost)
+// Licensed under the GPLv2 License (https://opensource.org/licenses/GPL-2.0)
+//
+/*
+* Copyright (C) 2023 by Bryan Biedenkapp N2PLL
+*
+* This program is free software; you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation; either version 2 of the License, or
+* (at your option) any later version.
+*
+* This program 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 General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with this program; if not, write to the Free Software
+* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+#if !defined(__THREAD_FUNC_H__)
+#define __THREAD_FUNC_H__
+
+#include "Defines.h"
+#include "Thread.h"
+
+#include
+#include
+
+// ---------------------------------------------------------------------------
+// Class Declaration
+// Implements a simple function threading mechanism.
+// ---------------------------------------------------------------------------
+
+class HOST_SW_API ThreadFunc : public Thread {
+public:
+ /// Initializes a new instance of the ThreadFunc class.
+ ThreadFunc(std::function&& e) : Thread(),
+ m_entry(e)
+ {
+ assert(e != nullptr);
+ }
+
+ /// User-defined function to run for the thread main.
+ virtual void entry()
+ {
+ if (m_entry != nullptr)
+ m_entry();
+ }
+
+private:
+ std::function m_entry;
+};
+
+#endif // __THREAD_FUNC_H__
diff --git a/src/host/Host.DMR.cpp b/src/host/Host.DMR.cpp
new file mode 100644
index 00000000..a8113860
--- /dev/null
+++ b/src/host/Host.DMR.cpp
@@ -0,0 +1,314 @@
+/**
+* Digital Voice Modem - Host Software
+* GPLv2 Open Source. Use is subject to license terms.
+* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+*
+* @package DVM / Host Software
+*
+*/
+//
+// Based on code from the MMDVMHost project. (https://github.com/g4klx/MMDVMHost)
+// Licensed under the GPLv2 License (https://opensource.org/licenses/GPL-2.0)
+//
+/*
+* Copyright (C) 2017-2023 by Bryan Biedenkapp N2PLL
+*
+* This program is free software; you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation; either version 2 of the License, or
+* (at your option) any later version.
+*
+* This program 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 General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with this program; if not, write to the Free Software
+* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+#include "Defines.h"
+#include "host/Host.h"
+
+using namespace modem;
+
+// ---------------------------------------------------------------------------
+// Macros
+// ---------------------------------------------------------------------------
+
+// Macro to start DMR duplex idle transmission (or beacon)
+#define START_DMR_DUPLEX_IDLE(x) \
+ if (control != nullptr) { \
+ if (m_duplex && !m_dmrTXTimer.isRunning()) { \
+ m_modem->writeDMRStart(x); \
+ m_dmrTXTimer.start(); \
+ } \
+ }
+
+// ---------------------------------------------------------------------------
+// Private Class Members
+// ---------------------------------------------------------------------------
+
+///
+/// Helper to interrupt a running DMR beacon.
+///
+///
+void Host::interruptDMRBeacon(dmr::Control* control)
+{
+ if (control != nullptr) {
+ if (m_dmrBeaconDurationTimer.isRunning() && !m_dmrBeaconDurationTimer.hasExpired()) {
+ if (m_dmrTSCCData && !m_dmrCtrlChannel) {
+ LogDebug(LOG_HOST, "interrupt DMR control, m_state = %u", m_state);
+ control->setCCHalted(true);
+ control->setCCRunning(false);
+ }
+ }
+
+ m_dmrBeaconDurationTimer.stop();
+ }
+}
+
+///
+/// Helper to read DMR slot 1 frames from modem.
+///
+///
+///
+void Host::readFramesDMR1(dmr::Control* control, std::function&& afterReadCallback)
+{
+ uint8_t data[dmr::DMR_FRAME_LENGTH_BYTES * 2U];
+
+ if (control != nullptr) {
+ // read DMR slot 1 frames from the modem, and if there is any
+ // write those frames to the DMR controller
+ uint32_t len = m_modem->readDMRFrame1(data);
+ if (len > 0U) {
+ if (m_state == STATE_IDLE) {
+ // if the modem is in duplex -- process wakeup CSBKs
+ if (m_duplex) {
+ bool ret = control->processWakeup(data);
+ if (ret) {
+ m_modeTimer.setTimeout(m_rfModeHang);
+ setState(STATE_DMR);
+
+ START_DMR_DUPLEX_IDLE(true);
+
+ if (afterReadCallback != nullptr) {
+ afterReadCallback();
+ }
+ }
+ }
+ else {
+ // in simplex directly process slot 1 frames
+ m_modeTimer.setTimeout(m_rfModeHang);
+ setState(STATE_DMR);
+ START_DMR_DUPLEX_IDLE(true);
+
+ control->processFrame(1U, data, len);
+
+ if (afterReadCallback != nullptr) {
+ afterReadCallback();
+ }
+ }
+ }
+ else if (m_state == STATE_DMR) {
+ // if the modem is in duplex, and hasn't started transmitting
+ // process wakeup CSBKs
+ if (m_duplex && !m_modem->hasTX()) {
+ bool ret = control->processWakeup(data);
+ if (ret) {
+ m_modem->writeDMRStart(true);
+ m_dmrTXTimer.start();
+ }
+ }
+ else {
+ // process slot 1 frames
+ bool ret = control->processFrame(1U, data, len);
+ if (ret) {
+ if (afterReadCallback != nullptr) {
+ afterReadCallback();
+ }
+
+ m_modeTimer.start();
+ if (m_duplex)
+ m_dmrTXTimer.start();
+ }
+ }
+ }
+ else if (m_state != HOST_STATE_LOCKOUT) {
+ LogWarning(LOG_HOST, "DMR modem data received, state = %u", m_state);
+ }
+ }
+ }
+}
+
+///
+/// Helper to write DMR slot 1 frames to modem.
+///
+///
+///
+void Host::writeFramesDMR1(dmr::Control* control, std::function&& afterWriteCallback)
+{
+ uint8_t data[dmr::DMR_FRAME_LENGTH_BYTES * 2U];
+
+ if (control != nullptr) {
+ // check if there is space on the modem for DMR slot 1 frames,
+ // if there is read frames from the DMR controller and write it
+ // to the modem
+ bool ret = m_modem->hasDMRSpace1();
+ if (ret) {
+ uint32_t len = control->getFrame(1U, data);
+ if (len > 0U) {
+ // if the state is idle; set to DMR, start mode timer and start DMR idle frames
+ if (m_state == STATE_IDLE) {
+ m_modeTimer.setTimeout(m_netModeHang);
+ setState(STATE_DMR);
+ START_DMR_DUPLEX_IDLE(true);
+ }
+
+ // if the state is DMR; start DMR idle frames and write DMR slot 1 data
+ if (m_state == STATE_DMR) {
+ START_DMR_DUPLEX_IDLE(true);
+
+ m_modem->writeDMRFrame1(data, len);
+
+ // if there is no DMR CC running; run the interrupt macro to stop
+ // any running DMR beacon
+ if (!control->getCCRunning()) {
+ interruptDMRBeacon(control);
+ }
+
+ if (afterWriteCallback != nullptr) {
+ afterWriteCallback();
+ }
+
+ m_modeTimer.start();
+ }
+
+ m_lastDstId = control->getLastDstId(1U);
+ m_lastSrcId = control->getLastSrcId(1U);
+ }
+ }
+ }
+}
+
+///
+/// Helper to read DMR slot 2 frames from modem.
+///
+///
+///
+void Host::readFramesDMR2(dmr::Control* control, std::function&& afterReadCallback)
+{
+ uint8_t data[dmr::DMR_FRAME_LENGTH_BYTES * 2U];
+
+ if (control != nullptr) {
+ // read DMR slot 2 frames from the modem, and if there is any
+ // write those frames to the DMR controller
+ uint32_t len = m_modem->readDMRFrame2(data);
+ if (len > 0U) {
+ if (m_state == STATE_IDLE) {
+ // if the modem is in duplex -- process wakeup CSBKs
+ if (m_duplex) {
+ bool ret = control->processWakeup(data);
+ if (ret) {
+ m_modeTimer.setTimeout(m_rfModeHang);
+ setState(STATE_DMR);
+ START_DMR_DUPLEX_IDLE(true);
+
+ if (afterReadCallback != nullptr) {
+ afterReadCallback();
+ }
+ }
+ }
+ else {
+ // in simplex -- directly process slot 2 frames
+ m_modeTimer.setTimeout(m_rfModeHang);
+ setState(STATE_DMR);
+ START_DMR_DUPLEX_IDLE(true);
+
+ control->processFrame(2U, data, len);
+
+ if (afterReadCallback != nullptr) {
+ afterReadCallback();
+ }
+ }
+ }
+ else if (m_state == STATE_DMR) {
+ // if the modem is in duplex, and hasn't started transmitting
+ // process wakeup CSBKs
+ if (m_duplex && !m_modem->hasTX()) {
+ bool ret = control->processWakeup(data);
+ if (ret) {
+ m_modem->writeDMRStart(true);
+ m_dmrTXTimer.start();
+ }
+ }
+ else {
+ // process slot 2 frames
+ bool ret = control->processFrame(2U, data, len);
+ if (ret) {
+ if (afterReadCallback != nullptr) {
+ afterReadCallback();
+ }
+
+ m_modeTimer.start();
+ if (m_duplex)
+ m_dmrTXTimer.start();
+ }
+ }
+ }
+ else if (m_state != HOST_STATE_LOCKOUT) {
+ LogWarning(LOG_HOST, "DMR modem data received, state = %u", m_state);
+ }
+ }
+ }
+}
+
+///
+/// Helper to write DMR slot 2 frames to modem.
+///
+///
+///
+void Host::writeFramesDMR2(dmr::Control* control, std::function&& afterWriteCallback)
+{
+ uint8_t data[dmr::DMR_FRAME_LENGTH_BYTES * 2U];
+
+ if (control != nullptr) {
+ // check if there is space on the modem for DMR slot 2 frames,
+ // if there is read frames from the DMR controller and write it
+ // to the modem
+ bool ret = m_modem->hasDMRSpace2();
+ if (ret) {
+ uint32_t len = control->getFrame(2U, data);
+ if (len > 0U) {
+ // if the state is idle; set to DMR, start mode timer and start DMR idle frames
+ if (m_state == STATE_IDLE) {
+ m_modeTimer.setTimeout(m_netModeHang);
+ setState(STATE_DMR);
+ START_DMR_DUPLEX_IDLE(true);
+ }
+
+ // if the state is DMR; start DMR idle frames and write DMR slot 2 data
+ if (m_state == STATE_DMR) {
+ START_DMR_DUPLEX_IDLE(true);
+
+ m_modem->writeDMRFrame2(data, len);
+
+ // if there is no DMR CC running; run the interrupt macro to stop
+ // any running DMR beacon
+ if (!control->getCCRunning()) {
+ interruptDMRBeacon(control);
+ }
+
+ if (afterWriteCallback != nullptr) {
+ afterWriteCallback();
+ }
+
+ m_modeTimer.start();
+ }
+
+ m_lastDstId = control->getLastDstId(2U);
+ m_lastSrcId = control->getLastSrcId(2U);
+ }
+ }
+ }
+}
diff --git a/src/host/Host.NXDN.cpp b/src/host/Host.NXDN.cpp
new file mode 100644
index 00000000..28fc529c
--- /dev/null
+++ b/src/host/Host.NXDN.cpp
@@ -0,0 +1,132 @@
+/**
+* Digital Voice Modem - Host Software
+* GPLv2 Open Source. Use is subject to license terms.
+* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+*
+* @package DVM / Host Software
+*
+*/
+//
+// Based on code from the MMDVMHost project. (https://github.com/g4klx/MMDVMHost)
+// Licensed under the GPLv2 License (https://opensource.org/licenses/GPL-2.0)
+//
+/*
+* Copyright (C) 2017-2023 by Bryan Biedenkapp N2PLL
+*
+* This program is free software; you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation; either version 2 of the License, or
+* (at your option) any later version.
+*
+* This program 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 General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with this program; if not, write to the Free Software
+* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+#include "Defines.h"
+#include "host/Host.h"
+
+using namespace modem;
+
+// ---------------------------------------------------------------------------
+// Private Class Members
+// ---------------------------------------------------------------------------
+
+///
+/// Helper to interrupt a running NXDN control channel.
+///
+///
+void Host::interruptNXDNControl(nxdn::Control* control)
+{
+ if (control != nullptr) {
+ LogDebug(LOG_HOST, "interrupt NXDN control, m_state = %u", m_state);
+ control->setCCHalted(true);
+
+ if (m_nxdnBcastDurationTimer.isRunning() && !m_nxdnBcastDurationTimer.isPaused()) {
+ m_nxdnBcastDurationTimer.pause();
+ }
+ }
+}
+
+///
+/// Helper to read NXDN frames from modem.
+///
+///
+///
+void Host::readFramesNXDN(nxdn::Control* control, std::function&& afterReadCallback)
+{
+ uint8_t data[nxdn::NXDN_FRAME_LENGTH_BYTES * 2U];
+
+ if (control != nullptr) {
+ uint32_t len = m_modem->readNXDNFrame(data);
+ if (len > 0U) {
+ if (m_state == STATE_IDLE) {
+ // process NXDN frames
+ bool ret = control->processFrame(data, len);
+ if (ret) {
+ m_modeTimer.setTimeout(m_rfModeHang);
+ setState(STATE_NXDN);
+
+ if (afterReadCallback != nullptr) {
+ afterReadCallback();
+ }
+ }
+ }
+ else if (m_state == STATE_NXDN) {
+ // process NXDN frames
+ bool ret = control->processFrame(data, len);
+ if (ret) {
+ m_modeTimer.start();
+ }
+ }
+ else if (m_state != HOST_STATE_LOCKOUT) {
+ LogWarning(LOG_HOST, "NXDN modem data received, state = %u", m_state);
+ }
+ }
+ }
+}
+
+///
+/// Helper to write NXDN frames to modem.
+///
+///
+///
+void Host::writeFramesNXDN(nxdn::Control* control, std::function&& afterWriteCallback)
+{
+ uint8_t data[nxdn::NXDN_FRAME_LENGTH_BYTES * 2U];
+
+ // check if there is space on the modem for NXDN frames,
+ // if there is read frames from the NXDN controller and write it
+ // to the modem
+ if (control != nullptr) {
+ bool ret = m_modem->hasNXDNSpace();
+ if (ret) {
+ uint32_t len = control->getFrame(data);
+ if (len > 0U) {
+ // if the state is idle; set to NXDN and start mode timer
+ if (m_state == STATE_IDLE) {
+ m_modeTimer.setTimeout(m_netModeHang);
+ setState(STATE_NXDN);
+ }
+
+ // if the state is NXDN; write NXDN data
+ if (m_state == STATE_NXDN) {
+ m_modem->writeNXDNFrame(data, len);
+
+ if (afterWriteCallback != nullptr) {
+ afterWriteCallback();
+ }
+
+ m_modeTimer.start();
+ }
+
+ m_lastDstId = control->getLastDstId();
+ m_lastSrcId = control->getLastSrcId();
+ }
+ }
+ }
+}
diff --git a/src/host/Host.P25.cpp b/src/host/Host.P25.cpp
new file mode 100644
index 00000000..6c61dca4
--- /dev/null
+++ b/src/host/Host.P25.cpp
@@ -0,0 +1,220 @@
+/**
+* Digital Voice Modem - Host Software
+* GPLv2 Open Source. Use is subject to license terms.
+* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+*
+* @package DVM / Host Software
+*
+*/
+//
+// Based on code from the MMDVMHost project. (https://github.com/g4klx/MMDVMHost)
+// Licensed under the GPLv2 License (https://opensource.org/licenses/GPL-2.0)
+//
+/*
+* Copyright (C) 2017-2023 by Bryan Biedenkapp N2PLL
+*
+* This program is free software; you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation; either version 2 of the License, or
+* (at your option) any later version.
+*
+* This program 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 General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with this program; if not, write to the Free Software
+* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+#include "Defines.h"
+#include "host/Host.h"
+#include "HostMain.h"
+
+using namespace modem;
+
+// ---------------------------------------------------------------------------
+// Private Class Members
+// ---------------------------------------------------------------------------
+
+///
+/// Helper to interrupt a running P25 control channel.
+///
+///
+void Host::interruptP25Control(p25::Control* control)
+{
+ if (control != nullptr) {
+ LogDebug(LOG_HOST, "interrupt P25 control, m_state = %u", m_state);
+ control->setCCHalted(true);
+
+ if (m_p25BcastDurationTimer.isRunning() && !m_p25BcastDurationTimer.isPaused()) {
+ m_p25BcastDurationTimer.pause();
+ }
+ }
+}
+
+///
+/// Helper to read P25 frames from modem.
+///
+///
+///
+void Host::readFramesP25(p25::Control* control, std::function&& afterReadCallback)
+{
+ uint8_t data[p25::P25_LDU_FRAME_LENGTH_BYTES * 2U];
+
+ // read P25 frames from modem, and if there are frames
+ // write those frames to the P25 controller
+ if (control != nullptr) {
+ uint32_t len = m_modem->readP25Frame(data);
+ if (len > 0U) {
+ if (m_state == STATE_IDLE) {
+ // process P25 frames
+ bool ret = control->processFrame(data, len);
+ if (ret) {
+ m_modeTimer.setTimeout(m_rfModeHang);
+ setState(STATE_P25);
+
+ if (afterReadCallback != nullptr) {
+ afterReadCallback();
+ }
+ }
+ else {
+ ret = control->writeRF_VoiceEnd();
+ if (ret) {
+ if (afterReadCallback != nullptr) {
+ afterReadCallback();
+ }
+
+ if (m_state == STATE_IDLE) {
+ m_modeTimer.setTimeout(m_rfModeHang);
+ setState(STATE_P25);
+ }
+
+ if (m_state == STATE_P25) {
+ m_modeTimer.start();
+ }
+
+ // if the modem is in duplex -- handle P25 CC burst control
+ if (m_duplex) {
+ if (m_p25BcastDurationTimer.isPaused() && !control->getCCHalted()) {
+ m_p25BcastDurationTimer.resume();
+ }
+
+ if (control->getCCHalted()) {
+ control->setCCHalted(false);
+ }
+
+ if (g_fireP25Control) {
+ m_modeTimer.stop();
+ }
+ }
+ else {
+ m_p25BcastDurationTimer.stop();
+ }
+ }
+ }
+ }
+ else if (m_state == STATE_P25) {
+ // process P25 frames
+ bool ret = control->processFrame(data, len);
+ if (ret) {
+ m_modeTimer.start();
+ }
+ else {
+ ret = control->writeRF_VoiceEnd();
+ if (ret) {
+ m_modeTimer.start();
+ }
+ }
+ }
+ else if (m_state != HOST_STATE_LOCKOUT) {
+ LogWarning(LOG_HOST, "P25 modem data received, state = %u", m_state);
+ }
+ }
+ }
+}
+
+///
+/// Helper to write P25 frames to modem.
+///
+///
+///
+void Host::writeFramesP25(p25::Control* control, std::function&& afterWriteCallback)
+{
+ uint8_t data[p25::P25_LDU_FRAME_LENGTH_BYTES * 2U];
+
+ // check if there is space on the modem for P25 frames,
+ // if there is read frames from the P25 controller and write it
+ // to the modem
+ if (control != nullptr) {
+ uint8_t nextLen = control->peekFrameLength();
+ if (nextLen > 0U) {
+ bool ret = m_modem->hasP25Space(nextLen);
+ if (ret) {
+ uint32_t len = control->getFrame(data);
+ if (len > 0U) {
+ // if the state is idle; set to P25 and start mode timer
+ if (m_state == STATE_IDLE) {
+ m_modeTimer.setTimeout(m_netModeHang);
+ setState(STATE_P25);
+ }
+
+ // if the state is P25; write P25 frame data
+ if (m_state == STATE_P25) {
+ m_modem->writeP25Frame(data, len);
+
+ if (afterWriteCallback != nullptr) {
+ afterWriteCallback();
+ }
+
+ m_modeTimer.start();
+ }
+
+ m_lastDstId = control->getLastDstId();
+ m_lastSrcId = control->getLastSrcId();
+ }
+ else {
+ nextLen = 0U;
+ }
+ }
+ }
+
+ if (nextLen == 0U) {
+ // if we have no P25 data, and we're either idle or P25 state, check if we
+ // need to be starting the CC running flag or writing end of voice call data
+ if (m_state == STATE_IDLE || m_state == STATE_P25) {
+ if (control->getCCHalted()) {
+ control->setCCHalted(false);
+ }
+
+ // write end of voice if necessary
+ bool ret = control->writeRF_VoiceEnd();
+ if (ret) {
+ if (m_state == STATE_IDLE) {
+ m_modeTimer.setTimeout(m_netModeHang);
+ setState(STATE_P25);
+ }
+
+ if (m_state == STATE_P25) {
+ m_modeTimer.start();
+ }
+ }
+ }
+ }
+
+ // if the modem is in duplex -- handle P25 CC burst control
+ if (m_duplex) {
+ if (m_p25BcastDurationTimer.isPaused() && !control->getCCHalted()) {
+ m_p25BcastDurationTimer.resume();
+ }
+
+ if (control->getCCHalted()) {
+ control->setCCHalted(false);
+ }
+
+ if (g_fireP25Control) {
+ m_modeTimer.stop();
+ }
+ }
+ }
+}
diff --git a/src/host/Host.cpp b/src/host/Host.cpp
index e351876d..d69545c4 100644
--- a/src/host/Host.cpp
+++ b/src/host/Host.cpp
@@ -30,11 +30,8 @@
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "Defines.h"
-#include "dmr/Control.h"
#include "dmr/DMRUtils.h"
-#include "p25/Control.h"
#include "p25/P25Utils.h"
-#include "nxdn/Control.h"
#include "modem/port/ModemNullPort.h"
#include "modem/port/UARTPort.h"
#include "modem/port/PseudoPTYPort.h"
@@ -46,6 +43,7 @@
#include "Log.h"
#include "StopWatch.h"
#include "Thread.h"
+#include "ThreadFunc.h"
#include "Utils.h"
using namespace network;
@@ -141,6 +139,9 @@ Host::Host(const std::string& confFile) :
m_nxdnQueueSizeBytes(1488U), // 31 frames
m_authoritative(true),
m_supervisor(false),
+ m_dmrBeaconDurationTimer(1000U),
+ m_p25BcastDurationTimer(1000U),
+ m_nxdnBcastDurationTimer(1000U),
m_activeTickDelay(5U),
m_idleTickDelay(5U),
m_RESTAPI(nullptr)
@@ -358,7 +359,6 @@ int Host::run()
// initialize DMR
Timer dmrBeaconIntervalTimer(1000U);
- Timer dmrBeaconDurationTimer(1000U);
std::unique_ptr dmr = nullptr;
#if defined(ENABLE_DMR)
@@ -410,7 +410,7 @@ int Host::run()
LogInfo(" Roaming Beacon Interval: %us", dmrBeaconInterval);
LogInfo(" Roaming Beacon Duration: %us", dmrBeaconDuration);
- dmrBeaconDurationTimer.setTimeout(dmrBeaconDuration);
+ m_dmrBeaconDurationTimer.setTimeout(dmrBeaconDuration);
dmrBeaconIntervalTimer.setTimeout(dmrBeaconInterval);
dmrBeaconIntervalTimer.start();
@@ -452,7 +452,6 @@ int Host::run()
// initialize P25
Timer p25BcastIntervalTimer(1000U);
- Timer p25BcastDurationTimer(1000U);
std::unique_ptr p25 = nullptr;
#if defined(ENABLE_P25)
@@ -492,7 +491,7 @@ int Host::run()
LogInfo(" Control Broadcast Duration: %us", p25ControlBcstDuration);
}
- p25BcastDurationTimer.setTimeout(p25ControlBcstDuration);
+ m_p25BcastDurationTimer.setTimeout(p25ControlBcstDuration);
p25BcastIntervalTimer.setTimeout(p25ControlBcstInterval);
p25BcastIntervalTimer.start();
@@ -524,7 +523,6 @@ int Host::run()
// initialize NXDN
Timer nxdnBcastIntervalTimer(1000U);
- Timer nxdnBcastDurationTimer(1000U);
std::unique_ptr nxdn = nullptr;
#if defined(ENABLE_NXDN)
@@ -557,7 +555,7 @@ int Host::run()
LogInfo(" Control Broadcast Duration: %us", nxdnControlBcstDuration);
}
- nxdnBcastDurationTimer.setTimeout(nxdnControlBcstDuration);
+ m_nxdnBcastDurationTimer.setTimeout(nxdnControlBcstDuration);
nxdnBcastIntervalTimer.setTimeout(nxdnControlBcstInterval);
nxdnBcastIntervalTimer.start();
@@ -758,39 +756,6 @@ int Host::run()
bool hasTxShutdown = false;
- // Macro to interrupt a running P25 control channel transmission
- #define INTERRUPT_P25_CONTROL \
- if (p25 != nullptr) { \
- LogDebug(LOG_HOST, "interrupt P25 control, m_state = %u", m_state); \
- p25->setCCHalted(true); \
- if (p25BcastDurationTimer.isRunning() && !p25BcastDurationTimer.isPaused()) { \
- p25BcastDurationTimer.pause(); \
- } \
- }
-
- // Macro to interrupt a running DMR roaming beacon
- #define INTERRUPT_DMR_BEACON \
- if (dmr != nullptr) { \
- if (dmrBeaconDurationTimer.isRunning() && !dmrBeaconDurationTimer.hasExpired()) { \
- if (m_dmrTSCCData && !m_dmrCtrlChannel) { \
- LogDebug(LOG_HOST, "interrupt DMR control, m_state = %u", m_state); \
- dmr->setCCHalted(true); \
- dmr->setCCRunning(false); \
- } \
- } \
- dmrBeaconDurationTimer.stop(); \
- }
-
- // Macro to interrupt a running NXDN control channel transmission
- #define INTERRUPT_NXDN_CONTROL \
- if (nxdn != nullptr) { \
- LogDebug(LOG_HOST, "interrupt NXDN control, m_state = %u", m_state); \
- nxdn->setCCHalted(true); \
- if (nxdnBcastDurationTimer.isRunning() && !nxdnBcastDurationTimer.isPaused()) { \
- nxdnBcastDurationTimer.pause(); \
- } \
- }
-
// Macro to start DMR duplex idle transmission (or beacon)
#define START_DMR_DUPLEX_IDLE(x) \
if (dmr != nullptr) { \
@@ -800,519 +765,256 @@ int Host::run()
} \
}
- // main execution loop
- while (!killed) {
- if (m_modem->hasLockout() && m_state != HOST_STATE_LOCKOUT)
- setState(HOST_STATE_LOCKOUT);
- else if (!m_modem->hasLockout() && m_state == HOST_STATE_LOCKOUT)
- setState(STATE_IDLE);
-
- if (m_modem->hasError() && m_state != HOST_STATE_ERROR)
- setState(HOST_STATE_ERROR);
- else if (!m_modem->hasError() && m_state == HOST_STATE_ERROR)
- setState(STATE_IDLE);
-
- uint32_t ms = stopWatch.elapsed();
- if (ms > 1U)
- m_modem->clock(ms);
-
- uint8_t data[220U];
- uint32_t len;
- bool ret;
-
- if (!m_fixedMode) {
- if (m_modeTimer.isRunning() && m_modeTimer.hasExpired()) {
- setState(STATE_IDLE);
- }
- }
- else {
- m_modeTimer.stop();
- if (dmr != nullptr && m_state != STATE_DMR && !m_modem->hasTX()) {
- LogDebug(LOG_HOST, "fixed mode state abnormal, m_state = %u, state = %u", m_state, STATE_DMR);
- setState(STATE_DMR);
- }
- if (p25 != nullptr && m_state != STATE_P25 && !m_modem->hasTX()) {
- LogDebug(LOG_HOST, "fixed mode state abnormal, m_state = %u, state = %u", m_state, STATE_P25);
- setState(STATE_P25);
- }
- if (nxdn != nullptr && m_state != STATE_NXDN && !m_modem->hasTX()) {
- LogDebug(LOG_HOST, "fixed mode state abnormal, m_state = %u, state = %u", m_state, STATE_NXDN);
- setState(STATE_NXDN);
- }
- }
-
- // ------------------------------------------------------
- // -- Write to Modem Processing --
- // ------------------------------------------------------
-
- /** Digital Mobile Radio */
+ // setup protocol processor threads
+ /** Digital Mobile Radio */
+ ThreadFunc dmrProcessThread([&, this]() {
#if defined(ENABLE_DMR)
if (dmr != nullptr) {
- // check if there is space on the modem for DMR slot 1 frames,
- // if there is read frames from the DMR controller and write it
- // to the modem
- ret = m_modem->hasDMRSpace1();
- if (ret) {
- len = dmr->getFrame(1U, data);
- if (len > 0U) {
- // if the state is idle; set to DMR, start mode timer and start DMR idle frames
- if (m_state == STATE_IDLE) {
- m_modeTimer.setTimeout(m_netModeHang);
- setState(STATE_DMR);
- START_DMR_DUPLEX_IDLE(true);
+ LogDebug(LOG_HOST, "DMR, started frame processor");
+ while (!g_killed) {
+ // ------------------------------------------------------
+ // -- Write to Modem Processing --
+ // ------------------------------------------------------
+
+ // write DMR slot 1 frames to modem
+ writeFramesDMR1(dmr.get(), [&, this]() {
+ // if there is a P25 CC running; halt the CC
+ if (p25 != nullptr) {
+ if (p25->getCCRunning() && !p25->getCCHalted()) {
+ this->interruptP25Control(p25.get());
+ }
}
- // if the state is DMR; start DMR idle frames and write DMR slot 1 data
- if (m_state == STATE_DMR) {
- START_DMR_DUPLEX_IDLE(true);
-
- m_modem->writeDMRFrame1(data, len);
-
- // if there is no DMR CC running; run the interrupt macro to stop
- // any running DMR beacon
- if (!dmr->getCCRunning()) {
- INTERRUPT_DMR_BEACON;
+ // if there is a NXDN CC running; halt the CC
+ if (nxdn != nullptr) {
+ if (nxdn->getCCRunning() && !nxdn->getCCHalted()) {
+ this->interruptNXDNControl(nxdn.get());
}
-
- // if there is a P25 CC running; halt the CC
- if (p25 != nullptr) {
- if (p25->getCCRunning() && !p25->getCCHalted()) {
- INTERRUPT_P25_CONTROL;
- }
+ }
+ });
+
+ // write DMR slot 2 frames to modem
+ writeFramesDMR2(dmr.get(), [&, this]() {
+ // if there is a P25 CC running; halt the CC
+ if (p25 != nullptr) {
+ if (p25->getCCRunning() && !p25->getCCHalted()) {
+ this->interruptP25Control(p25.get());
}
+ }
- // if there is a NXDN CC running; halt the CC
- if (nxdn != nullptr) {
- if (nxdn->getCCRunning() && !nxdn->getCCHalted()) {
- INTERRUPT_NXDN_CONTROL;
- }
+ // if there is a NXDN CC running; halt the CC
+ if (nxdn != nullptr) {
+ if (nxdn->getCCRunning() && !nxdn->getCCHalted()) {
+ this->interruptNXDNControl(nxdn.get());
}
-
- m_modeTimer.start();
}
+ });
- m_lastDstId = dmr->getLastDstId(1U);
- m_lastSrcId = dmr->getLastSrcId(1U);
- }
- }
+ // ------------------------------------------------------
+ // -- Read from Modem Processing --
+ // ------------------------------------------------------
- // check if there is space on the modem for DMR slot 2 frames,
- // if there is read frames from the DMR controller and write it
- // to the modem
- ret = m_modem->hasDMRSpace2();
- if (ret) {
- len = dmr->getFrame(2U, data);
- if (len > 0U) {
- // if the state is idle; set to DMR, start mode timer and start DMR idle frames
- if (m_state == STATE_IDLE) {
- m_modeTimer.setTimeout(m_netModeHang);
- setState(STATE_DMR);
- START_DMR_DUPLEX_IDLE(true);
+ // read DMR slot 1 frames from modem
+ readFramesDMR1(dmr.get(), [&, this]() {
+ if (dmr != nullptr) {
+ this->interruptDMRBeacon(dmr.get());
}
- // if the state is DMR; start DMR idle frames and write DMR slot 2 data
- if (m_state == STATE_DMR) {
- START_DMR_DUPLEX_IDLE(true);
-
- m_modem->writeDMRFrame2(data, len);
-
- // if there is no DMR CC running; run the interrupt macro to stop
- // any running DMR beacon
- if (!dmr->getCCRunning()) {
- INTERRUPT_DMR_BEACON;
+ // if there is a P25 CC running; halt the CC
+ if (p25 != nullptr) {
+ if (p25->getCCRunning() && !p25->getCCHalted()) {
+ this->interruptP25Control(p25.get());
}
+ }
- // if there is a P25 CC running; halt the CC
- if (p25 != nullptr) {
- if (p25->getCCRunning() && !p25->getCCHalted()) {
- INTERRUPT_P25_CONTROL;
- }
+ // if there is a NXDN CC running; halt the CC
+ if (nxdn != nullptr) {
+ if (nxdn->getCCRunning() && !nxdn->getCCHalted()) {
+ this->interruptNXDNControl(nxdn.get());
}
+ }
+ });
- // if there is a NXDN CC running; halt the CC
- if (nxdn != nullptr) {
- if (nxdn->getCCRunning() && !nxdn->getCCHalted()) {
- INTERRUPT_NXDN_CONTROL;
- }
+ // read DMR slot 2 frames from modem
+ readFramesDMR2(dmr.get(), [&, this]() {
+ if (dmr != nullptr) {
+ this->interruptDMRBeacon(dmr.get());
+ }
+
+ // if there is a P25 CC running; halt the CC
+ if (p25 != nullptr) {
+ if (p25->getCCRunning() && !p25->getCCHalted()) {
+ this->interruptP25Control(p25.get());
}
+ }
- m_modeTimer.start();
+ // if there is a NXDN CC running; halt the CC
+ if (nxdn != nullptr) {
+ if (nxdn->getCCRunning() && !nxdn->getCCHalted()) {
+ this->interruptNXDNControl(nxdn.get());
+ }
}
+ });
- m_lastDstId = dmr->getLastDstId(2U);
- m_lastSrcId = dmr->getLastSrcId(2U);
- }
+ if (m_state != STATE_IDLE)
+ Thread::sleep(m_activeTickDelay);
+ if (m_state == STATE_IDLE)
+ Thread::sleep(m_idleTickDelay);
}
}
#endif // defined(ENABLE_DMR)
+ });
+ dmrProcessThread.run();
- /** Project 25 */
+ /** Project 25 */
+ ThreadFunc p25ProcessThread([&, this]() {
#if defined(ENABLE_P25)
- // check if there is space on the modem for P25 frames,
- // if there is read frames from the P25 controller and write it
- // to the modem
if (p25 != nullptr) {
- uint8_t nextLen = p25->peekFrameLength();
- if (nextLen > 0U) {
- ret = m_modem->hasP25Space(nextLen);
- if (ret) {
- len = p25->getFrame(data);
- if (len > 0U) {
- // if the state is idle; set to P25 and start mode timer
- if (m_state == STATE_IDLE) {
- m_modeTimer.setTimeout(m_netModeHang);
- setState(STATE_P25);
- }
-
- // if the state is P25; write P25 frame data
- if (m_state == STATE_P25) {
- m_modem->writeP25Frame(data, len);
-
- INTERRUPT_DMR_BEACON;
-
- // if there is a NXDN CC running; halt the CC
- if (nxdn != nullptr) {
- if (nxdn->getCCRunning() && !nxdn->getCCHalted()) {
- INTERRUPT_NXDN_CONTROL;
- }
- }
+ LogDebug(LOG_HOST, "P25, started frame processor");
+ while (!g_killed) {
+ // ------------------------------------------------------
+ // -- Write to Modem Processing --
+ // ------------------------------------------------------
+
+ // write P25 frames to modem
+ writeFramesP25(p25.get(), [&, this]() {
+ if (dmr != nullptr) {
+ this->interruptDMRBeacon(dmr.get());
+ }
- m_modeTimer.start();
+ // if there is a NXDN CC running; halt the CC
+ if (nxdn != nullptr) {
+ if (nxdn->getCCRunning() && !nxdn->getCCHalted()) {
+ this->interruptNXDNControl(nxdn.get());
}
-
- m_lastDstId = p25->getLastDstId();
- m_lastSrcId = p25->getLastSrcId();
}
- else {
- nextLen = 0U;
- }
- }
- }
+ });
- if (nextLen == 0U) {
- // if we have no P25 data, and we're either idle or P25 state, check if we
- // need to be starting the CC running flag or writing end of voice call data
- if (m_state == STATE_IDLE || m_state == STATE_P25) {
- if (p25->getCCHalted()) {
- p25->setCCHalted(false);
- }
+ // ------------------------------------------------------
+ // -- Read from Modem Processing --
+ // ------------------------------------------------------
- // write end of voice if necessary
- ret = p25->writeRF_VoiceEnd();
- if (ret) {
- if (m_state == STATE_IDLE) {
- m_modeTimer.setTimeout(m_netModeHang);
- setState(STATE_P25);
- }
+ // read P25 frames from modem
+ readFramesP25(p25.get(), [&, this]() {
+ if (dmr != nullptr) {
+ this->interruptDMRBeacon(dmr.get());
+ }
- if (m_state == STATE_P25) {
- m_modeTimer.start();
+ // if there is a NXDN CC running; halt the CC
+ if (nxdn != nullptr) {
+ if (nxdn->getCCRunning() && !nxdn->getCCHalted()) {
+ this->interruptNXDNControl(nxdn.get());
}
}
- }
- }
-
- // if the modem is in duplex -- handle P25 CC burst control
- if (m_duplex) {
- if (p25BcastDurationTimer.isPaused() && !p25->getCCHalted()) {
- p25BcastDurationTimer.resume();
- }
-
- if (p25->getCCHalted()) {
- p25->setCCHalted(false);
- }
+ });
- if (g_fireP25Control) {
- m_modeTimer.stop();
- }
+ if (m_state != STATE_IDLE)
+ Thread::sleep(m_activeTickDelay);
+ if (m_state == STATE_IDLE)
+ Thread::sleep(m_idleTickDelay);
}
}
#endif // defined(ENABLE_P25)
+ });
+ p25ProcessThread.run();
- /** Next Generation Digital Narrowband */
+ /** Next Generation Digital Narrowband */
+ ThreadFunc nxdnProcessThread([&, this]() {
#if defined(ENABLE_NXDN)
- // check if there is space on the modem for NXDN frames,
- // if there is read frames from the NXDN controller and write it
- // to the modem
if (nxdn != nullptr) {
- ret = m_modem->hasNXDNSpace();
- if (ret) {
- len = nxdn->getFrame(data);
- if (len > 0U) {
- // if the state is idle; set to NXDN and start mode timer
- if (m_state == STATE_IDLE) {
- m_modeTimer.setTimeout(m_netModeHang);
- setState(STATE_NXDN);
+ LogDebug(LOG_HOST, "NXDN, started frame processor");
+ while (!g_killed) {
+ // ------------------------------------------------------
+ // -- Write to Modem Processing --
+ // ------------------------------------------------------
+
+ // write NXDN frames to modem
+ writeFramesNXDN(nxdn.get(), [&, this]() {
+ if (dmr != nullptr) {
+ this->interruptDMRBeacon(dmr.get());
}
- // if the state is NXDN; write NXDN data
- if (m_state == STATE_NXDN) {
- m_modem->writeNXDNFrame(data, len);
-
- INTERRUPT_DMR_BEACON;
-
- // if there is a P25 CC running; halt the CC
- if (p25 != nullptr) {
- if (p25->getCCRunning() && !p25->getCCHalted()) {
- INTERRUPT_P25_CONTROL;
- }
+ // if there is a P25 CC running; halt the CC
+ if (p25 != nullptr) {
+ if (p25->getCCRunning() && !p25->getCCHalted()) {
+ this->interruptP25Control(p25.get());
}
-
- m_modeTimer.start();
}
+ });
- m_lastDstId = nxdn->getLastDstId();
- m_lastSrcId = nxdn->getLastSrcId();
- }
- }
- }
-#endif // defined(ENABLE_NXDN)
-
- // ------------------------------------------------------
- // -- Modem Clocking --
- // ------------------------------------------------------
-
- ms = stopWatch.elapsed();
- stopWatch.start();
-
- m_modem->clock(ms);
+ // ------------------------------------------------------
+ // -- Read from Modem Processing --
+ // ------------------------------------------------------
- // ------------------------------------------------------
- // -- Read from Modem Processing --
- // ------------------------------------------------------
-
- /** Digital Mobile Radio */
-#if defined(ENABLE_DMR)
- if (dmr != nullptr) {
- // read DMR slot 1 frames from the modem, and if there is any
- // write those frames to the DMR controller
- len = m_modem->readDMRFrame1(data);
- if (len > 0U) {
- if (m_state == STATE_IDLE) {
- // if the modem is in duplex -- process wakeup CSBKs
- if (m_duplex) {
- bool ret = dmr->processWakeup(data);
- if (ret) {
- m_modeTimer.setTimeout(m_rfModeHang);
- setState(STATE_DMR);
-
- START_DMR_DUPLEX_IDLE(true);
-
- INTERRUPT_DMR_BEACON;
- INTERRUPT_P25_CONTROL;
- INTERRUPT_NXDN_CONTROL;
- }
+ // read NXDN frames from modem
+ readFramesNXDN(nxdn.get(), [&, this]() {
+ if (dmr != nullptr) {
+ this->interruptDMRBeacon(dmr.get());
}
- else {
- // in simplex directly process slot 1 frames
- m_modeTimer.setTimeout(m_rfModeHang);
- setState(STATE_DMR);
- START_DMR_DUPLEX_IDLE(true);
-
- dmr->processFrame(1U, data, len);
- INTERRUPT_DMR_BEACON;
- INTERRUPT_P25_CONTROL;
- INTERRUPT_NXDN_CONTROL;
- }
- }
- else if (m_state == STATE_DMR) {
- // if the modem is in duplex, and hasn't started transmitting
- // process wakeup CSBKs
- if (m_duplex && !m_modem->hasTX()) {
- bool ret = dmr->processWakeup(data);
- if (ret) {
- m_modem->writeDMRStart(true);
- m_dmrTXTimer.start();
+ // if there is a P25 CC running; halt the CC
+ if (p25 != nullptr) {
+ if (p25->getCCRunning() && !p25->getCCHalted()) {
+ this->interruptP25Control(p25.get());
}
}
- else {
- // process slot 1 frames
- bool ret = dmr->processFrame(1U, data, len);
- if (ret) {
- INTERRUPT_DMR_BEACON;
- INTERRUPT_P25_CONTROL;
- INTERRUPT_NXDN_CONTROL;
+ });
- m_modeTimer.start();
- if (m_duplex)
- m_dmrTXTimer.start();
- }
- }
- }
- else if (m_state != HOST_STATE_LOCKOUT) {
- LogWarning(LOG_HOST, "DMR modem data received, state = %u", m_state);
- }
+ if (m_state != STATE_IDLE)
+ Thread::sleep(m_activeTickDelay);
+ if (m_state == STATE_IDLE)
+ Thread::sleep(m_idleTickDelay);
}
+ }
+#endif // defined(ENABLE_NXDN)
+ });
+ nxdnProcessThread.run();
- // read DMR slot 2 frames from the modem, and if there is any
- // write those frames to the DMR controller
- len = m_modem->readDMRFrame2(data);
- if (len > 0U) {
- if (m_state == STATE_IDLE) {
- // if the modem is in duplex -- process wakeup CSBKs
- if (m_duplex) {
- bool ret = dmr->processWakeup(data);
- if (ret) {
- m_modeTimer.setTimeout(m_rfModeHang);
- setState(STATE_DMR);
- START_DMR_DUPLEX_IDLE(true);
-
- INTERRUPT_DMR_BEACON;
- INTERRUPT_P25_CONTROL;
- INTERRUPT_NXDN_CONTROL;
- }
- }
- else {
- // in simplex -- directly process slot 2 frames
- m_modeTimer.setTimeout(m_rfModeHang);
- setState(STATE_DMR);
- START_DMR_DUPLEX_IDLE(true);
+ // main execution loop
+ while (!killed) {
+ if (m_modem->hasLockout() && m_state != HOST_STATE_LOCKOUT)
+ setState(HOST_STATE_LOCKOUT);
+ else if (!m_modem->hasLockout() && m_state == HOST_STATE_LOCKOUT)
+ setState(STATE_IDLE);
- dmr->processFrame(2U, data, len);
+ if (m_modem->hasError() && m_state != HOST_STATE_ERROR)
+ setState(HOST_STATE_ERROR);
+ else if (!m_modem->hasError() && m_state == HOST_STATE_ERROR)
+ setState(STATE_IDLE);
- INTERRUPT_DMR_BEACON;
- INTERRUPT_P25_CONTROL;
- INTERRUPT_NXDN_CONTROL;
- }
- }
- else if (m_state == STATE_DMR) {
- // if the modem is in duplex, and hasn't started transmitting
- // process wakeup CSBKs
- if (m_duplex && !m_modem->hasTX()) {
- bool ret = dmr->processWakeup(data);
- if (ret) {
- m_modem->writeDMRStart(true);
- m_dmrTXTimer.start();
- }
- }
- else {
- // process slot 2 frames
- bool ret = dmr->processFrame(2U, data, len);
- if (ret) {
- INTERRUPT_DMR_BEACON;
- INTERRUPT_P25_CONTROL;
- INTERRUPT_NXDN_CONTROL;
+ uint32_t ms = stopWatch.elapsed();
+ if (ms > 1U)
+ m_modem->clock(ms);
- m_modeTimer.start();
- if (m_duplex)
- m_dmrTXTimer.start();
- }
- }
- }
- else if (m_state != HOST_STATE_LOCKOUT) {
- LogWarning(LOG_HOST, "DMR modem data received, state = %u", m_state);
- }
+ if (!m_fixedMode) {
+ if (m_modeTimer.isRunning() && m_modeTimer.hasExpired()) {
+ setState(STATE_IDLE);
}
}
-#endif // defined(ENABLE_DMR)
-
- /** Project 25 */
-#if defined(ENABLE_P25)
- // read P25 frames from modem, and if there are frames
- // write those frames to the P25 controller
- if (p25 != nullptr) {
- len = m_modem->readP25Frame(data);
- if (len > 0U) {
- if (m_state == STATE_IDLE) {
- // process P25 frames
- bool ret = p25->processFrame(data, len);
- if (ret) {
- m_modeTimer.setTimeout(m_rfModeHang);
- setState(STATE_P25);
-
- INTERRUPT_DMR_BEACON;
- //INTERRUPT_P25_CONTROL;
- INTERRUPT_NXDN_CONTROL;
- }
- else {
- ret = p25->writeRF_VoiceEnd();
- if (ret) {
- INTERRUPT_DMR_BEACON;
- INTERRUPT_NXDN_CONTROL;
-
- if (m_state == STATE_IDLE) {
- m_modeTimer.setTimeout(m_rfModeHang);
- setState(STATE_P25);
- }
-
- if (m_state == STATE_P25) {
- m_modeTimer.start();
- }
-
- // if the modem is in duplex -- handle P25 CC burst control
- if (m_duplex) {
- if (p25BcastDurationTimer.isPaused() && !p25->getCCHalted()) {
- p25BcastDurationTimer.resume();
- }
-
- if (p25->getCCHalted()) {
- p25->setCCHalted(false);
- }
-
- if (g_fireP25Control) {
- m_modeTimer.stop();
- }
- }
- else {
- p25BcastDurationTimer.stop();
- }
- }
- }
- }
- else if (m_state == STATE_P25) {
- // process P25 frames
- bool ret = p25->processFrame(data, len);
- if (ret) {
- m_modeTimer.start();
- //INTERRUPT_P25_CONTROL;
- }
- else {
- ret = p25->writeRF_VoiceEnd();
- if (ret) {
- m_modeTimer.start();
- }
- }
- }
- else if (m_state != HOST_STATE_LOCKOUT) {
- LogWarning(LOG_HOST, "P25 modem data received, state = %u", m_state);
- }
+ else {
+ m_modeTimer.stop();
+ if (dmr != nullptr && m_state != STATE_DMR && !m_modem->hasTX()) {
+ LogDebug(LOG_HOST, "fixed mode state abnormal, m_state = %u, state = %u", m_state, STATE_DMR);
+ setState(STATE_DMR);
+ }
+ if (p25 != nullptr && m_state != STATE_P25 && !m_modem->hasTX()) {
+ LogDebug(LOG_HOST, "fixed mode state abnormal, m_state = %u, state = %u", m_state, STATE_P25);
+ setState(STATE_P25);
+ }
+ if (nxdn != nullptr && m_state != STATE_NXDN && !m_modem->hasTX()) {
+ LogDebug(LOG_HOST, "fixed mode state abnormal, m_state = %u, state = %u", m_state, STATE_NXDN);
+ setState(STATE_NXDN);
}
}
-#endif // defined(ENABLE_P25)
- /** Next Generation Digital Narrowband */
- // read NXDN frames from modem, and if there are frames
- // write those frames to the NXDN controller
-#if defined(ENABLE_NXDN)
- if (nxdn != nullptr) {
- len = m_modem->readNXDNFrame(data);
- if (len > 0U) {
- if (m_state == STATE_IDLE) {
- // process NXDN frames
- bool ret = nxdn->processFrame(data, len);
- if (ret) {
- m_modeTimer.setTimeout(m_rfModeHang);
- setState(STATE_NXDN);
+ // ------------------------------------------------------
+ // -- Modem Clocking --
+ // ------------------------------------------------------
- INTERRUPT_DMR_BEACON;
- INTERRUPT_P25_CONTROL;
- }
- }
- else if (m_state == STATE_NXDN) {
- // process NXDN frames
- bool ret = nxdn->processFrame(data, len);
- if (ret) {
- m_modeTimer.start();
- //INTERRUPT_NXDN_CONTROL;
- }
- }
- else if (m_state != HOST_STATE_LOCKOUT) {
- LogWarning(LOG_HOST, "NXDN modem data received, state = %u", m_state);
- }
- }
- }
-#endif // defined(ENABLE_NXDN)
+ ms = stopWatch.elapsed();
+ stopWatch.start();
+
+ m_modem->clock(ms);
// ------------------------------------------------------
// -- Network, DMR, and P25 Clocking --
@@ -1342,11 +1044,11 @@ int Host::run()
m_cwIdTimer.clock(ms);
if (m_cwIdTimer.isRunning() && m_cwIdTimer.hasExpired()) {
if (!m_modem->hasTX() && !m_p25CtrlChannel && !m_dmrCtrlChannel) {
- if (dmrBeaconDurationTimer.isRunning() || p25BcastDurationTimer.isRunning()) {
+ if (m_dmrBeaconDurationTimer.isRunning() || m_p25BcastDurationTimer.isRunning()) {
LogDebug(LOG_HOST, "CW, beacon or CC timer running, ceasing");
- dmrBeaconDurationTimer.stop();
- p25BcastDurationTimer.stop();
+ m_dmrBeaconDurationTimer.stop();
+ m_p25BcastDurationTimer.stop();
}
LogDebug(LOG_HOST, "CW, start transmitting");
@@ -1424,14 +1126,14 @@ int Host::run()
LogDebug(LOG_HOST, "DMR, roaming beacon burst");
}
dmrBeaconIntervalTimer.start();
- dmrBeaconDurationTimer.start();
+ m_dmrBeaconDurationTimer.start();
}
}
// clock and check DMR roaming beacon duration timer
- dmrBeaconDurationTimer.clock(ms);
- if (dmrBeaconDurationTimer.isRunning() && dmrBeaconDurationTimer.hasExpired()) {
- dmrBeaconDurationTimer.stop();
+ m_dmrBeaconDurationTimer.clock(ms);
+ if (m_dmrBeaconDurationTimer.isRunning() && m_dmrBeaconDurationTimer.hasExpired()) {
+ m_dmrBeaconDurationTimer.stop();
if (!m_fixedMode) {
if (m_state == STATE_DMR && !m_modeTimer.isRunning()) {
@@ -1481,23 +1183,23 @@ int Host::run()
g_fireP25Control = false;
p25BcastIntervalTimer.start();
- p25BcastDurationTimer.start();
+ m_p25BcastDurationTimer.start();
// if the CC is continuous -- clock one cycle into the duration timer
if (m_p25CtrlChannel) {
- p25BcastDurationTimer.clock(ms);
+ m_p25BcastDurationTimer.clock(ms);
}
}
}
- if (p25BcastDurationTimer.isPaused()) {
- p25BcastDurationTimer.resume();
+ if (m_p25BcastDurationTimer.isPaused()) {
+ m_p25BcastDurationTimer.resume();
}
// clock and check P25 CC broadcast duration timer
- p25BcastDurationTimer.clock(ms);
- if (p25BcastDurationTimer.isRunning() && p25BcastDurationTimer.hasExpired()) {
- p25BcastDurationTimer.stop();
+ m_p25BcastDurationTimer.clock(ms);
+ if (m_p25BcastDurationTimer.isRunning() && m_p25BcastDurationTimer.hasExpired()) {
+ m_p25BcastDurationTimer.stop();
p25->setCCRunning(false);
@@ -1548,23 +1250,23 @@ int Host::run()
g_fireNXDNControl = false;
nxdnBcastIntervalTimer.start();
- nxdnBcastDurationTimer.start();
+ m_nxdnBcastDurationTimer.start();
// if the CC is continuous -- clock one cycle into the duration timer
if (m_nxdnCtrlChannel) {
- nxdnBcastDurationTimer.clock(ms);
+ m_nxdnBcastDurationTimer.clock(ms);
}
}
}
- if (nxdnBcastDurationTimer.isPaused()) {
- nxdnBcastDurationTimer.resume();
+ if (m_nxdnBcastDurationTimer.isPaused()) {
+ m_nxdnBcastDurationTimer.resume();
}
// clock and check NXDN CC broadcast duration timer
- nxdnBcastDurationTimer.clock(ms);
- if (nxdnBcastDurationTimer.isRunning() && nxdnBcastDurationTimer.hasExpired()) {
- nxdnBcastDurationTimer.stop();
+ m_nxdnBcastDurationTimer.clock(ms);
+ if (m_nxdnBcastDurationTimer.isRunning() && m_nxdnBcastDurationTimer.hasExpired()) {
+ m_nxdnBcastDurationTimer.stop();
nxdn->setCCRunning(false);
@@ -1589,6 +1291,11 @@ int Host::run()
#endif // defined(ENABLE_NXDN)
if (g_killed) {
+ // shutdown reader threads
+ dmrProcessThread.wait();
+ p25ProcessThread.wait();
+ nxdnProcessThread.wait();
+
#if defined(ENABLE_DMR)
if (dmr != nullptr) {
if (m_dmrCtrlChannel) {
@@ -1600,7 +1307,7 @@ int Host::run()
dmr->setCCRunning(false);
dmr->setCCHalted(true);
- dmrBeaconDurationTimer.stop();
+ m_dmrBeaconDurationTimer.stop();
dmrBeaconIntervalTimer.stop();
}
}
@@ -1616,7 +1323,7 @@ int Host::run()
p25->setCCRunning(false);
- p25BcastDurationTimer.stop();
+ m_p25BcastDurationTimer.stop();
p25BcastIntervalTimer.stop();
}
}
@@ -1632,7 +1339,7 @@ int Host::run()
nxdn->setCCRunning(false);
- nxdnBcastDurationTimer.stop();
+ m_nxdnBcastDurationTimer.stop();
nxdnBcastIntervalTimer.stop();
}
}
diff --git a/src/host/Host.h b/src/host/Host.h
index d5a2f674..07423a3b 100644
--- a/src/host/Host.h
+++ b/src/host/Host.h
@@ -32,6 +32,9 @@
#define __HOST_H__
#include "Defines.h"
+#include "dmr/Control.h"
+#include "p25/Control.h"
+#include "nxdn/Control.h"
#include "network/Network.h"
#include "network/RESTAPI.h"
#include "modem/Modem.h"
@@ -44,6 +47,7 @@
#include
#include
+#include
// ---------------------------------------------------------------------------
// Class Prototypes
@@ -151,6 +155,10 @@ private:
bool m_authoritative;
bool m_supervisor;
+ Timer m_dmrBeaconDurationTimer;
+ Timer m_p25BcastDurationTimer;
+ Timer m_nxdnBcastDurationTimer;
+
uint8_t m_activeTickDelay;
uint8_t m_idleTickDelay;
@@ -178,6 +186,37 @@ private:
void createLockFile(const char* state) const;
/// Helper to remove the state lock file.
void removeLockFile() const;
+
+ /** Digital Mobile Radio */
+ /// Helper to interrupt a running DMR beacon.
+ void interruptDMRBeacon(dmr::Control* control);
+
+ /// Helper to read DMR slot 1 frames from modem.
+ void readFramesDMR1(dmr::Control* control, std::function&& afterReadCallback);
+ /// Helper to write DMR slot 1 frames to modem.
+ void writeFramesDMR1(dmr::Control* control, std::function&& afterWriteCallback);
+ /// Helper to read DMR slot 2 frames from modem.
+ void readFramesDMR2(dmr::Control* control, std::function&& afterReadCallback);
+ /// Helper to write DMR slot 2 frames to modem.
+ void writeFramesDMR2(dmr::Control* control, std::function&& afterWriteCallback);
+
+ /** Project 25 */
+ /// Helper to interrupt a running P25 control channel.
+ void interruptP25Control(p25::Control* control);
+
+ /// Helper to read P25 frames from modem.
+ void readFramesP25(p25::Control* control, std::function&& afterReadCallback);
+ /// Helper to write P25 frames to modem.
+ void writeFramesP25(p25::Control* control, std::function&& afterWriteCallback);
+
+ /** Next Generation Digital Narrowband */
+ /// Helper to interrupt a running NXDN control channel.
+ void interruptNXDNControl(nxdn::Control* control);
+
+ /// Helper to read NXDN frames from modem.
+ void readFramesNXDN(nxdn::Control* control, std::function&& afterReadCallback);
+ /// Helper to write NXDN frames to modem.
+ void writeFramesNXDN(nxdn::Control* control, std::function&& afterWriteCallback);
};
#endif // __HOST_H__