initial commit for dvmhost 3.55 dev branch; add support for std::function<> based lambdas as threads; split DMR, P25 and NXDN frame processing into their own source files for the host (Host.DMR.cpp, Host.P25.cpp, Host.NXDN.cpp) these files will contain host-level processing code for the specific protocols; implement protocol frame processors as their own running threads;
parent
82a5b775a9
commit
629c2fe75e
@ -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 <cassert>
|
||||
#include <functional>
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Class Declaration
|
||||
// Implements a simple function threading mechanism.
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
class HOST_SW_API ThreadFunc : public Thread {
|
||||
public:
|
||||
/// <summary>Initializes a new instance of the ThreadFunc class.</summary>
|
||||
ThreadFunc(std::function<void()>&& e) : Thread(),
|
||||
m_entry(e)
|
||||
{
|
||||
assert(e != nullptr);
|
||||
}
|
||||
|
||||
/// <summary>User-defined function to run for the thread main.</summary>
|
||||
virtual void entry()
|
||||
{
|
||||
if (m_entry != nullptr)
|
||||
m_entry();
|
||||
}
|
||||
|
||||
private:
|
||||
std::function<void()> m_entry;
|
||||
};
|
||||
|
||||
#endif // __THREAD_FUNC_H__
|
||||
@ -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
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/// <summary>
|
||||
/// Helper to interrupt a running DMR beacon.
|
||||
/// </summary>
|
||||
/// <param name="control"></param>
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Helper to read DMR slot 1 frames from modem.
|
||||
/// </summary>
|
||||
/// <param name="control"></param>
|
||||
/// <param name="afterReadCallback"></param>
|
||||
void Host::readFramesDMR1(dmr::Control* control, std::function<void()>&& 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Helper to write DMR slot 1 frames to modem.
|
||||
/// </summary>
|
||||
/// <param name="control"></param>
|
||||
/// <param name="afterWriteCallback"></param>
|
||||
void Host::writeFramesDMR1(dmr::Control* control, std::function<void()>&& 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Helper to read DMR slot 2 frames from modem.
|
||||
/// </summary>
|
||||
/// <param name="control"></param>
|
||||
/// <param name="afterReadCallback"></param>
|
||||
void Host::readFramesDMR2(dmr::Control* control, std::function<void()>&& 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Helper to write DMR slot 2 frames to modem.
|
||||
/// </summary>
|
||||
/// <param name="control"></param>
|
||||
/// <param name="afterWriteCallback"></param>
|
||||
void Host::writeFramesDMR2(dmr::Control* control, std::function<void()>&& 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/// <summary>
|
||||
/// Helper to interrupt a running NXDN control channel.
|
||||
/// </summary>
|
||||
/// <param name="control"></param>
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Helper to read NXDN frames from modem.
|
||||
/// </summary>
|
||||
/// <param name="control"></param>
|
||||
/// <param name="afterReadCallback"></param>
|
||||
void Host::readFramesNXDN(nxdn::Control* control, std::function<void()>&& 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Helper to write NXDN frames to modem.
|
||||
/// </summary>
|
||||
/// <param name="control"></param>
|
||||
/// <param name="afterWriteCallback"></param>
|
||||
void Host::writeFramesNXDN(nxdn::Control* control, std::function<void()>&& 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/// <summary>
|
||||
/// Helper to interrupt a running P25 control channel.
|
||||
/// </summary>
|
||||
/// <param name="control"></param>
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Helper to read P25 frames from modem.
|
||||
/// </summary>
|
||||
/// <param name="control"></param>
|
||||
/// <param name="afterReadCallback"></param>
|
||||
void Host::readFramesP25(p25::Control* control, std::function<void()>&& 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Helper to write P25 frames to modem.
|
||||
/// </summary>
|
||||
/// <param name="control"></param>
|
||||
/// <param name="afterWriteCallback"></param>
|
||||
void Host::writeFramesP25(p25::Control* control, std::function<void()>&& 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in new issue