From b237829b95703720aaec39726e0324b495b21df5 Mon Sep 17 00:00:00 2001 From: Geoffrey Merck Date: Sat, 19 Feb 2022 16:46:16 +0100 Subject: [PATCH] #20 first working prototype of time server --- DGWTimeServer/DGWTimeServerApp.cpp | 61 +- DGWTimeServer/DGWTimeServerApp.h | 21 + DGWTimeServer/TimeServerConfig.cpp | 32 + DGWTimeServer/TimeServerConfig.h | 11 +- DGWTimeServer/TimeServerDefs.h | 2 +- DGWTimeServer/TimeServerThread.cpp | 1458 ++++++++++++++++++++++++++++ DGWTimeServer/TimeServerThread.h | 122 +++ DGWTimeServer/example.cfg | 3 + 8 files changed, 1705 insertions(+), 5 deletions(-) create mode 100644 DGWTimeServer/TimeServerThread.cpp create mode 100644 DGWTimeServer/TimeServerThread.h diff --git a/DGWTimeServer/DGWTimeServerApp.cpp b/DGWTimeServer/DGWTimeServerApp.cpp index 6b894ec..5088ee2 100644 --- a/DGWTimeServer/DGWTimeServerApp.cpp +++ b/DGWTimeServer/DGWTimeServerApp.cpp @@ -19,9 +19,9 @@ #include #include +#include #include "DGWTimeServerApp.h" -#include "TimeServerConfig.h" int main(int argc, char * argv[]) { @@ -34,7 +34,62 @@ int main(int argc, char * argv[]) std::string configfile(argv[1]); CTimeServerConfig config(configfile); if(!config.load()) + return 1; + + CDGWTimeServerApp app(&config); + + if(!app.init()) return 0; - return 1; -} \ No newline at end of file + app.run(); + + return 0; +} + +CDGWTimeServerApp::CDGWTimeServerApp(const CTimeServerConfig * config) : +m_config(config) +{ + assert(config != nullptr); +} + +CDGWTimeServerApp::~CDGWTimeServerApp() +{ + delete m_thread; +} + +bool CDGWTimeServerApp::init() +{ + return createThread(); +} + +void CDGWTimeServerApp::run() +{ + m_thread->Run(); + m_thread->Wait(); +} + +bool CDGWTimeServerApp::createThread() +{ + m_thread = new CTimeServerThread(); + + TTimeServer timeserver; + m_config->getTimeServer(timeserver); + + std::vector rptrs = { "", "", "", "" }; + TRepeater repeater; + for(unsigned int i = 0u; i < m_config->getRepeaterCount(); i++) { + m_config->getRepeater(i, repeater); + rptrs[i].assign(repeater.band); + } + + TPaths paths; + m_config->getPaths(paths); + + m_thread = new CTimeServerThread(); + bool ret = m_thread->setGateway(timeserver.callsign, rptrs[0], rptrs[1], rptrs[2], rptrs[3], timeserver.address, paths.data); + if(ret) { + m_thread->setAnnouncements(timeserver.language, timeserver.format, timeserver.interval); + } + + return ret; +} diff --git a/DGWTimeServer/DGWTimeServerApp.h b/DGWTimeServer/DGWTimeServerApp.h index fc87275..ffcecdf 100644 --- a/DGWTimeServer/DGWTimeServerApp.h +++ b/DGWTimeServer/DGWTimeServerApp.h @@ -19,3 +19,24 @@ #pragma once +#include "TimeServerDefs.h" +#include "TimeServerConfig.h" +#include "TimeServerThread.h" + +class CDGWTimeServerApp +{ +private: + /* data */ +public: + CDGWTimeServerApp(const CTimeServerConfig * config); + ~CDGWTimeServerApp(); + + bool init(); + void run(); + +private: + bool createThread(); + + const CTimeServerConfig * m_config; + CTimeServerThread * m_thread; +}; diff --git a/DGWTimeServer/TimeServerConfig.cpp b/DGWTimeServer/TimeServerConfig.cpp index ec31444..5d4b067 100644 --- a/DGWTimeServer/TimeServerConfig.cpp +++ b/DGWTimeServer/TimeServerConfig.cpp @@ -49,6 +49,7 @@ bool CTimeServerConfig::load() ret = loadTimeServer(cfg) && ret; ret = loadRepeaters(cfg) && ret; ret = loadDaemon(cfg) && ret; + ret = loadPaths(cfg) && ret; } return ret; @@ -137,3 +138,34 @@ bool CTimeServerConfig::loadDaemon(const CConfig & cfg) ret = cfg.getValue("daemon", "user", m_daemon.user, 0, 1024, "") && ret; return ret; } + +bool CTimeServerConfig::loadPaths(const CConfig & cfg) +{ + bool ret = cfg.getValue("paths", "data", m_paths.data, 1, 1024, ""); + return ret; +} + +void CTimeServerConfig::getTimeServer(TTimeServer& timeserver) const +{ + timeserver = m_timeServer; +} + +void CTimeServerConfig::getDameon(TDaemon& daemon) const +{ + daemon = m_daemon; +} + +unsigned int CTimeServerConfig::getRepeaterCount() const +{ + return m_repeaters.size(); +} + +void CTimeServerConfig::getRepeater(unsigned int idx, TRepeater& repeater) const +{ + repeater = *(m_repeaters[idx]); +} + +void CTimeServerConfig::getPaths(TPaths& paths) const +{ + paths = m_paths; +} diff --git a/DGWTimeServer/TimeServerConfig.h b/DGWTimeServer/TimeServerConfig.h index d17a650..a28d36d 100644 --- a/DGWTimeServer/TimeServerConfig.h +++ b/DGWTimeServer/TimeServerConfig.h @@ -33,7 +33,6 @@ typedef struct { } TTimeServer; typedef struct { - bool enabled; std::string band; } TRepeater; @@ -43,6 +42,9 @@ typedef struct { std::string user; } TDaemon; +typedef struct { + std::string data; +} TPaths; class CTimeServerConfig { @@ -51,15 +53,22 @@ public: ~CTimeServerConfig(); bool load(); + void getTimeServer(TTimeServer& timeserver) const; + void getDameon(TDaemon& daemon) const; + unsigned int getRepeaterCount() const; + void getRepeater(unsigned int idx, TRepeater& repeater) const; + void getPaths(TPaths& paths) const; private: bool open(CConfig & cfg); bool loadRepeaters(const CConfig & cfg); bool loadTimeServer(const CConfig & cfg); bool loadDaemon(const CConfig & cfg); + bool loadPaths(const CConfig & cfg); std::string m_fileName; std::vector m_repeaters; TTimeServer m_timeServer; TDaemon m_daemon; + TPaths m_paths; }; diff --git a/DGWTimeServer/TimeServerDefs.h b/DGWTimeServer/TimeServerDefs.h index d82a57e..6b5ca4b 100644 --- a/DGWTimeServer/TimeServerDefs.h +++ b/DGWTimeServer/TimeServerDefs.h @@ -19,7 +19,7 @@ #pragma once -const std::string APPLICATION_NAME("Time Server"); +const std::string APPLICATION_NAME("DStarGateway time Server"); enum LANGUAGE { LANG_ENGLISH_UK_1, diff --git a/DGWTimeServer/TimeServerThread.cpp b/DGWTimeServer/TimeServerThread.cpp new file mode 100644 index 0000000..2bfcccc --- /dev/null +++ b/DGWTimeServer/TimeServerThread.cpp @@ -0,0 +1,1458 @@ +/* + * Copyright (C) 2012,2013 by Jonathan Naylor G4KLX + * Copyright (C) 2022 by Geoffrey Merck F4FXL / KC3FRA + * + * 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 +#include +#include +#include +#include +#include +#include + +#include "TimeServerThread.h" +#include "DStarDefines.h" +#include "Utils.h" +#include "NetUtils.h" +#include "StringUtils.h" +#include "Log.h" + +const unsigned int MAX_FRAMES = 60U * DSTAR_FRAMES_PER_SEC; + +const unsigned int SILENCE_LENGTH = 10U; + +enum SLOW_DATA { + SD_HEADER, + SD_TEXT +}; + +CTimeServerThread::CTimeServerThread() : +CThread("Time Server"), +m_socket("", 0U), +m_callsign(), +m_callsignA(), +m_callsignB(), +m_callsignC(), +m_callsignD(), +m_callsignE(), +m_callsignG(), +m_address(), +m_language(LANG_ENGLISH_UK_1), +m_format(FORMAT_VOICE_TIME), +m_interval(INTERVAL_15MINS), +m_ambe(NULL), +m_ambeLength(0U), +m_index(), +m_seqNo(0U), +m_in(0U), +m_encoder(), +m_data(NULL), +m_killed(false), +m_dataPath("") +{ + CHeaderData::initialise(); + m_address.s_addr = INADDR_NONE; + + m_data = new CAMBEData*[MAX_FRAMES]; + + for (unsigned int i = 0U; i < MAX_FRAMES; i++) + m_data[i] = nullptr; +} + +CTimeServerThread::~CTimeServerThread() +{ + for (auto it = m_index.begin(); it != m_index.end(); it++) + delete it->second; + + delete[] m_ambe; + delete[] m_data; +} + +void * CTimeServerThread::Entry() +{ + // Wait here until we have the essentials to run + while (!m_killed && m_address.s_addr == INADDR_NONE && m_callsignA.empty() && m_callsignB.empty() && m_callsignC.empty() && m_callsignD.empty() && m_callsignE.empty()) + Sleep(500UL); // 1/2 sec + + if (m_killed) + return nullptr; + + if (m_format != FORMAT_TEXT_TIME) { + bool ret = loadAMBE(); + if (!ret) { + CLog::logWarning(("Cannot load the AMBE data, using text only time")); + m_format = FORMAT_TEXT_TIME; + } + } + + CLog::logInfo(("Starting the Time Server thread")); + + unsigned int lastMin = 0U; + + while (!m_killed) { + time_t now; + ::time(&now); + + struct tm* tm = ::localtime(&now); + + unsigned int hour = tm->tm_hour; + unsigned int min = tm->tm_min; + + if (min != lastMin) { + if (m_interval == INTERVAL_15MINS && (min == 0U || min == 15U || min == 30U || min == 45U)) + sendTime(hour, min); + else if (m_interval == INTERVAL_30MINS && (min == 0U || min == 30U)) + sendTime(hour, min); + else if (m_interval == INTERVAL_60MINS && min == 0U) + sendTime(hour, min); + } + + lastMin = min; + + Sleep(450UL); + } + + CLog::logInfo(("Stopping the Time Server thread")); + + m_socket.close(); + + return nullptr; +} + +void CTimeServerThread::kill() +{ + m_killed = true; +} + +bool CTimeServerThread::setGateway(const std::string& callsign, const std::string& rpt1, const std::string& rpt2, const std::string& rpt3, const std::string& rpt4, const std::string& address, const std::string& dataPath) +{ + m_callsign = callsign; + m_callsign.resize(LONG_CALLSIGN_LENGTH - 1U, (' ')); + + m_callsignG = m_callsign; + m_callsignG.push_back('G'); + + if (!rpt1.empty()) { + m_callsignA = m_callsign + rpt1; + } + + if (!rpt2.empty()) { + m_callsignB = m_callsign + rpt2; + } + + if (!rpt3.empty()) { + m_callsignC = m_callsign + rpt3; + } + + if (!rpt4.empty()) { + m_callsignD = m_callsign + rpt4; + } + + m_callsign.push_back(' '); + + m_address = CUDPReaderWriter::lookup(address); + m_dataPath.assign(dataPath); + + bool ret = m_socket.open(); + if (!ret) + return false; + + return true; +} + +void CTimeServerThread::setAnnouncements(LANGUAGE language, FORMAT format, INTERVAL interval) +{ + m_language = language; + m_format = format; + m_interval = interval; +} + +void CTimeServerThread::sendTime(unsigned int hour, unsigned int min) +{ + std::vector words; + + switch (m_language) { + case LANG_ENGLISH_UK_1: + words = sendTimeEnGB1(hour, min); + break; + case LANG_ENGLISH_UK_2: + words = sendTimeEnGB2(hour, min); + break; + case LANG_ENGLISH_US_1: + words = sendTimeEnUS1(hour, min); + break; + case LANG_ENGLISH_US_2: + words = sendTimeEnUS2(hour, min); + break; + case LANG_DEUTSCH_1: + words = sendTimeDeDE1(hour, min); + break; + case LANG_DEUTSCH_2: + words = sendTimeDeDE2(hour, min); + break; + case LANG_FRANCAIS: + words = sendTimeFrFR(hour, min); + break; + case LANG_NEDERLANDS: + words = sendTimeNlNL(hour, min); + break; + case LANG_SVENSKA: + words = sendTimeSeSE(hour, min); + break; + case LANG_ESPANOL: + words = sendTimeEsES(hour, min); + break; + case LANG_NORSK: + words = sendTimeNoNO(hour, min); + break; + case LANG_PORTUGUES: + words = sendTimePtPT(hour, min); + break; + default: + break; + } + + send(words, hour, min); +} + +std::vector CTimeServerThread::sendTimeEnGB1(unsigned int hour, unsigned int min) +{ + std::vector words; + + words.push_back(("It_is")); + + switch (hour) { + case 0U: + case 12U: + words.push_back(("twelve")); + break; + case 1U: + case 13U: + words.push_back(("one")); + break; + case 2U: + case 14U: + words.push_back(("two")); + break; + case 3U: + case 15U: + words.push_back(("three")); + break; + case 4U: + case 16U: + words.push_back(("four")); + break; + case 5U: + case 17U: + words.push_back(("five")); + break; + case 6U: + case 18U: + words.push_back(("six")); + break; + case 7U: + case 19U: + words.push_back(("seven")); + break; + case 8U: + case 20U: + words.push_back(("eight")); + break; + case 9U: + case 21U: + words.push_back(("nine")); + break; + case 10U: + case 22U: + words.push_back(("ten")); + break; + case 11U: + case 23U: + words.push_back(("eleven")); + break; + default: + break; + } + + switch (min) { + case 15U: + words.push_back(("fifteen")); + break; + case 30U: + words.push_back(("thirty")); + break; + case 45U: + words.push_back(("forty-five")); + break; + default: + break; + } + + if (hour >= 12U) + words.push_back(("PM")); + else + words.push_back(("AM")); + + return words; +} + +std::vector CTimeServerThread::sendTimeEnGB2(unsigned int hour, unsigned int min) +{ + std::vector words; + + words.push_back("It_is"); + + if (min == 15U) { + words.push_back(("a_quarter_past")); + } else if (min == 30U) { + words.push_back(("half_past")); + } else if (min == 45U) { + words.push_back(("a_quarter_to")); + if (hour == 23U) + hour = 0U; + else + hour++; + } + + if (hour == 0U && min == 0U) { + words.push_back(("midnight")); + } else if (hour == 12U && min == 0U) { + words.push_back(("twelve")); + words.push_back(("noon")); + } else if (hour == 0U || hour == 12U) { + words.push_back(("twelve")); + } else if (hour == 1U || hour == 13U) { + words.push_back(("one")); + } else if (hour == 2U || hour == 14U) { + words.push_back(("two")); + } else if (hour == 3U || hour == 15U) { + words.push_back(("three")); + } else if (hour == 4U || hour == 16U) { + words.push_back(("four")); + } else if (hour == 5U || hour == 17U) { + words.push_back(("five")); + } else if (hour == 6U || hour == 18U) { + words.push_back(("six")); + } else if (hour == 7U || hour == 19U) { + words.push_back(("seven")); + } else if (hour == 8U || hour == 20U) { + words.push_back(("eight")); + } else if (hour == 9U || hour == 21U) { + words.push_back(("nine")); + } else if (hour == 10U || hour == 22U) { + words.push_back(("ten")); + } else if (hour == 11U || hour == 23U) { + words.push_back(("eleven")); + } + + if (hour != 0U && hour != 12U && min == 0U) + words.push_back(("O_Clock")); + + return words; +} + +std::vector CTimeServerThread::sendTimeEnUS1(unsigned int hour, unsigned int min) +{ + std::vector words; + + words.push_back(("It_is")); + + switch (hour) { + case 0U: + case 12U: + words.push_back(("twelve")); + break; + case 1U: + case 13U: + words.push_back(("one")); + break; + case 2U: + case 14U: + words.push_back(("two")); + break; + case 3U: + case 15U: + words.push_back(("three")); + break; + case 4U: + case 16U: + words.push_back(("four")); + break; + case 5U: + case 17U: + words.push_back(("five")); + break; + case 6U: + case 18U: + words.push_back(("six")); + break; + case 7U: + case 19U: + words.push_back(("seven")); + break; + case 8U: + case 20U: + words.push_back(("eight")); + break; + case 9U: + case 21U: + words.push_back(("nine")); + break; + case 10U: + case 22U: + words.push_back(("ten")); + break; + case 11U: + case 23U: + words.push_back(("eleven")); + break; + default: + break; + } + + switch (min) { + case 15U: + words.push_back(("fifteen")); + break; + case 30U: + words.push_back(("thirty")); + break; + case 45U: + words.push_back(("forty-five")); + break; + default: + break; + } + + if (hour >= 12U) + words.push_back(("PM")); + else + words.push_back(("AM")); + + return words; +} + +std::vector CTimeServerThread::sendTimeEnUS2(unsigned int hour, unsigned int min) +{ + std::vector words; + + words.push_back(("It_is")); + + if (min == 15U) { + words.push_back(("a_quarter_past")); + } else if (min == 30U) { + words.push_back(("half_past")); + } else if (min == 45U) { + words.push_back(("a_quarter_to")); + if (hour == 23U) + hour = 0U; + else + hour++; + } + + if (hour == 0U && min == 0U) { + words.push_back(("midnight")); + } else if (hour == 12U && min == 0U) { + words.push_back(("twelve")); + words.push_back(("noon")); + } else if (hour == 0U || hour == 12U) { + words.push_back(("twelve")); + } else if (hour == 1U || hour == 13U) { + words.push_back(("one")); + } else if (hour == 2U || hour == 14U) { + words.push_back(("two")); + } else if (hour == 3U || hour == 15U) { + words.push_back(("three")); + } else if (hour == 4U || hour == 16U) { + words.push_back(("four")); + } else if (hour == 5U || hour == 17U) { + words.push_back(("five")); + } else if (hour == 6U || hour == 18U) { + words.push_back(("six")); + } else if (hour == 7U || hour == 19U) { + words.push_back(("seven")); + } else if (hour == 8U || hour == 20U) { + words.push_back(("eight")); + } else if (hour == 9U || hour == 21U) { + words.push_back(("nine")); + } else if (hour == 10U || hour == 22U) { + words.push_back(("ten")); + } else if (hour == 11U || hour == 23U) { + words.push_back(("eleven")); + } + + if (hour != 0U && hour != 12U && min == 0U) + words.push_back(("O_Clock")); + + return words; +} + +std::vector CTimeServerThread::sendTimeDeDE1(unsigned int hour, unsigned int min) +{ + std::vector words; + + words.push_back(("Es_ist")); + + switch (hour) { + case 0U: words.push_back(("null")); break; + case 1U: words.push_back(("ein")); break; + case 2U: words.push_back(("zwei")); break; + case 3U: words.push_back(("drei")); break; + case 4U: words.push_back(("vier")); break; + case 5U: words.push_back(("fuenf")); break; + case 6U: words.push_back(("sechs")); break; + case 7U: words.push_back(("sieben")); break; + case 8U: words.push_back(("acht")); break; + case 9U: words.push_back(("neun")); break; + case 10U: words.push_back(("zehn")); break; + case 11U: words.push_back(("elf")); break; + case 12U: words.push_back(("zwoelf")); break; + case 13U: words.push_back(("dreizehn")); break; + case 14U: words.push_back(("vierzehn")); break; + case 15U: words.push_back(("fuenfzehn")); break; + case 16U: words.push_back(("sechzehn")); break; + case 17U: words.push_back(("siebzehn")); break; + case 18U: words.push_back(("achtzehn")); break; + case 19U: words.push_back(("neunzehn")); break; + case 20U: words.push_back(("zwanzig")); break; + case 21U: words.push_back(("einundzwanzig")); break; + case 22U: words.push_back(("zweiundzwanzig")); break; + case 23U: words.push_back(("dreiundzwanzig")); break; + default: break; + } + + words.push_back(("Uhr")); + + switch (min) { + case 15U: + words.push_back(("fuenfzehn")); + break; + case 30U: + words.push_back(("dreissig")); + break; + case 45U: + words.push_back(("fuenfundvierzig")); + break; + default: + break; + } + + return words; +} + +std::vector CTimeServerThread::sendTimeDeDE2(unsigned int hour, unsigned int min) +{ + std::vector words; + + words.push_back(("Es_ist")); + + if (min == 15U) { + words.push_back(("viertel_nach")); + } else if (min == 30U) { + words.push_back(("halb")); + if (hour == 23U) + hour = 0U; + else + hour++; + } else if (min == 45U) { + words.push_back(("viertel_vor")); + if (hour == 23U) + hour = 0U; + else + hour++; + } + + if (hour == 0U) { + words.push_back(("null")); + } else if (hour == 1U || hour == 13U) { + words.push_back(("ein")); + } else if (hour == 2U || hour == 14U) { + words.push_back(("zwei")); + } else if (hour == 3U || hour == 15U) { + words.push_back(("drei")); + } else if (hour == 4U || hour == 16U) { + words.push_back(("vier")); + } else if (hour == 5U || hour == 17U) { + words.push_back(("fuenf")); + } else if (hour == 6U || hour == 18U) { + words.push_back(("sechs")); + } else if (hour == 7U || hour == 19U) { + words.push_back(("sieben")); + } else if (hour == 8U || hour == 20U) { + words.push_back(("acht")); + } else if (hour == 9U || hour == 21U) { + words.push_back(("neun")); + } else if (hour == 10U || hour == 22U) { + words.push_back(("zehn")); + } else if (hour == 11U || hour == 23U) { + words.push_back(("elf")); + } else if (hour == 12U) { + words.push_back(("zwoelf")); + } + + if (min == 0U) + words.push_back(("Uhr")); + + return words; +} + +std::vector CTimeServerThread::sendTimeFrFR(unsigned int hour, unsigned int min) +{ + std::vector words; + + // if (hour > 17U) + // words.push_back(("bonsoir")); + // else + // words.push_back(("bonjour")); + + words.push_back(("il_est")); + + if (min == 45U) + hour++; + + if (hour == 0U) { + words.push_back(("minuit")); + } else if (hour == 1U || hour == 13U) { + words.push_back(("une")); + } else if (hour == 2U || hour == 14U) { + words.push_back(("deux")); + } else if (hour == 3U || hour == 15U) { + words.push_back(("trois")); + } else if (hour == 4U || hour == 16U) { + words.push_back(("quatre")); + } else if (hour == 5U || hour == 17U) { + words.push_back(("cinq")); + } else if (hour == 6U || hour == 18U) { + words.push_back(("six")); + } else if (hour == 7U || hour == 19U) { + words.push_back(("sept")); + } else if (hour == 8U || hour == 20U) { + words.push_back(("huit")); + } else if (hour == 9U || hour == 21U) { + words.push_back(("neuf")); + } else if (hour == 10U || hour == 22U) { + words.push_back(("dix")); + } else if (hour == 11U || hour == 23U) { + words.push_back(("onze")); + } else if (hour == 12U) { + words.push_back(("midi")); + } + + if (hour == 1U || hour == 13U) + words.push_back(("heure")); + else if (hour != 12U && hour != 0U) + words.push_back(("heures")); + + if (min == 15U) { + words.push_back(("et_quart")); + } else if (min == 30U) { + words.push_back(("et_demie")); + } else if (min == 45U) { + words.push_back(("moins_le_quart")); + if (hour == 23U) + hour = 0U; + else + hour++; + } + + return words; +} + +std::vector CTimeServerThread::sendTimeNlNL(unsigned int hour, unsigned int min) +{ + std::vector words; + + words.push_back(("Het_is")); + + if (min == 15U) { + words.push_back(("kwart_over")); + } else if (min == 30U) { + words.push_back(("half")); + if (hour == 23U) + hour = 0U; + else + hour++; + } else if (min == 45U) { + words.push_back(("kwart_voor")); + if (hour == 23U) + hour = 0U; + else + hour++; + } + + if (hour == 0U || hour == 12U) { + words.push_back(("twaalf")); + } else if (hour == 1U || hour == 13U) { + words.push_back(("een")); + } else if (hour == 2U || hour == 14U) { + words.push_back(("twee")); + } else if (hour == 3U || hour == 15U) { + words.push_back(("drie")); + } else if (hour == 4U || hour == 16U) { + words.push_back(("vier")); + } else if (hour == 5U || hour == 17U) { + words.push_back(("vijf")); + } else if (hour == 6U || hour == 18U) { + words.push_back(("zes")); + } else if (hour == 7U || hour == 19U) { + words.push_back(("zeven")); + } else if (hour == 8U || hour == 20U) { + words.push_back(("acht")); + } else if (hour == 9U || hour == 21U) { + words.push_back(("negen")); + } else if (hour == 10U || hour == 22U) { + words.push_back(("tien")); + } else if (hour == 11U || hour == 23U) { + words.push_back(("elf")); + } + + if (min == 0U) + words.push_back(("uur")); + + return words; +} + +std::vector CTimeServerThread::sendTimeSeSE(unsigned int hour, unsigned int min) +{ + std::vector words; + + words.push_back(("Klockan_ar")); + + if (min == 15U) { + words.push_back(("kvart_over")); + } else if (min == 30U) { + words.push_back(("halv")); + if (hour == 23U) + hour = 0U; + else + hour++; + } else if (min == 45U) { + words.push_back(("kvart_i")); + if (hour == 23U) + hour = 0U; + else + hour++; + } + + if (hour == 0U || hour == 12U) { + words.push_back(("tolv")); + } else if (hour == 1U || hour == 13U) { + words.push_back(("ett")); + } else if (hour == 2U || hour == 14U) { + words.push_back(("tva")); + } else if (hour == 3U || hour == 15U) { + words.push_back(("tre")); + } else if (hour == 4U || hour == 16U) { + words.push_back(("fyra")); + } else if (hour == 5U || hour == 17U) { + words.push_back(("fem")); + } else if (hour == 6U || hour == 18U) { + words.push_back(("sex")); + } else if (hour == 7U || hour == 19U) { + words.push_back(("sju")); + } else if (hour == 8U || hour == 20U) { + words.push_back(("atta")); + } else if (hour == 9U || hour == 21U) { + words.push_back(("nio")); + } else if (hour == 10U || hour == 22U) { + words.push_back(("tio")); + } else if (hour == 11U || hour == 23U) { + words.push_back(("elva")); + } + + return words; +} + +std::vector CTimeServerThread::sendTimeEsES(unsigned int hour, unsigned int min) +{ + std::vector words; + + if (min == 45U) { + hour++; + if (hour == 24U) + hour = 0U; + } + + if (hour == 1U) + words.push_back(("Es_la")); + else if (hour == 0U || hour == 12U) + words.push_back(("Es")); + else + words.push_back(("Son_las")); + + if (hour == 0U) { + words.push_back(("medianoche")); + } else if (hour == 1U || hour == 13U) { + words.push_back(("una")); + } else if (hour == 2U || hour == 14U) { + words.push_back(("dos")); + } else if (hour == 3U || hour == 15U) { + words.push_back(("tres")); + } else if (hour == 4U || hour == 16U) { + words.push_back(("cuarto")); + } else if (hour == 5U || hour == 17U) { + words.push_back(("cinco")); + } else if (hour == 6U || hour == 18U) { + words.push_back(("seis")); + } else if (hour == 7U || hour == 19U) { + words.push_back(("siete")); + } else if (hour == 8U || hour == 20U) { + words.push_back(("ocho")); + } else if (hour == 9U || hour == 21U) { + words.push_back(("nueve")); + } else if (hour == 10U || hour == 22U) { + words.push_back(("diez")); + } else if (hour == 11U || hour == 23U) { + words.push_back(("once")); + } else { + words.push_back(("mediodia")); + } + + if (min == 15U) + words.push_back(("y_cuarto")); + else if (min == 30U) + words.push_back(("y_media")); + else if (min == 45U) + words.push_back(("menos_cuarto")); + + if (hour > 0U && hour < 12U) + words.push_back(("de_la_manana")); + else if (hour > 12U && hour < 19U) + words.push_back(("de_la_tarde")); + else if (hour >= 19U && hour <= 23U) + words.push_back(("de_la_noche")); + + return words; +} + +std::vector CTimeServerThread::sendTimeNoNO(unsigned int hour, unsigned int min) +{ + std::vector words; + + words.push_back(("Klokken_er")); + + if (min == 15U) { + words.push_back(("kvart_over")); + } else if (min == 30U) { + words.push_back(("halv")); + if (hour == 23U) + hour = 0U; + else + hour++; + } else if (min == 45U) { + words.push_back(("kvart_pa")); + if (hour == 23U) + hour = 0U; + else + hour++; + } + + if (hour == 0U || hour == 12U) { + words.push_back(("tolv")); + } else if (hour == 1U || hour == 13U) { + words.push_back(("ett")); + } else if (hour == 2U || hour == 14U) { + words.push_back(("to")); + } else if (hour == 3U || hour == 15U) { + words.push_back(("tre")); + } else if (hour == 4U || hour == 16U) { + words.push_back(("fire")); + } else if (hour == 5U || hour == 17U) { + words.push_back(("fem")); + } else if (hour == 6U || hour == 18U) { + words.push_back(("seks")); + } else if (hour == 7U || hour == 19U) { + words.push_back(("sju")); + } else if (hour == 8U || hour == 20U) { + words.push_back(("atte")); + } else if (hour == 9U || hour == 21U) { + words.push_back(("ni")); + } else if (hour == 10U || hour == 22U) { + words.push_back(("ti")); + } else if (hour == 11U || hour == 23U) { + words.push_back(("elleve")); + } + + return words; +} + +std::vector CTimeServerThread::sendTimePtPT(unsigned int hour, unsigned int min) +{ + std::vector words; + + if (min == 45U) { + hour++; + if (hour == 24U) + hour = 0U; + } + + if (hour == 1U || hour == 13U) + words.push_back(("E")); + else if (hour == 0U || hour == 12U) + words.push_back(("Es")); + else + words.push_back(("Sao")); + + if (min == 45U) { + if (hour == 0U || hour == 12U || hour == 1U || hour == 13U) + words.push_back(("quinze_para")); + else + words.push_back(("quinze_para_as")); + } + + if (hour == 0U) { + words.push_back(("meia-noite")); + } else if (hour == 1U || hour == 13U) { + words.push_back(("uma")); + } else if (hour == 2U || hour == 14U) { + words.push_back(("duas")); + } else if (hour == 3U || hour == 15U) { + words.push_back(("tres")); + } else if (hour == 4U || hour == 16U) { + words.push_back(("quatro")); + } else if (hour == 5U || hour == 17U) { + words.push_back(("cinco")); + } else if (hour == 6U || hour == 18U) { + words.push_back(("seis")); + } else if (hour == 7U || hour == 19U) { + words.push_back(("sete")); + } else if (hour == 8U || hour == 20U) { + words.push_back(("oito")); + } else if (hour == 9U || hour == 21U) { + words.push_back(("nove")); + } else if (hour == 10U || hour == 22U) { + words.push_back(("dez")); + } else if (hour == 11U || hour == 23U) { + words.push_back(("onze")); + } else { + words.push_back(("meio-dia")); + } + + if (min == 0U) + words.push_back(("hora")); + else if (min == 15U) + words.push_back(("e_quinze")); + else if (min == 30U) + words.push_back(("e_meia")); + + return words; +} + +bool CTimeServerThread::loadAMBE() +{ + std::string ambeFileName; + std::string indxFileName; + + switch (m_language) { + case LANG_ENGLISH_US_1: + case LANG_ENGLISH_US_2: + ambeFileName = "TIME_en_US.ambe"; + indxFileName = "TIME_en_US.indx"; + break; + case LANG_DEUTSCH_1: + case LANG_DEUTSCH_2: + ambeFileName = "TIME_de_DE.ambe"; + indxFileName = "TIME_de_DE.indx"; + break; + case LANG_FRANCAIS: + ambeFileName = "TIME_fr_FR.ambe"; + indxFileName = "TIME_fr_FR.indx"; + break; + case LANG_NEDERLANDS: + ambeFileName = "TIME_nl_NL.ambe"; + indxFileName = "TIME_nl_NL.indx"; + break; + case LANG_SVENSKA: + ambeFileName = "TIME_se_SE.ambe"; + indxFileName = "TIME_se_SE.indx"; + break; + case LANG_ESPANOL: + ambeFileName = "TIME_es_ES.ambe"; + indxFileName = "TIME_es_ES.indx"; + break; + case LANG_NORSK: + ambeFileName = "TIME_no_NO.ambe"; + indxFileName = "TIME_no_NO.indx"; + break; + case LANG_PORTUGUES: + ambeFileName = "TIME_pt_PT.ambe"; + indxFileName = "TIME_pt_PT.indx"; + break; + default: + ambeFileName = "TIME_en_GB.ambe"; + indxFileName = "TIME_en_GB.indx"; + break; + } + + bool ret = readAMBE(m_dataPath, ambeFileName); + if (!ret) { + delete[] m_ambe; + m_ambe = NULL; + return false; + } + + ret = readIndex(m_dataPath, indxFileName); + if (!ret) { + delete[] m_ambe; + m_ambe = NULL; + return false; + } + + return true; +} + +bool CTimeServerThread::readAMBE(const std::string& dir, const std::string& name) +{ + std::string fileName = dir + "/" + name; + struct stat sbuf; + + if (stat(fileName.c_str(), &sbuf)) { + CLog::logInfo("File %s not readable\n", fileName.c_str()); + fileName.append("/data/"); + fileName += name; + if (stat(fileName.c_str(), &sbuf)) { + CLog::logInfo("File %s not readable\n", fileName.c_str()); + return false; + } + } + unsigned int fsize = sbuf.st_size; + + FILE *file = fopen(fileName.c_str(), "rb"); + if (NULL == file) { + CLog::logInfo("Cannot open %s for reading\n", fileName.c_str()); + return false; + } + + CLog::logInfo("Reading %s\n", fileName.c_str()); + + unsigned char buffer[VOICE_FRAME_LENGTH_BYTES]; + + size_t n = fread(buffer, sizeof(unsigned char), 4, file); + if (n != 4) { + CLog::logError("Unable to read the header from %s\n", fileName.c_str()); + fclose(file); + return false; + } + + if (memcmp(buffer, "AMBE", 4)) { + CLog::logError("Invalid header from %s\n", fileName.c_str()); + fclose(file); + return false; + } + + // Length of the file minus the header + unsigned int length = fsize - 4U; + + // Hold the file data plus silence at the end + m_ambe = new unsigned char[length + SILENCE_LENGTH * VOICE_FRAME_LENGTH_BYTES]; + m_ambeLength = length / VOICE_FRAME_LENGTH_BYTES; + + // Add silence to the beginning of the buffer + unsigned char* p = m_ambe; + for (unsigned int i = 0U; i < SILENCE_LENGTH; i++, p += VOICE_FRAME_LENGTH_BYTES) + memcpy(p, NULL_AMBE_DATA_BYTES, VOICE_FRAME_LENGTH_BYTES); + + n = fread(p, 1, length, file); + if (n != length) { + CLog::logError("Unable to read the AMBE data from %s\n", fileName.c_str()); + fclose(file); + delete[] m_ambe; + m_ambe = NULL; + return false; + } + + fclose(file); + + return true; +} + +bool CTimeServerThread::readIndex(const std::string& dir, const std::string& name) +{ + std::string fileName = dir + "/" + name; + struct stat sbuf; + + if (stat(fileName.c_str(), &sbuf)) { + CLog::logInfo("File %s not readable\n", fileName.c_str()); + fileName.append("/data/"); + fileName += name; + if (stat(fileName.c_str(), &sbuf)) { + CLog::logInfo("File %s not readable\n", fileName.c_str()); + return false; + } + } + + FILE *file = fopen(fileName.c_str(), "r"); + if (NULL == file) { + CLog::logInfo("Cannot open %s for reading\n", fileName.c_str()); + return false; + } + + // Add a silence entry at the beginning + m_index[" "] = new CIndexRecord(" ", 0, SILENCE_LENGTH); + + CLog::logInfo("Reading %s\n", fileName.c_str()); + + char line[128]; + while (fgets(line, 128, file)) { + + if (strlen(line) && '#'!=line[0]) { + const std::string space(" \t\r\n"); + std::string name(strtok(line, space.c_str())); + std::string strt(strtok(NULL, space.c_str())); + std::string leng(strtok(NULL, space.c_str())); + + if (name.size() && strt.size() && leng.size()) { + unsigned long start = std::stoul(strt); + unsigned long length = std::stoul(leng); + + if (start >= m_ambeLength || (start + length) >= m_ambeLength) + CLog::logInfo("The start or end for *%s* is out of range, start: %lu, end: %lu\n", name.c_str(), start, start + length); + else + m_index[name] = new CIndexRecord(name, start + SILENCE_LENGTH, length); + } + } + } + + fclose(file); + + return true; +} + +bool CTimeServerThread::lookup(const std::string &id) +{ + CIndexRecord* info = m_index[id]; + if (info == NULL) { + // wxLogError(("Cannot find the AMBE index for *%s*"), id.c_str()); + return false; + } + + unsigned int start = info->getStart(); + unsigned int length = info->getLength(); + + SLOW_DATA slowData = SD_TEXT; + + for (unsigned int i = 0U; i < length; i++) { + unsigned char* dataIn = m_ambe + (start + i) * VOICE_FRAME_LENGTH_BYTES; + + CAMBEData* dataOut = new CAMBEData; + dataOut->setDestination(m_address, G2_DV_PORT); + dataOut->setSeq(m_seqNo); + + unsigned char buffer[DV_FRAME_LENGTH_BYTES]; + ::memcpy(buffer + 0U, dataIn, VOICE_FRAME_LENGTH_BYTES); + + // Insert sync bytes when the sequence number is zero, slow data otherwise + if (m_seqNo == 0U) { + ::memcpy(buffer + VOICE_FRAME_LENGTH_BYTES, DATA_SYNC_BYTES, DATA_FRAME_LENGTH_BYTES); + m_encoder.sync(); + + switch (slowData) { + case SD_HEADER: + slowData = SD_TEXT; + break; + case SD_TEXT: + slowData = SD_HEADER; + break; + } + } else { + switch (slowData) { + case SD_HEADER: + m_encoder.getHeaderData(buffer + VOICE_FRAME_LENGTH_BYTES); + break; + case SD_TEXT: + m_encoder.getTextData(buffer + VOICE_FRAME_LENGTH_BYTES); + break; + } + } + + dataOut->setData(buffer, DV_FRAME_LENGTH_BYTES); + + m_seqNo++; + if (m_seqNo == 21U) + m_seqNo = 0U; + + m_data[m_in] = dataOut; + m_in++; + } + + return true; +} + +void CTimeServerThread::end() +{ + CAMBEData* dataOut = new CAMBEData; + dataOut->setData(END_PATTERN_BYTES, DV_FRAME_LENGTH_BYTES); + dataOut->setDestination(m_address, G2_DV_PORT); + dataOut->setSeq(m_seqNo); + dataOut->setEnd(true); + + m_data[m_in] = dataOut; + m_in++; +} + +bool CTimeServerThread::send(const std::vector &words, unsigned int hour, unsigned int min) +{ + unsigned int idA = CHeaderData::createId(); + unsigned int idB = CHeaderData::createId(); + unsigned int idC = CHeaderData::createId(); + unsigned int idD = CHeaderData::createId(); + unsigned int idE = CHeaderData::createId(); + + CHeaderData header; + header.setMyCall1(m_callsign); + header.setRptCall1(m_callsignG); + header.setRptCall2(m_callsign); // Just for the slow data header + header.setYourCall("CQCQCQ "); + header.setDestination(m_address, G2_DV_PORT); + + std::string slowData; + switch (m_language) { + case LANG_DEUTSCH_1: + case LANG_DEUTSCH_2: + header.setMyCall2(("ZEIT")); + slowData = CStringUtils::string_format(("Es ist %02u:%02u Uhr"), hour, min); + break; + case LANG_FRANCAIS: + header.setMyCall2(("TIME")); + slowData = CStringUtils::string_format(("Il est %02u:%02u"), hour, min); + break; + case LANG_NEDERLANDS: + header.setMyCall2(("TIJD")); + slowData = CStringUtils::string_format(("Het is %02u:%02u"), hour, min); + break; + case LANG_SVENSKA: + header.setMyCall2(("TID ")); + slowData = CStringUtils::string_format(("Klockan ar %02u:%02u"), hour, min); + break; + case LANG_ENGLISH_US_1: + case LANG_ENGLISH_UK_1: + header.setMyCall2(("TIME")); + if (hour == 0U) + slowData = CStringUtils::string_format(("It is 12:%02u AM"), min); + else if (hour == 12U) + slowData = CStringUtils::string_format(("It is 12:%02u PM"), min); + else if (hour > 12U) + slowData = CStringUtils::string_format(("It is %02u:%02u PM"), hour - 12U, min); + else + slowData = CStringUtils::string_format(("It is %02u:%02u AM"), hour, min); + break; + case LANG_ESPANOL: + header.setMyCall2(("HORA")); + if (hour == 1U) + slowData = CStringUtils::string_format(("Es la %02u:%02u"), hour, min); + else + slowData = CStringUtils::string_format(("Son las %02u:%02u"), hour, min); + break; + case LANG_NORSK: + header.setMyCall2(("TID ")); + slowData = CStringUtils::string_format(("Klokken er %02u:%02u"), hour, min); + break; + case LANG_PORTUGUES: + header.setMyCall2(("HORA")); + if (hour == 1U) + slowData = CStringUtils::string_format(("E %02u:%02u"), hour, min); + else + slowData = CStringUtils::string_format(("Sao %02u:%02u"), hour, min); + break; + default: + header.setMyCall2(("TIME")); + slowData = CStringUtils::string_format(("It is %02u:%02u"), hour, min); + break; + } + + m_encoder.setHeaderData(header); + m_encoder.setTextData(slowData); + + m_in = 0U; + + if (m_format != FORMAT_TEXT_TIME) { + std::string text = words.at(0U); + for (unsigned int i = 1U; i < words.size(); i++) { + text.push_back(' '); + text += words.at(i); + } + + boost::replace_all(text, "_", " "); + CLog::logInfo(("Sending voice \"%s\", sending text \"%s\""), text.c_str(), slowData.c_str()); + + m_seqNo = 0U; + + // Build the audio + lookup((" ")); + lookup((" ")); + lookup((" ")); + lookup((" ")); + + for (unsigned int i = 0U; i < words.size(); i++) + lookup(words.at(i)); + + lookup((" ")); + lookup((" ")); + lookup((" ")); + lookup((" ")); + + end(); + } else { + CLog::logInfo(("Sending text \"%s\""), slowData.c_str()); + + for (unsigned int i = 0U; i < 21U; i++) { + CAMBEData* dataOut = new CAMBEData; + dataOut->setDestination(m_address, G2_DV_PORT); + dataOut->setSeq(i); + + unsigned char buffer[DV_FRAME_LENGTH_BYTES]; + ::memcpy(buffer + 0U, NULL_AMBE_DATA_BYTES, VOICE_FRAME_LENGTH_BYTES); + + // Insert sync bytes when the sequence number is zero, slow data otherwise + if (i == 0U) { + ::memcpy(buffer + VOICE_FRAME_LENGTH_BYTES, DATA_SYNC_BYTES, DATA_FRAME_LENGTH_BYTES); + m_encoder.sync(); + } else { + m_encoder.getTextData(buffer + VOICE_FRAME_LENGTH_BYTES); + } + + dataOut->setData(buffer, DV_FRAME_LENGTH_BYTES); + + m_data[m_in] = dataOut; + m_in++; + } + + CAMBEData* dataOut = new CAMBEData; + dataOut->setData(END_PATTERN_BYTES, DV_FRAME_LENGTH_BYTES); + dataOut->setDestination(m_address, G2_DV_PORT); + dataOut->setSeq(0U); + dataOut->setEnd(true); + + m_data[m_in] = dataOut; + m_in++; + } + + if (m_in == 0U) { + CLog::logWarning(("Not sending, no audio files loaded")); + return false; + } + + if (!m_callsignA.empty()) { + header.setRptCall2(m_callsignA); + header.setId(idA); + sendHeader(header); + } + + if (!m_callsignB.empty()) { + header.setRptCall2(m_callsignB); + header.setId(idB); + sendHeader(header); + } + + if (!m_callsignC.empty()) { + header.setRptCall2(m_callsignC); + header.setId(idC); + sendHeader(header); + } + + if (!m_callsignD.empty()) { + header.setRptCall2(m_callsignD); + header.setId(idD); + sendHeader(header); + } + + if (!m_callsignE.empty()) { + header.setRptCall2(m_callsignE); + header.setId(idE); + sendHeader(header); + } + + unsigned int out = 0U; + + auto start = std::chrono::high_resolution_clock::now(); + + for (;;) { + unsigned int needed = std::chrono::duration_cast(std::chrono::high_resolution_clock::now() - start).count(); + needed /= DSTAR_FRAME_TIME_MS; + + while (out < needed) { + CAMBEData* data = m_data[out]; + m_data[out] = NULL; + out++; + + if (!m_callsignA.empty()) { + data->setId(idA); + sendData(*data); + } + + if (!m_callsignB.empty()) { + data->setId(idB); + sendData(*data); + } + + if (!m_callsignC.empty()) { + data->setId(idC); + sendData(*data); + } + + if (!m_callsignD.empty()) { + data->setId(idD); + sendData(*data); + } + + if (!m_callsignE.empty()) { + data->setId(idE); + sendData(*data); + } + + delete data; + + if (m_in == out) + return true; + } + + Sleep(10UL); + } +} + +bool CTimeServerThread::sendHeader(const CHeaderData &header) +{ + unsigned char buffer[60U]; + unsigned int length = header.getG2Data(buffer, 60U, true); + +#if defined(DUMP_TX) + CUtils::dump(("Sending Header"), buffer, length); + return true; +#else + for (unsigned int i = 0U; i < 5U; i++) { + bool res = m_socket.write(buffer, length, header.getYourAddress(), header.getYourPort()); + if (!res) + return false; + } + + return true; +#endif +} + +bool CTimeServerThread::sendData(const CAMBEData& data) +{ + unsigned char buffer[40U]; + unsigned int length = data.getG2Data(buffer, 40U); + +#if defined(DUMP_TX) + CUtils::dump(("Sending Data"), buffer, length); + return true; +#else + return m_socket.write(buffer, length, data.getYourAddress(), data.getYourPort()); +#endif +} diff --git a/DGWTimeServer/TimeServerThread.h b/DGWTimeServer/TimeServerThread.h new file mode 100644 index 0000000..ee3f967 --- /dev/null +++ b/DGWTimeServer/TimeServerThread.h @@ -0,0 +1,122 @@ +/* + * Copyright (C) 2012,2013 by Jonathan Naylor G4KLX + * Copyright (C) 2022 by Geoffrey Merck F4FXL / KC3FRA + * + * 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. + */ + +#pragma once + +#include + +#include "SlowDataEncoder.h" +#include "UDPReaderWriter.h" +#include "TimeServerDefs.h" +#include "HeaderData.h" +#include "AMBEData.h" +#include "Thread.h" + +class CIndexRecord { +public: + CIndexRecord(const std::string& name, unsigned int start, unsigned int length) : + m_name(name), + m_start(start), + m_length(length) + { + } + + std::string getName() const + { + return m_name; + } + + unsigned int getStart() const + { + return m_start; + } + + unsigned int getLength() const + { + return m_length; + } + +private: + std::string m_name; + unsigned int m_start; + unsigned int m_length; +}; + + +class CTimeServerThread : public CThread +{ +public: + CTimeServerThread(); + ~CTimeServerThread(); + + bool setGateway(const std::string& callsign, const std::string& rpt1, const std::string& rpt2, const std::string& rpt3, const std::string& rpt4, const std::string& address, const std::string& dataPath); + void setAnnouncements(LANGUAGE language, FORMAT format, INTERVAL interval); + + void * Entry(); + void kill(); + +private: + CUDPReaderWriter m_socket; + std::string m_callsign; + std::string m_callsignA; + std::string m_callsignB; + std::string m_callsignC; + std::string m_callsignD; + std::string m_callsignE; + std::string m_callsignG; + in_addr m_address; + LANGUAGE m_language; + FORMAT m_format; + INTERVAL m_interval; + unsigned char* m_ambe; + unsigned int m_ambeLength; + std::unordered_map m_index; + unsigned int m_seqNo; + unsigned int m_in; + CSlowDataEncoder m_encoder; + CAMBEData** m_data; + bool m_killed; + std::string m_dataPath; + + void sendTime(unsigned int hour, unsigned int min); + + std::vector sendTimeEnGB1(unsigned int hour, unsigned int min); + std::vector sendTimeEnGB2(unsigned int hour, unsigned int min); + std::vector sendTimeEnUS1(unsigned int hour, unsigned int min); + std::vector sendTimeEnUS2(unsigned int hour, unsigned int min); + std::vector sendTimeDeDE1(unsigned int hour, unsigned int min); + std::vector sendTimeDeDE2(unsigned int hour, unsigned int min); + std::vector sendTimeFrFR(unsigned int hour, unsigned int min); + std::vector sendTimeNlNL(unsigned int hour, unsigned int min); + std::vector sendTimeSeSE(unsigned int hour, unsigned int min); + std::vector sendTimeEsES(unsigned int hour, unsigned int min); + std::vector sendTimeNoNO(unsigned int hour, unsigned int min); + std::vector sendTimePtPT(unsigned int hour, unsigned int min); + + bool send(const std::vector& words, unsigned int hour, unsigned int min); + bool sendHeader(const CHeaderData& header); + bool sendData(const CAMBEData& data); + + bool loadAMBE(); + bool readAMBE(const std::string& dir, const std::string& name); + bool readIndex(const std::string& dir, const std::string& name); + + bool lookup(const std::string& id); + void end(); +}; diff --git a/DGWTimeServer/example.cfg b/DGWTimeServer/example.cfg index cc6b0cf..27a40df 100644 --- a/DGWTimeServer/example.cfg +++ b/DGWTimeServer/example.cfg @@ -5,6 +5,9 @@ format= # possible values are voice, text, voiceandtext, defaults to voice a language= # valid values: english_uk_1, english_uk_2, english_us_1, english_us_2, deutsch_1, deutsch_2, francais, nederlands, svenska, espanol, norsk, portugues. Defaults to english_uk_1 interval= # valid values are 15, 30 and 60, defaults to 30 +[Paths] +data=/usr/local/share/dstargateway.d/ #Path where the data (hostfiles, audio files etc) can be found + # Up to 4 repeaters can be enabled to transmit time beacons [Repeater_1] enabled=true # enable time beacons on this repeater