diff --git a/Conf.cpp b/Conf.cpp index 0a9cf5a..18e20b6 100644 --- a/Conf.cpp +++ b/Conf.cpp @@ -184,6 +184,7 @@ m_fmCallsignHighLevel(35.0F), m_fmCallsignLowLevel(15.0F), m_fmCallsignAtStart(true), m_fmCallsignAtEnd(true), +m_fmCallsignAtLatch(true), m_fmRFAck("K"), m_fmExtAck("N"), m_fmAckSpeed(20U), @@ -199,6 +200,7 @@ m_fmCTCSSLevel(2.0F), m_fmKerchunkTime(0U), m_fmHangTime(7U), m_fmUseCOS(true), +m_fmCOSInvert(false), m_fmRFAudioBoost(1U), m_fmMaxDevLevel(90.0F), m_fmExtAudioBoost(1U), @@ -722,6 +724,8 @@ bool CConf::read() m_fmCallsignAtStart = ::atoi(value) == 1; else if (::strcmp(key, "CallsignAtEnd") == 0) m_fmCallsignAtEnd = ::atoi(value) == 1; + else if (::strcmp(key, "CallsignAtLatch") == 0) + m_fmCallsignAtLatch = ::atoi(value) == 1; else if (::strcmp(key, "RFAck") == 0) { // Convert the ack to upper case for (unsigned int i = 0U; value[i] != 0; i++) @@ -758,6 +762,8 @@ bool CConf::read() m_fmHangTime = (unsigned int)::atoi(value); else if (::strcmp(key, "UseCOS") == 0) m_fmUseCOS = ::atoi(value) == 1; + else if (::strcmp(key, "COSInvert") == 0) + m_fmCOSInvert = ::atoi(value) == 1; else if (::strcmp(key, "RFAudioBoost") == 0) m_fmRFAudioBoost = (unsigned int)::atoi(value); else if (::strcmp(key, "MaxDevLevel") == 0) @@ -1561,6 +1567,11 @@ bool CConf::getFMCallsignAtEnd() const return m_fmCallsignAtEnd; } +bool CConf::getFMCallsignAtLatch() const +{ + return m_fmCallsignAtLatch; +} + std::string CConf::getFMRFAck() const { return m_fmRFAck; @@ -1636,6 +1647,11 @@ bool CConf::getFMUseCOS() const return m_fmUseCOS; } +bool CConf::getFMCOSInvert() const +{ + return m_fmCOSInvert; +} + unsigned int CConf::getFMRFAudioBoost() const { return m_fmRFAudioBoost; diff --git a/Conf.h b/Conf.h index 66ff4fa..c60d943 100644 --- a/Conf.h +++ b/Conf.h @@ -182,6 +182,7 @@ public: float getFMCallsignLowLevel() const; bool getFMCallsignAtStart() const; bool getFMCallsignAtEnd() const; + bool getFMCallsignAtLatch() const; std::string getFMRFAck() const; std::string getFMExtAck() const; unsigned int getFMAckSpeed() const; @@ -197,6 +198,7 @@ public: unsigned int getFMKerchunkTime() const; unsigned int getFMHangTime() const; bool getFMUseCOS() const; + bool getFMCOSInvert() const; unsigned int getFMRFAudioBoost() const; float getFMMaxDevLevel() const; unsigned int getFMExtAudioBoost() const; @@ -449,6 +451,7 @@ private: float m_fmCallsignLowLevel; bool m_fmCallsignAtStart; bool m_fmCallsignAtEnd; + bool m_fmCallsignAtLatch; std::string m_fmRFAck; std::string m_fmExtAck; unsigned int m_fmAckSpeed; @@ -464,6 +467,7 @@ private: unsigned int m_fmKerchunkTime; unsigned int m_fmHangTime; bool m_fmUseCOS; + bool m_fmCOSInvert; unsigned int m_fmRFAudioBoost; float m_fmMaxDevLevel; unsigned int m_fmExtAudioBoost; diff --git a/Log.cpp b/Log.cpp index fc37ebf..f600b90 100644 --- a/Log.cpp +++ b/Log.cpp @@ -22,6 +22,7 @@ #include #else #include +#include #endif #include @@ -36,6 +37,7 @@ static std::string m_filePath; static std::string m_fileRoot; static FILE* m_fpLog = NULL; +static bool m_daemon = false; static unsigned int m_displayLevel = 2U; @@ -45,6 +47,8 @@ static char LEVELS[] = " DMIWEF"; static bool LogOpen() { + bool status = false; + if (m_fileLevel == 0U) return true; @@ -68,18 +72,28 @@ static bool LogOpen() ::sprintf(filename, "%s/%s-%04d-%02d-%02d.log", m_filePath.c_str(), m_fileRoot.c_str(), tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday); #endif - m_fpLog = ::fopen(filename, "a+t"); + if ((m_fpLog = ::fopen(filename, "a+t")) != NULL) + { + status = true; + +#if !defined(_WIN32) && !defined(_WIN64) + if (m_daemon) + dup2(fileno(m_fpLog), fileno(stderr)); +#endif + } + m_tm = *tm; - return m_fpLog != NULL; + return status; } -bool LogInitialise(const std::string& filePath, const std::string& fileRoot, unsigned int fileLevel, unsigned int displayLevel) +bool LogInitialise(bool daemon, const std::string& filePath, const std::string& fileRoot, unsigned int fileLevel, unsigned int displayLevel) { m_filePath = filePath; m_fileRoot = fileRoot; m_fileLevel = fileLevel; m_displayLevel = displayLevel; + m_daemon = daemon; return ::LogOpen(); } diff --git a/Log.h b/Log.h index d671ef9..0d00653 100644 --- a/Log.h +++ b/Log.h @@ -30,7 +30,7 @@ extern void Log(unsigned int level, const char* fmt, ...); -extern bool LogInitialise(const std::string& filePath, const std::string& fileRoot, unsigned int fileLevel, unsigned int displayLevel); +extern bool LogInitialise(bool daemon, const std::string& filePath, const std::string& fileRoot, unsigned int fileLevel, unsigned int displayLevel); extern void LogFinalise(); #endif diff --git a/MMDVM.ini b/MMDVM.ini index 9774018..b6042bf 100644 --- a/MMDVM.ini +++ b/MMDVM.ini @@ -152,6 +152,7 @@ CallsignHighLevel=50 CallsignLowLevel=20 CallsignAtStart=1 CallsignAtEnd=1 +CallsignAtLatch=0 RFAck=K ExtAck=N AckSpeed=20 @@ -167,6 +168,7 @@ CTCSSLevel=20 KerchunkTime=0 HangTime=7 UseCOS=1 +COSInvert=0 RFAudioBoost=1 MaxDevLevel=90 ExtAudioBoost=1 diff --git a/MMDVMHost.cpp b/MMDVMHost.cpp index b688692..bb96545 100644 --- a/MMDVMHost.cpp +++ b/MMDVMHost.cpp @@ -237,7 +237,11 @@ int CMMDVMHost::run() #endif #endif - ret = ::LogInitialise(m_conf.getLogFilePath(), m_conf.getLogFileRoot(), m_conf.getLogFileLevel(), m_conf.getLogDisplayLevel()); +#if !defined(_WIN32) && !defined(_WIN64) + ret = ::LogInitialise(m_daemon, m_conf.getLogFilePath(), m_conf.getLogFileRoot(), m_conf.getLogFileLevel(), m_conf.getLogDisplayLevel()); +#else + ret = ::LogInitialise(false, m_conf.getLogFilePath(), m_conf.getLogFileRoot(), m_conf.getLogFileLevel(), m_conf.getLogDisplayLevel()); +#endif if (!ret) { ::fprintf(stderr, "MMDVMHost: unable to open the log file\n"); return 1; @@ -247,7 +251,6 @@ int CMMDVMHost::run() if (m_daemon) { ::close(STDIN_FILENO); ::close(STDOUT_FILENO); - ::close(STDERR_FILENO); } #endif @@ -1223,6 +1226,7 @@ bool CMMDVMHost::createModem() float callsignLowLevel = m_conf.getFMCallsignLowLevel(); bool callsignAtStart = m_conf.getFMCallsignAtStart(); bool callsignAtEnd = m_conf.getFMCallsignAtEnd(); + bool callsignAtLatch = m_conf.getFMCallsignAtLatch(); std::string rfAck = m_conf.getFMRFAck(); std::string extAck = m_conf.getFMExtAck(); unsigned int ackSpeed = m_conf.getFMAckSpeed(); @@ -1238,6 +1242,7 @@ bool CMMDVMHost::createModem() unsigned int kerchunkTime = m_conf.getFMKerchunkTime(); unsigned int hangTime = m_conf.getFMHangTime(); bool useCOS = m_conf.getFMUseCOS(); + bool cosInvert = m_conf.getFMCOSInvert(); unsigned int rfAudioBoost = m_conf.getFMRFAudioBoost(); float maxDevLevel = m_conf.getFMMaxDevLevel(); unsigned int extAudioBoost = m_conf.getFMExtAudioBoost(); @@ -1252,6 +1257,7 @@ bool CMMDVMHost::createModem() LogInfo(" Callsign Low Level: %.1f%%", callsignLowLevel); LogInfo(" Callsign At Start: %s", callsignAtStart ? "yes" : "no"); LogInfo(" Callsign At End: %s", callsignAtEnd ? "yes" : "no"); + LogInfo(" Callsign At Latch: %s", callsignAtLatch ? "yes" : "no"); LogInfo(" RF Ack: %s", rfAck.c_str()); // LogInfo(" Ext. Ack: %s", extAck.c_str()); LogInfo(" Ack Speed: %uWPM", ackSpeed); @@ -1267,13 +1273,14 @@ bool CMMDVMHost::createModem() LogInfo(" Kerchunk Time: %us", kerchunkTime); LogInfo(" Hang Time: %us", hangTime); LogInfo(" Use COS: %s", useCOS ? "yes" : "no"); + LogInfo(" COS Invert: %s", cosInvert ? "yes" : "no"); LogInfo(" RF Audio Boost: x%u", rfAudioBoost); LogInfo(" Max. Deviation Level: %.1f%%", maxDevLevel); // LogInfo(" Ext. Audio Boost: x%u", extAudioBoost); - m_modem->setFMCallsignParams(callsign, callsignSpeed, callsignFrequency, callsignTime, callsignHoldoff, callsignHighLevel, callsignLowLevel, callsignAtStart, callsignAtEnd); + m_modem->setFMCallsignParams(callsign, callsignSpeed, callsignFrequency, callsignTime, callsignHoldoff, callsignHighLevel, callsignLowLevel, callsignAtStart, callsignAtEnd, callsignAtLatch); m_modem->setFMAckParams(rfAck, ackSpeed, ackFrequency, ackMinTime, ackDelay, ackLevel); - m_modem->setFMMiscParams(timeout, timeoutLevel, ctcssFrequency, ctcssThreshold, ctcssLevel, kerchunkTime, hangTime, useCOS, rfAudioBoost, maxDevLevel); + m_modem->setFMMiscParams(timeout, timeoutLevel, ctcssFrequency, ctcssThreshold, ctcssLevel, kerchunkTime, hangTime, useCOS, cosInvert, rfAudioBoost, maxDevLevel); } bool ret = m_modem->open(); @@ -1983,6 +1990,58 @@ void CMMDVMHost::remoteControl() if (m_nxdn != NULL) processModeCommand(MODE_NXDN, m_nxdnRFModeHang); break; + case RCD_MODE_FM: + if (m_fmEnabled != false) + processModeCommand(MODE_FM, 0); + break; + case RCD_ENABLE_DSTAR: + if (m_dstar != NULL && m_dstarEnabled==false) + processEnableCommand(m_dstarEnabled, true); + break; + case RCD_ENABLE_DMR: + if (m_dmr != NULL && m_dmrEnabled==false) + processEnableCommand(m_dmrEnabled, true); + break; + case RCD_ENABLE_YSF: + if (m_ysf != NULL && m_ysfEnabled==false) + processEnableCommand(m_ysfEnabled, true); + break; + case RCD_ENABLE_P25: + if (m_p25 != NULL && m_p25Enabled==false) + processEnableCommand(m_p25Enabled, true); + break; + case RCD_ENABLE_NXDN: + if (m_nxdn != NULL && m_nxdnEnabled==false) + processEnableCommand(m_nxdnEnabled, true); + break; + case RCD_ENABLE_FM: + if (m_fmEnabled==false) + processEnableCommand(m_fmEnabled, true); + break; + case RCD_DISABLE_DSTAR: + if (m_dstar != NULL && m_dstarEnabled==true) + processEnableCommand(m_dstarEnabled, false); + break; + case RCD_DISABLE_DMR: + if (m_dmr != NULL && m_dmrEnabled==true) + processEnableCommand(m_dmrEnabled, false); + break; + case RCD_DISABLE_YSF: + if (m_ysf != NULL && m_ysfEnabled==true) + processEnableCommand(m_ysfEnabled, false); + break; + case RCD_DISABLE_P25: + if (m_p25 != NULL && m_p25Enabled==true) + processEnableCommand(m_p25Enabled, false); + break; + case RCD_DISABLE_NXDN: + if (m_nxdn != NULL && m_nxdnEnabled==true) + processEnableCommand(m_nxdnEnabled, false); + break; + case RCD_DISABLE_FM: + if (m_fmEnabled == true) + processEnableCommand(m_fmEnabled, false); + break; case RCD_PAGE: if (m_pocsag != NULL) { unsigned int ric = m_remoteControl->getArgUInt(0U); @@ -2028,3 +2087,12 @@ void CMMDVMHost::processModeCommand(unsigned char mode, unsigned int timeout) setMode(mode); } + +void CMMDVMHost::processEnableCommand(bool& mode, bool enabled) +{ + LogDebug("Setting mode current=%s new=%s",mode ? "true" : "false",enabled ? "true" : "false"); + mode=enabled; + m_modem->setModeParams(m_dstarEnabled, m_dmrEnabled, m_ysfEnabled, m_p25Enabled, m_nxdnEnabled, m_pocsagEnabled, m_fmEnabled); + if (!m_modem->writeConfig()) + LogError("Cannot write Config to MMDVM"); +} diff --git a/MMDVMHost.h b/MMDVMHost.h index de22de2..17d6786 100644 --- a/MMDVMHost.h +++ b/MMDVMHost.h @@ -117,6 +117,7 @@ private: void remoteControl(); void processModeCommand(unsigned char mode, unsigned int timeout); + void processEnableCommand(bool& mode, bool enabled); void setMode(unsigned char mode); diff --git a/Modem.cpp b/Modem.cpp index e6f1ebb..306f794 100644 --- a/Modem.cpp +++ b/Modem.cpp @@ -180,6 +180,7 @@ m_fmCallsignHighLevel(35.0F), m_fmCallsignLowLevel(15.0F), m_fmCallsignAtStart(true), m_fmCallsignAtEnd(true), +m_fmCallsignAtLatch(true), m_fmRfAck("K"), m_fmAckSpeed(20U), m_fmAckFrequency(1750U), @@ -194,6 +195,7 @@ m_fmCtcssLevel(10.0F), m_fmKerchunkTime(0U), m_fmHangTime(5U), m_fmUseCOS(true), +m_fmCOSInvert(false), m_fmRFAudioBoost(1U), m_fmMaxDevLevel(90.0F) { @@ -1503,6 +1505,11 @@ bool CModem::readStatus() return m_serial->write(buffer, 3U) == 3; } +bool CModem::writeConfig() +{ + return setConfig(); +} + bool CModem::setConfig() { assert(m_serial != NULL); @@ -1898,7 +1905,7 @@ bool CModem::writeDMRShortLC(const unsigned char* lc) return m_serial->write(buffer, 12U) == 12; } -void CModem::setFMCallsignParams(const std::string& callsign, unsigned int callsignSpeed, unsigned int callsignFrequency, unsigned int callsignTime, unsigned int callsignHoldoff, float callsignHighLevel, float callsignLowLevel, bool callsignAtStart, bool callsignAtEnd) +void CModem::setFMCallsignParams(const std::string& callsign, unsigned int callsignSpeed, unsigned int callsignFrequency, unsigned int callsignTime, unsigned int callsignHoldoff, float callsignHighLevel, float callsignLowLevel, bool callsignAtStart, bool callsignAtEnd, bool callsignAtLatch) { m_fmCallsign = callsign; m_fmCallsignSpeed = callsignSpeed; @@ -1909,6 +1916,7 @@ void CModem::setFMCallsignParams(const std::string& callsign, unsigned int calls m_fmCallsignLowLevel = callsignLowLevel; m_fmCallsignAtStart = callsignAtStart; m_fmCallsignAtEnd = callsignAtEnd; + m_fmCallsignAtLatch = callsignAtLatch; } void CModem::setFMAckParams(const std::string& rfAck, unsigned int ackSpeed, unsigned int ackFrequency, unsigned int ackMinTime, unsigned int ackDelay, float ackLevel) @@ -1921,7 +1929,7 @@ void CModem::setFMAckParams(const std::string& rfAck, unsigned int ackSpeed, uns m_fmAckLevel = ackLevel; } -void CModem::setFMMiscParams(unsigned int timeout, float timeoutLevel, float ctcssFrequency, unsigned int ctcssThreshold, float ctcssLevel, unsigned int kerchunkTime, unsigned int hangTime, bool useCOS, unsigned int rfAudioBoost, float maxDevLevel) +void CModem::setFMMiscParams(unsigned int timeout, float timeoutLevel, float ctcssFrequency, unsigned int ctcssThreshold, float ctcssLevel, unsigned int kerchunkTime, unsigned int hangTime, bool useCOS, bool cosInvert, unsigned int rfAudioBoost, float maxDevLevel) { m_fmTimeout = timeout; m_fmTimeoutLevel = timeoutLevel; @@ -1934,6 +1942,8 @@ void CModem::setFMMiscParams(unsigned int timeout, float timeoutLevel, float ctc m_fmHangTime = hangTime; m_fmUseCOS = useCOS; + m_fmCOSInvert = cosInvert; + m_fmRFAudioBoost = rfAudioBoost; m_fmMaxDevLevel = maxDevLevel; } @@ -1962,6 +1972,8 @@ bool CModem::setFMCallsignParams() buffer[9U] |= 0x01U; if (m_fmCallsignAtEnd) buffer[9U] |= 0x02U; + if (m_fmCallsignAtLatch) + buffer[9U] |= 0x04U; for (unsigned int i = 0U; i < m_fmCallsign.size(); i++) buffer[10U + i] = m_fmCallsign.at(i); @@ -2072,6 +2084,8 @@ bool CModem::setFMMiscParams() buffer[10U] = 0x00U; if (m_fmUseCOS) buffer[10U] |= 0x01U; + if (m_fmCOSInvert) + buffer[10U] |= 0x02U; buffer[11U] = m_fmRFAudioBoost; diff --git a/Modem.h b/Modem.h index 95314f3..4525e71 100644 --- a/Modem.h +++ b/Modem.h @@ -47,9 +47,9 @@ public: virtual void setNXDNParams(unsigned int txHang); virtual void setTransparentDataParams(unsigned int sendFrameType); - virtual void setFMCallsignParams(const std::string& callsign, unsigned int callsignSpeed, unsigned int callsignFrequency, unsigned int callsignTime, unsigned int callsignHoldoff, float callsignHighLevel, float callsignLowLevel, bool callsignAtStart, bool callsignAtEnd); + virtual void setFMCallsignParams(const std::string& callsign, unsigned int callsignSpeed, unsigned int callsignFrequency, unsigned int callsignTime, unsigned int callsignHoldoff, float callsignHighLevel, float callsignLowLevel, bool callsignAtStart, bool callsignAtEnd, bool callsignAtLatch); virtual void setFMAckParams(const std::string& rfAck, unsigned int ackSpeed, unsigned int ackFrequency, unsigned int ackMinTime, unsigned int ackDelay, float ackLevel); - virtual void setFMMiscParams(unsigned int timeout, float timeoutLevel, float ctcssFrequency, unsigned int ctcssThreshold, float ctcssLevel, unsigned int kerchunkTime, unsigned int hangTime, bool useCOS, unsigned int rfAudioBoost, float maxDevLevel); + virtual void setFMMiscParams(unsigned int timeout, float timeoutLevel, float ctcssFrequency, unsigned int ctcssThreshold, float ctcssLevel, unsigned int kerchunkTime, unsigned int hangTime, bool useCOS, bool cosInvert, unsigned int rfAudioBoost, float maxDevLevel); virtual bool open(); @@ -77,6 +77,7 @@ public: virtual bool hasLockout() const; virtual bool hasError() const; + virtual bool writeConfig(); virtual bool writeDStarData(const unsigned char* data, unsigned int length); virtual bool writeDMRData1(const unsigned char* data, unsigned int length); virtual bool writeDMRData2(const unsigned char* data, unsigned int length); @@ -197,6 +198,7 @@ private: float m_fmCallsignLowLevel; bool m_fmCallsignAtStart; bool m_fmCallsignAtEnd; + bool m_fmCallsignAtLatch; std::string m_fmRfAck; unsigned int m_fmAckSpeed; unsigned int m_fmAckFrequency; @@ -211,6 +213,7 @@ private: unsigned int m_fmKerchunkTime; unsigned int m_fmHangTime; bool m_fmUseCOS; + bool m_fmCOSInvert; unsigned int m_fmRFAudioBoost; float m_fmMaxDevLevel; diff --git a/RemoteCommand.cpp b/RemoteCommand.cpp index bf3fb1e..fd96e46 100644 --- a/RemoteCommand.cpp +++ b/RemoteCommand.cpp @@ -51,7 +51,7 @@ int main(int argc, char** argv) CRemoteCommand::CRemoteCommand(unsigned int port) : m_port(port) { - ::LogInitialise(".", "RemoteCommand", 2U, 2U); + ::LogInitialise(false, ".", "RemoteCommand", 2U, 2U); } CRemoteCommand::~CRemoteCommand() diff --git a/RemoteControl.cpp b/RemoteControl.cpp index 2aca7ff..15ac46c 100644 --- a/RemoteControl.cpp +++ b/RemoteControl.cpp @@ -25,6 +25,8 @@ #include const unsigned int SET_MODE_ARGS = 2U; +const unsigned int ENABLE_ARGS = 2U; +const unsigned int DISABLE_ARGS = 2U; const unsigned int PAGE_ARGS = 3U; const unsigned int CW_ARGS = 2U; @@ -87,6 +89,32 @@ REMOTE_COMMAND CRemoteControl::getCommand() m_command = RCD_MODE_P25; else if (m_args.at(1U) == "nxdn") m_command = RCD_MODE_NXDN; + } else if (m_args.at(0U) == "enable" && m_args.size() >= ENABLE_ARGS) { + if (m_args.at(1U) == "dstar") + m_command = RCD_ENABLE_DSTAR; + else if (m_args.at(1U) == "dmr") + m_command = RCD_ENABLE_DMR; + else if (m_args.at(1U) == "ysf") + m_command = RCD_ENABLE_YSF; + else if (m_args.at(1U) == "p25") + m_command = RCD_ENABLE_P25; + else if (m_args.at(1U) == "nxdn") + m_command = RCD_ENABLE_NXDN; + else if (m_args.at(1U) == "fm") + m_command = RCD_ENABLE_FM; + } else if (m_args.at(0U) == "disable" && m_args.size() >= DISABLE_ARGS) { + if (m_args.at(1U) == "dstar") + m_command = RCD_DISABLE_DSTAR; + else if (m_args.at(1U) == "dmr") + m_command = RCD_DISABLE_DMR; + else if (m_args.at(1U) == "ysf") + m_command = RCD_DISABLE_YSF; + else if (m_args.at(1U) == "p25") + m_command = RCD_DISABLE_P25; + else if (m_args.at(1U) == "nxdn") + m_command = RCD_DISABLE_NXDN; + else if (m_args.at(1U) == "fm") + m_command = RCD_DISABLE_FM; } else if (m_args.at(0U) == "page" && m_args.size() >= PAGE_ARGS) { // Page command is in the form of "page " m_command = RCD_PAGE; diff --git a/RemoteControl.h b/RemoteControl.h index c5be6ba..53b9820 100644 --- a/RemoteControl.h +++ b/RemoteControl.h @@ -33,8 +33,21 @@ enum REMOTE_COMMAND { RCD_MODE_YSF, RCD_MODE_P25, RCD_MODE_NXDN, + RCD_MODE_FM, + RCD_ENABLE_DSTAR, + RCD_ENABLE_DMR, + RCD_ENABLE_YSF, + RCD_ENABLE_P25, + RCD_ENABLE_NXDN, + RCD_ENABLE_FM, + RCD_DISABLE_DSTAR, + RCD_DISABLE_DMR, + RCD_DISABLE_YSF, + RCD_DISABLE_P25, + RCD_DISABLE_NXDN, + RCD_DISABLE_FM, RCD_PAGE, - RCD_CW + RCD_CW }; class CRemoteControl {