mirror of
https://github.com/g4klx/DAPNETGateway.git
synced 2026-08-26 12:32:45 -04:00
Add initial timeslot support.
This commit is contained in:
parent
46d619c89e
commit
a196abdd56
4 changed files with 146 additions and 41 deletions
|
|
@ -39,6 +39,8 @@ const char* DEFAULT_INI_FILE = "DAPNETGateway.ini";
|
|||
const char* DEFAULT_INI_FILE = "/etc/DAPNETGateway.ini";
|
||||
#endif
|
||||
|
||||
#include <utility>
|
||||
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
|
|
@ -46,6 +48,18 @@ const char* DEFAULT_INI_FILE = "/etc/DAPNETGateway.ini";
|
|||
#include <cassert>
|
||||
#include <cmath>
|
||||
|
||||
const unsigned int POCSAG_BATCH_LENGTH_WORDS = 17U;
|
||||
const unsigned int POCSAG_FRAME_LENGTH_WORDS = 2U;
|
||||
|
||||
const unsigned int CODEWORD_TIME_US = 26667U; // 26.667ms
|
||||
const unsigned int FRAME_TIME_US = CODEWORD_TIME_US * POCSAG_FRAME_LENGTH_WORDS; // 53.333ms
|
||||
const unsigned int BATCH_TIME_US = CODEWORD_TIME_US * POCSAG_BATCH_LENGTH_WORDS; // 453.333ms
|
||||
|
||||
const unsigned int SLOT_TIME_US = 6400000U; // 6.4s
|
||||
const unsigned int SLOT_TIME_MS = SLOT_TIME_US / 1000U; // 6.4s
|
||||
const unsigned int CODEWORDS_PER_SLOT = SLOT_TIME_US / CODEWORD_TIME_US; // 240
|
||||
const unsigned int BATCHES_PER_SLOT = SLOT_TIME_US / BATCH_TIME_US; // 14
|
||||
|
||||
const unsigned char FUNCTIONAL_NUMERIC = 0U;
|
||||
const unsigned char FUNCTIONAL_ALPHANUMERIC = 3U;
|
||||
|
||||
|
|
@ -80,7 +94,11 @@ CDAPNETGateway::CDAPNETGateway(const std::string& configFile) :
|
|||
m_conf(configFile),
|
||||
m_dapnetNetwork(NULL),
|
||||
m_pocsagNetwork(NULL),
|
||||
m_queue()
|
||||
m_queue(),
|
||||
m_schedule(NULL),
|
||||
m_currentSlot(0U),
|
||||
m_sentCodewords(0U),
|
||||
m_mmdvmFree(false)
|
||||
{
|
||||
}
|
||||
|
||||
|
|
@ -216,10 +234,7 @@ int CDAPNETGateway::run()
|
|||
|
||||
::LogMessage("Logged into the DAPNET network");
|
||||
|
||||
CTimer messageTimer(1000U, 0U, 200U);
|
||||
|
||||
CStopWatch stopWatch;
|
||||
stopWatch.start();
|
||||
CStopWatch slotTimer;
|
||||
|
||||
LogMessage("Starting DAPNETGateway-%s", VERSION);
|
||||
|
||||
|
|
@ -230,11 +245,14 @@ int CDAPNETGateway::run()
|
|||
switch (buffer[0U]) {
|
||||
case 0x00U:
|
||||
// The MMDVM is idle
|
||||
messageTimer.start();
|
||||
if (!m_mmdvmFree) {
|
||||
m_mmdvmFree = true;
|
||||
m_sentCodewords = (slotTimer.elapsed() * 1000U) / CODEWORD_TIME_US;
|
||||
}
|
||||
break;
|
||||
case 0xFFU:
|
||||
// The MMDVM is busy
|
||||
messageTimer.stop();
|
||||
m_mmdvmFree = false;
|
||||
break;
|
||||
default:
|
||||
// The MMDVM is sending crap
|
||||
|
|
@ -249,7 +267,7 @@ int CDAPNETGateway::run()
|
|||
|
||||
CPOCSAGMessage* message = m_dapnetNetwork->readMessage();
|
||||
if (message != NULL) {
|
||||
if (!messageTimer.isRunning() || !m_queue.empty()) {
|
||||
if (!m_mmdvmFree) {
|
||||
if (!isTimeMessage(message)) {
|
||||
LogDebug("Queueing message to %07u, type %u, func %s: \"%.*s\"", message->m_ric, message->m_type, message->m_functional == FUNCTIONAL_NUMERIC ? "Numeric" : "Alphanumeric", message->m_length, message->m_message);
|
||||
m_queue.push_front(message);
|
||||
|
|
@ -258,25 +276,25 @@ int CDAPNETGateway::run()
|
|||
delete message;
|
||||
}
|
||||
} else {
|
||||
LogMessage("Sending message to %07u, type %u, func %s: \"%.*s\"", message->m_ric, message->m_type, message->m_functional == FUNCTIONAL_NUMERIC ? "Numeric" : "Alphanumeric", message->m_length, message->m_message);
|
||||
m_pocsagNetwork->write(message);
|
||||
delete message;
|
||||
LogDebug("Queueing message to %07u, type %u, func %s: \"%.*s\"", message->m_ric, message->m_type, message->m_functional == FUNCTIONAL_NUMERIC ? "Numeric" : "Alphanumeric", message->m_length, message->m_message);
|
||||
m_queue.push_front(message);
|
||||
}
|
||||
}
|
||||
|
||||
unsigned int ms = stopWatch.elapsed();
|
||||
stopWatch.start();
|
||||
|
||||
messageTimer.clock(ms);
|
||||
if (messageTimer.isRunning() && messageTimer.hasExpired()) {
|
||||
if (!m_queue.empty())
|
||||
sendData();
|
||||
|
||||
messageTimer.start();
|
||||
unsigned int t = (slotTimer.time() / 100ULL) % 1024ULL;
|
||||
unsigned int slot = t / 64U;
|
||||
if (slot != m_currentSlot) {
|
||||
m_currentSlot = slot;
|
||||
LogDebug("Start of slot %u", m_currentSlot);
|
||||
if (m_schedule == NULL || m_currentSlot == 0U)
|
||||
loadSchedule();
|
||||
m_sentCodewords = 0U;
|
||||
slotTimer.start();
|
||||
}
|
||||
|
||||
if (ms < 5U)
|
||||
CThread::sleep(5U);
|
||||
sendMessages();
|
||||
|
||||
CThread::sleep(5U);
|
||||
}
|
||||
|
||||
m_pocsagNetwork->close();
|
||||
|
|
@ -290,20 +308,37 @@ int CDAPNETGateway::run()
|
|||
return 0;
|
||||
}
|
||||
|
||||
bool CDAPNETGateway::sendData()
|
||||
void CDAPNETGateway::sendMessages()
|
||||
{
|
||||
assert(m_pocsagNetwork != NULL);
|
||||
// If the MMDVM is busy, we can't send anything.
|
||||
if (!m_mmdvmFree)
|
||||
return;
|
||||
|
||||
if (!m_queue.empty()) {
|
||||
CPOCSAGMessage* message = m_queue.back();
|
||||
m_queue.pop_back();
|
||||
LogMessage("Sending message to %07u, type %u, func %s: \"%.*s\"", message->m_ric, message->m_type, message->m_functional == FUNCTIONAL_NUMERIC ? "Numeric" : "Alphanumeric", message->m_length, message->m_message);
|
||||
m_pocsagNetwork->write(message);
|
||||
delete message;
|
||||
return true;
|
||||
}
|
||||
// Do we have a schedule?
|
||||
if (m_schedule == NULL)
|
||||
return;
|
||||
|
||||
return false;
|
||||
// Check to see if we're allowed to send within a slot.
|
||||
if (!m_schedule[m_currentSlot])
|
||||
return;
|
||||
|
||||
// If we have data to send, see if we have time to do so in the current schedule.
|
||||
if (m_queue.empty())
|
||||
return;
|
||||
|
||||
CPOCSAGMessage* message = m_queue.back();
|
||||
assert(message != NULL);
|
||||
|
||||
unsigned int totalCodewords = m_sentCodewords + calculateCodewords(message);
|
||||
if (totalCodewords >= CODEWORDS_PER_SLOT)
|
||||
return;
|
||||
|
||||
m_sentCodewords = totalCodewords;
|
||||
LogMessage("Sending message to %07u, type %u, func %s: \"%.*s\"", message->m_ric, message->m_type, message->m_functional == FUNCTIONAL_NUMERIC ? "Numeric" : "Alphanumeric", message->m_length, message->m_message);
|
||||
|
||||
m_queue.pop_back();
|
||||
m_pocsagNetwork->write(message);
|
||||
delete message;
|
||||
}
|
||||
|
||||
bool CDAPNETGateway::recover()
|
||||
|
|
@ -332,3 +367,43 @@ bool CDAPNETGateway::isTimeMessage(const CPOCSAGMessage* message) const
|
|||
return false;
|
||||
}
|
||||
|
||||
unsigned int CDAPNETGateway::calculateCodewords(const CPOCSAGMessage* message) const
|
||||
{
|
||||
assert(message != NULL);
|
||||
|
||||
unsigned int len = 0U;
|
||||
if (message->m_functional == FUNCTIONAL_NUMERIC)
|
||||
len = message->m_length / 5U; // For the number packing, five to a word
|
||||
else
|
||||
len = (message->m_length * 20U) / 7U; // For the ASCII packing, 20/7 to a word
|
||||
|
||||
len++; // For the address word
|
||||
|
||||
if ((len % 2U) == 1U) // Always an even number of words
|
||||
len++;
|
||||
|
||||
if (len > 16U) // A very long message will include a sync word
|
||||
len++;
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
void CDAPNETGateway::loadSchedule()
|
||||
{
|
||||
bool* schedule = m_dapnetNetwork->readSchedule();
|
||||
if (schedule == NULL)
|
||||
return;
|
||||
|
||||
delete[] m_schedule;
|
||||
m_schedule = schedule;
|
||||
|
||||
std::string text;
|
||||
for (unsigned int i = 0U; i < 16U; i++) {
|
||||
if (m_schedule[i])
|
||||
text += "*";
|
||||
else
|
||||
text += "-";
|
||||
}
|
||||
|
||||
LogMessage("Loaded new schedule: %s", text.c_str());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@
|
|||
|
||||
#include <string>
|
||||
#include <deque>
|
||||
#include <vector>
|
||||
|
||||
class CDAPNETGateway
|
||||
{
|
||||
|
|
@ -40,10 +41,17 @@ private:
|
|||
CDAPNETNetwork* m_dapnetNetwork;
|
||||
CPOCSAGNetwork* m_pocsagNetwork;
|
||||
std::deque<CPOCSAGMessage*> m_queue;
|
||||
bool* m_schedule;
|
||||
unsigned int m_currentSlot;
|
||||
unsigned int m_sentCodewords;
|
||||
bool m_mmdvmFree;
|
||||
|
||||
bool sendData();
|
||||
|
||||
void sendMessages();
|
||||
bool recover();
|
||||
bool isTimeMessage(const CPOCSAGMessage* message) const;
|
||||
unsigned int calculateCodewords(const CPOCSAGMessage* message) const;
|
||||
void loadSchedule();
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (C) 2015,2016 by Jonathan Naylor G4KLX
|
||||
* Copyright (C) 2015,2016,2018 by Jonathan Naylor G4KLX
|
||||
*
|
||||
* 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
|
||||
|
|
@ -21,21 +21,32 @@
|
|||
#if defined(_WIN32) || defined(_WIN64)
|
||||
|
||||
CStopWatch::CStopWatch() :
|
||||
m_frequency(),
|
||||
m_frequencyMS(),
|
||||
m_frequencyS(),
|
||||
m_start()
|
||||
{
|
||||
::QueryPerformanceFrequency(&m_frequency);
|
||||
::QueryPerformanceFrequency(&m_frequencyS);
|
||||
|
||||
m_frequencyMS.QuadPart = m_frequencyS.QuadPart / 1000ULL;
|
||||
}
|
||||
|
||||
CStopWatch::~CStopWatch()
|
||||
{
|
||||
}
|
||||
|
||||
unsigned long long CStopWatch::time() const
|
||||
{
|
||||
LARGE_INTEGER now;
|
||||
::QueryPerformanceCounter(&now);
|
||||
|
||||
return (unsigned long long)(now.QuadPart / m_frequencyMS.QuadPart);
|
||||
}
|
||||
|
||||
unsigned long CStopWatch::start()
|
||||
{
|
||||
::QueryPerformanceCounter(&m_start);
|
||||
|
||||
return (unsigned long)(m_start.QuadPart / m_frequency.QuadPart);
|
||||
return (unsigned long)(m_start.QuadPart / m_frequencyS.QuadPart);
|
||||
}
|
||||
|
||||
unsigned int CStopWatch::elapsed()
|
||||
|
|
@ -46,7 +57,7 @@ unsigned int CStopWatch::elapsed()
|
|||
LARGE_INTEGER temp;
|
||||
temp.QuadPart = (now.QuadPart - m_start.QuadPart) * 1000;
|
||||
|
||||
return (unsigned int)(temp.QuadPart / m_frequency.QuadPart);
|
||||
return (unsigned int)(temp.QuadPart / m_frequencyS.QuadPart);
|
||||
}
|
||||
|
||||
#else
|
||||
|
|
@ -62,6 +73,14 @@ CStopWatch::~CStopWatch()
|
|||
{
|
||||
}
|
||||
|
||||
unsigned long long CStopWatch::time() const
|
||||
{
|
||||
struct timeval now;
|
||||
::gettimeofday(&now, NULL);
|
||||
|
||||
return now.tv_sec * 1000ULL + now.tv_usec / 1000ULL;
|
||||
}
|
||||
|
||||
unsigned long CStopWatch::start()
|
||||
{
|
||||
::gettimeofday(&m_start, NULL);
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (C) 2015,2016 by Jonathan Naylor G4KLX
|
||||
* Copyright (C) 2015,2016,2018 by Jonathan Naylor G4KLX
|
||||
*
|
||||
* 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
|
||||
|
|
@ -31,12 +31,15 @@ public:
|
|||
CStopWatch();
|
||||
~CStopWatch();
|
||||
|
||||
unsigned long long time() const;
|
||||
|
||||
unsigned long start();
|
||||
unsigned int elapsed();
|
||||
|
||||
private:
|
||||
#if defined(_WIN32) || defined(_WIN64)
|
||||
LARGE_INTEGER m_frequency;
|
||||
LARGE_INTEGER m_frequencyS;
|
||||
LARGE_INTEGER m_frequencyMS;
|
||||
LARGE_INTEGER m_start;
|
||||
#else
|
||||
struct timeval m_start;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue