From 9ed2c00b0c69c884e18fb92dbfbe3fca72194bee Mon Sep 17 00:00:00 2001 From: Jonathan Naylor Date: Mon, 16 May 2016 21:57:32 +0100 Subject: [PATCH] First go at System Fusion networking. --- Display.cpp | 5 +- Display.h | 4 +- HD44780.cpp | 3 +- HD44780.h | 2 +- MMDVMHost.cpp | 52 +++++- MMDVMHost.h | 3 + MMDVMHost.vcxproj | 2 + MMDVMHost.vcxproj.filters | 6 + Makefile | 2 +- Makefile.Pi.Adafruit | 2 +- Makefile.Pi.HD44780 | 2 +- Makefile.Pi.OLED | 2 +- Nextion.cpp | 7 +- Nextion.h | 2 +- NullDisplay.cpp | 2 +- NullDisplay.h | 2 +- OLED.cpp | 6 +- OLED.h | 2 +- TFTSerial.cpp | 7 +- TFTSerial.h | 2 +- YSFControl.cpp | 330 ++++++++++++++++++++++---------------- YSFControl.h | 31 ++-- YSFDefines.h | 2 + YSFNetwork.cpp | 204 +++++++++++++++++++++++ YSFNetwork.h | 64 ++++++++ 25 files changed, 573 insertions(+), 173 deletions(-) create mode 100644 YSFNetwork.cpp create mode 100644 YSFNetwork.h diff --git a/Display.cpp b/Display.cpp index dcedceb0a..c19f8ac45 100644 --- a/Display.cpp +++ b/Display.cpp @@ -131,15 +131,16 @@ void CDisplay::clearDMR(unsigned int slotNo) } } -void CDisplay::writeFusion(const char* source, const char* dest) +void CDisplay::writeFusion(const char* source, const char* dest, const char* type) { assert(source != NULL); assert(dest != NULL); + assert(type != NULL); m_timer1.start(); m_mode1 = MODE_IDLE; - writeFusionInt(source, dest); + writeFusionInt(source, dest, type); } void CDisplay::clearFusion() diff --git a/Display.h b/Display.h index ab5275197..431a5347a 100644 --- a/Display.h +++ b/Display.h @@ -41,7 +41,7 @@ class CDisplay void writeDMR(unsigned int slotNo, const std::string& src, bool group, const std::string& dst, const char* type); void clearDMR(unsigned int slotNo); - void writeFusion(const char* source, const char* dest); + void writeFusion(const char* source, const char* dest, const char* type); void clearFusion(); virtual void close() = 0; @@ -59,7 +59,7 @@ class CDisplay virtual void writeDMRInt(unsigned int slotNo, const std::string& src, bool group, const std::string& dst, const char* type) = 0; virtual void clearDMRInt(unsigned int slotNo) = 0; - virtual void writeFusionInt(const char* source, const char* dest) = 0; + virtual void writeFusionInt(const char* source, const char* dest, const char* type) = 0; virtual void clearFusionInt() = 0; private: diff --git a/HD44780.cpp b/HD44780.cpp index 41a28117a..a18e098bc 100644 --- a/HD44780.cpp +++ b/HD44780.cpp @@ -593,10 +593,11 @@ void CHD44780::clearDMRInt(unsigned int slotNo) } } -void CHD44780::writeFusionInt(const char* source, const char* dest) +void CHD44780::writeFusionInt(const char* source, const char* dest, const char* type) { assert(source != NULL); assert(dest != NULL); + assert(type != NULL); #ifdef ADAFRUIT_DISPLAY adafruitLCDColour(AC_RED); diff --git a/HD44780.h b/HD44780.h index b71456a63..9f8ff3f83 100644 --- a/HD44780.h +++ b/HD44780.h @@ -70,7 +70,7 @@ class CHD44780 : public CDisplay virtual void writeDMRInt(unsigned int slotNo, const std::string& src, bool group, const std::string& dst, const char* type); virtual void clearDMRInt(unsigned int slotNo); - virtual void writeFusionInt(const char* source, const char* dest); + virtual void writeFusionInt(const char* source, const char* dest, const char* type); virtual void clearFusionInt(); private: diff --git a/MMDVMHost.cpp b/MMDVMHost.cpp index 5ba5cf8f3..5f93babb1 100644 --- a/MMDVMHost.cpp +++ b/MMDVMHost.cpp @@ -119,6 +119,7 @@ m_conf(confFile), m_modem(NULL), m_dstarNetwork(NULL), m_dmrNetwork(NULL), +m_ysfNetwork(NULL), m_display(NULL), m_mode(MODE_IDLE), m_modeTimer(1000U), @@ -241,6 +242,12 @@ int CMMDVMHost::run() return 1; } + if (m_ysfEnabled && m_conf.getFusionNetworkEnabled()) { + ret = createYSFNetwork(); + if (!ret) + return 1; + } + if (m_conf.getCWIdEnabled()) { unsigned int time = m_conf.getCWIdTime(); @@ -309,7 +316,7 @@ int CMMDVMHost::run() LogInfo(" Callsign: %s", m_callsign.c_str()); LogInfo(" Timeout: %us", timeout); - ysf = new CYSFControl(m_callsign, m_display, timeout, m_duplex); + ysf = new CYSFControl(m_callsign, m_ysfNetwork, m_display, timeout, m_duplex); } m_modeTimer.setTimeout(m_conf.getModeHang()); @@ -529,12 +536,14 @@ int CMMDVMHost::run() if (dmr != NULL) dmr->clock(); if (ysf != NULL) - ysf->clock(); + ysf->clock(ms); if (m_dstarNetwork != NULL) m_dstarNetwork->clock(ms); if (m_dmrNetwork != NULL) m_dmrNetwork->clock(ms); + if (m_ysfNetwork != NULL) + m_ysfNetwork->clock(ms); m_cwIdTimer.clock(ms); if (m_cwIdTimer.isRunning() && m_cwIdTimer.hasExpired()) { @@ -584,6 +593,11 @@ int CMMDVMHost::run() delete m_dmrNetwork; } + if (m_ysfNetwork != NULL) { + m_ysfNetwork->close(); + delete m_ysfNetwork; + } + delete dstar; delete dmr; delete ysf; @@ -721,6 +735,30 @@ bool CMMDVMHost::createDMRNetwork() return true; } +bool CMMDVMHost::createYSFNetwork() +{ + std::string address = m_conf.getFusionNetworkAddress(); + unsigned int port = m_conf.getFusionNetworkPort(); + bool debug = m_conf.getFusionNetworkDebug(); + + LogInfo("System Fusion Network Parameters"); + LogInfo(" Reflector Address: %s", address.c_str()); + LogInfo(" Reflector Port: %u", port); + + m_ysfNetwork = new CYSFNetwork(address, port, m_callsign, debug); + + bool ret = m_ysfNetwork->open(); + if (!ret) { + delete m_ysfNetwork; + m_ysfNetwork = NULL; + return false; + } + + m_ysfNetwork->enable(true); + + return true; +} + void CMMDVMHost::readParams() { m_dstarEnabled = m_conf.getDStarEnabled(); @@ -808,6 +846,8 @@ void CMMDVMHost::setMode(unsigned char mode) case MODE_DSTAR: if (m_dmrNetwork != NULL) m_dmrNetwork->enable(false); + if (m_ysfNetwork != NULL) + m_ysfNetwork->enable(false); m_modem->setMode(MODE_DSTAR); m_mode = MODE_DSTAR; m_modeTimer.start(); @@ -816,6 +856,8 @@ void CMMDVMHost::setMode(unsigned char mode) case MODE_DMR: if (m_dstarNetwork != NULL) m_dstarNetwork->enable(false); + if (m_ysfNetwork != NULL) + m_ysfNetwork->enable(false); m_modem->setMode(MODE_DMR); if (m_duplex) { m_modem->writeDMRStart(true); @@ -841,6 +883,8 @@ void CMMDVMHost::setMode(unsigned char mode) m_dstarNetwork->enable(false); if (m_dmrNetwork != NULL) m_dmrNetwork->enable(false); + if (m_ysfNetwork != NULL) + m_ysfNetwork->enable(false); if (m_mode == MODE_DMR && m_duplex && m_modem->hasTX()) { m_modem->writeDMRStart(false); m_dmrTXTimer.stop(); @@ -858,6 +902,8 @@ void CMMDVMHost::setMode(unsigned char mode) m_dstarNetwork->enable(false); if (m_dmrNetwork != NULL) m_dmrNetwork->enable(false); + if (m_ysfNetwork != NULL) + m_ysfNetwork->enable(false); if (m_mode == MODE_DMR && m_duplex && m_modem->hasTX()) { m_modem->writeDMRStart(false); m_dmrTXTimer.stop(); @@ -873,6 +919,8 @@ void CMMDVMHost::setMode(unsigned char mode) m_dstarNetwork->enable(true); if (m_dmrNetwork != NULL) m_dmrNetwork->enable(true); + if (m_ysfNetwork != NULL) + m_ysfNetwork->enable(true); if (m_mode == MODE_DMR && m_duplex && m_modem->hasTX()) { m_modem->writeDMRStart(false); m_dmrTXTimer.stop(); diff --git a/MMDVMHost.h b/MMDVMHost.h index c6c63d210..3f656fdc4 100644 --- a/MMDVMHost.h +++ b/MMDVMHost.h @@ -20,6 +20,7 @@ #define MMDVMHOST_H #include "DStarNetwork.h" +#include "YSFNetwork.h" #include "DMRIPSC.h" #include "Display.h" #include "Timer.h" @@ -41,6 +42,7 @@ class CMMDVMHost CModem* m_modem; CDStarNetwork* m_dstarNetwork; CDMRIPSC* m_dmrNetwork; + CYSFNetwork* m_ysfNetwork; CDisplay* m_display; unsigned char m_mode; CTimer m_modeTimer; @@ -56,6 +58,7 @@ class CMMDVMHost bool createModem(); bool createDStarNetwork(); bool createDMRNetwork(); + bool createYSFNetwork(); void createDisplay(); void setMode(unsigned char mode); diff --git a/MMDVMHost.vcxproj b/MMDVMHost.vcxproj index eb4fd5add..1803f8c2d 100644 --- a/MMDVMHost.vcxproj +++ b/MMDVMHost.vcxproj @@ -195,6 +195,7 @@ + @@ -238,6 +239,7 @@ + diff --git a/MMDVMHost.vcxproj.filters b/MMDVMHost.vcxproj.filters index 6e041f3ef..b271f5929 100644 --- a/MMDVMHost.vcxproj.filters +++ b/MMDVMHost.vcxproj.filters @@ -161,6 +161,9 @@ Header Files + + Header Files + @@ -295,5 +298,8 @@ Source Files + + Source Files + \ No newline at end of file diff --git a/Makefile b/Makefile index c22e5101f..830ee3694 100644 --- a/Makefile +++ b/Makefile @@ -10,7 +10,7 @@ OBJECTS = \ AMBEFEC.o BPTC19696.o Conf.o CRC.o Display.o DMRControl.o DMRCSBK.o DMRData.o DMRDataHeader.o DMREMB.o DMREmbeddedLC.o DMRFullLC.o DMRIPSC.o DMRLookup.o DMRLC.o \ DMRShortLC.o DMRSlot.o DMRSlotType.o DStarControl.o DStarHeader.o DStarNetwork.o DStarSlowData.o Golay2087.o Golay24128.o Hamming.o Log.o MMDVMHost.o Modem.o \ Nextion.o NullDisplay.o QR1676.o RS129.o SerialController.o SHA256.o StopWatch.o Sync.o TFTSerial.o Timer.o UDPSocket.o Utils.o YSFControl.o YSFConvolution.o \ - YSFFICH.o YSFPayload.o + YSFFICH.o YSFNetwork.o YSFPayload.o all: MMDVMHost diff --git a/Makefile.Pi.Adafruit b/Makefile.Pi.Adafruit index fc0fcdc49..5ef85b35e 100644 --- a/Makefile.Pi.Adafruit +++ b/Makefile.Pi.Adafruit @@ -10,7 +10,7 @@ OBJECTS = \ AMBEFEC.o BPTC19696.o Conf.o CRC.o Display.o DMRControl.o DMRCSBK.o DMRData.o DMRDataHeader.o DMREMB.o DMREmbeddedLC.o DMRFullLC.o DMRIPSC.o DMRLookup.o DMRLC.o \ DMRShortLC.o DMRSlot.o DMRSlotType.o DStarControl.o DStarHeader.o DStarNetwork.o DStarSlowData.o Golay2087.o Golay24128.o Hamming.o HD44780.o Log.o MMDVMHost.o \ Modem.o Nextion.o NullDisplay.o QR1676.o RS129.o SerialController.o SHA256.o StopWatch.o Sync.o TFTSerial.o Timer.o UDPSocket.o Utils.o YSFControl.o \ - YSFConvolution.o YSFFICH.o YSFPayload.o + YSFConvolution.o YSFFICH.o YSFNetwork.o YSFPayload.o all: MMDVMHost diff --git a/Makefile.Pi.HD44780 b/Makefile.Pi.HD44780 index 6088f5a65..7ac9e204f 100644 --- a/Makefile.Pi.HD44780 +++ b/Makefile.Pi.HD44780 @@ -10,7 +10,7 @@ OBJECTS = \ AMBEFEC.o BPTC19696.o Conf.o CRC.o Display.o DMRControl.o DMRCSBK.o DMRData.o DMRDataHeader.o DMREMB.o DMREmbeddedLC.o DMRFullLC.o DMRIPSC.o DMRLookup.o DMRLC.o \ DMRShortLC.o DMRSlot.o DMRSlotType.o DStarControl.o DStarHeader.o DStarNetwork.o DStarSlowData.o Golay2087.o Golay24128.o Hamming.o HD44780.o Log.o MMDVMHost.o \ Modem.o Nextion.o NullDisplay.o QR1676.o RS129.o SerialController.o SHA256.o StopWatch.o Sync.o TFTSerial.o Timer.o UDPSocket.o Utils.o YSFControl.o \ - YSFConvolution.o YSFFICH.o YSFPayload.o + YSFConvolution.o YSFFICH.o YSFNetwork.o YSFPayload.o all: MMDVMHost diff --git a/Makefile.Pi.OLED b/Makefile.Pi.OLED index 70316e404..bd7df7f33 100644 --- a/Makefile.Pi.OLED +++ b/Makefile.Pi.OLED @@ -10,7 +10,7 @@ OBJECTS = \ AMBEFEC.o BPTC19696.o Conf.o CRC.o Display.o DMRControl.o DMRCSBK.o DMRData.o DMRDataHeader.o DMREMB.o DMREmbeddedLC.o DMRFullLC.o DMRIPSC.o DMRLookup.o DMRLC.o \ DMRShortLC.o DMRSlot.o DMRSlotType.o DStarControl.o DStarHeader.o DStarNetwork.o DStarSlowData.o Golay2087.o Golay24128.o Hamming.o OLED.o Log.o MMDVMHost.o \ Modem.o Nextion.o NullDisplay.o QR1676.o RS129.o SerialController.o SHA256.o StopWatch.o Sync.o TFTSerial.o Timer.o UDPSocket.o Utils.o YSFControl.o \ - YSFConvolution.o YSFFICH.o YSFPayload.o + YSFConvolution.o YSFFICH.o YSFNetwork.o YSFPayload.o all: MMDVMHost diff --git a/Nextion.cpp b/Nextion.cpp index 3f71bae8f..62e255f08 100644 --- a/Nextion.cpp +++ b/Nextion.cpp @@ -181,19 +181,20 @@ void CNextion::clearDMRInt(unsigned int slotNo) } } -void CNextion::writeFusionInt(const char* source, const char* dest) +void CNextion::writeFusionInt(const char* source, const char* dest, const char* type) { assert(source != NULL); assert(dest != NULL); + assert(type != NULL); if (m_mode != MODE_YSF) sendCommand("page YSF"); char text[30U]; - ::sprintf(text, "t0.txt=\"%.10s\"", source); + ::sprintf(text, "t0.txt=\"%s %.10s\"", type, source); sendCommand(text); - ::sprintf(text, "t1.txt=\"%.10s\"", dest); + ::sprintf(text, "t1.txt=\" %.10s\"", dest); sendCommand(text); m_mode = MODE_YSF; diff --git a/Nextion.h b/Nextion.h index 911c636db..c4221afd3 100644 --- a/Nextion.h +++ b/Nextion.h @@ -46,7 +46,7 @@ class CNextion : public CDisplay virtual void writeDMRInt(unsigned int slotNo, const std::string& src, bool group, const std::string& dst, const char* type); virtual void clearDMRInt(unsigned int slotNo); - virtual void writeFusionInt(const char* source, const char* dest); + virtual void writeFusionInt(const char* source, const char* dest, const char* type); virtual void clearFusionInt(); private: diff --git a/NullDisplay.cpp b/NullDisplay.cpp index 2aec2c3bf..ef1e33071 100644 --- a/NullDisplay.cpp +++ b/NullDisplay.cpp @@ -60,7 +60,7 @@ void CNullDisplay::clearDMRInt(unsigned int slotNo) { } -void CNullDisplay::writeFusionInt(const char* source, const char* dest) +void CNullDisplay::writeFusionInt(const char* source, const char* dest, const char* type) { } diff --git a/NullDisplay.h b/NullDisplay.h index 57f18d81e..880b1a6c4 100644 --- a/NullDisplay.h +++ b/NullDisplay.h @@ -44,7 +44,7 @@ class CNullDisplay : public CDisplay virtual void writeDMRInt(unsigned int slotNo, const std::string& src, bool group, const std::string& dst, const char* type); virtual void clearDMRInt(unsigned int slotNo); - virtual void writeFusionInt(const char* source, const char* dest); + virtual void writeFusionInt(const char* source, const char* dest, const char* type); virtual void clearFusionInt(); private: diff --git a/OLED.cpp b/OLED.cpp index 42cd03da5..8ae707def 100644 --- a/OLED.cpp +++ b/OLED.cpp @@ -187,15 +187,15 @@ void COLED::clearDMRInt(unsigned int slotNo) display.display(); } -void COLED::writeFusionInt(const char* source, const char* dest) +void COLED::writeFusionInt(const char* source, const char* dest, const char* type) { m_mode = MODE_YSF; display.fillRect(0, OLED_LINE1, display.width(), 10, BLACK); display.setCursor(0,OLED_LINE1); - display.printf("%.10s", source); + display.printf("%s %.10s", type, source); display.fillRect(0, OLED_LINE2, display.width(), 10, BLACK); display.setCursor(0,OLED_LINE2); - display.printf("%.10s", dest); + display.printf(" %.10s", dest); OLED_statusbar(); display.display(); } diff --git a/OLED.h b/OLED.h index 6e8556556..7e903b235 100644 --- a/OLED.h +++ b/OLED.h @@ -91,7 +91,7 @@ class COLED : public CDisplay virtual void writeDMRInt(unsigned int slotNo, const std::string& src, bool group, const std::string& dst, const char* type); virtual void clearDMRInt(unsigned int slotNo); - virtual void writeFusionInt(const char* source, const char* dest); + virtual void writeFusionInt(const char* source, const char* dest, const char* type); virtual void clearFusionInt(); virtual void close(); diff --git a/TFTSerial.cpp b/TFTSerial.cpp index 846674228..56cc856bb 100644 --- a/TFTSerial.cpp +++ b/TFTSerial.cpp @@ -259,10 +259,11 @@ void CTFTSerial::clearDMRInt(unsigned int slotNo) } } -void CTFTSerial::writeFusionInt(const char* source, const char* dest) +void CTFTSerial::writeFusionInt(const char* source, const char* dest, const char* type) { assert(source != NULL); assert(dest != NULL); + assert(type != NULL); if (m_mode != MODE_YSF) { // Clear the screen @@ -275,12 +276,12 @@ void CTFTSerial::writeFusionInt(const char* source, const char* dest) } char text[30U]; - ::sprintf(text, "%.10s", source); + ::sprintf(text, "%s %.10s", type, source); gotoPosPixel(5U, 80U); displayText(text); - ::sprintf(text, "%.10s", dest); + ::sprintf(text, " %.10s", dest); gotoPosPixel(5U, 100U); displayText(text); diff --git a/TFTSerial.h b/TFTSerial.h index acb2b65ba..6bcf036a2 100644 --- a/TFTSerial.h +++ b/TFTSerial.h @@ -46,7 +46,7 @@ class CTFTSerial : public CDisplay virtual void writeDMRInt(unsigned int slotNo, const std::string& src, bool group, const std::string& dst, const char* type); virtual void clearDMRInt(unsigned int slotNo); - virtual void writeFusionInt(const char* source, const char* dest); + virtual void writeFusionInt(const char* source, const char* dest, const char* type); virtual void clearFusionInt(); private: diff --git a/YSFControl.cpp b/YSFControl.cpp index b3526a5e6..25c945eb4 100644 --- a/YSFControl.cpp +++ b/YSFControl.cpp @@ -24,16 +24,21 @@ // #define DUMP_YSF -CYSFControl::CYSFControl(const std::string& callsign, CDisplay* display, unsigned int timeout, bool duplex) : +CYSFControl::CYSFControl(const std::string& callsign, CYSFNetwork* network, CDisplay* display, unsigned int timeout, bool duplex) : +m_network(network), m_display(display), m_duplex(duplex), m_queue(5000U, "YSF Control"), -m_state(RS_RF_LISTENING), -m_timeoutTimer(1000U, timeout), -m_interval(), -m_frames(0U), -m_errs(0U), -m_bits(0U), +m_rfState(RS_RF_LISTENING), +m_netState(RS_NET_IDLE), +m_rfTimeoutTimer(1000U, timeout), +m_netTimeoutTimer(1000U, timeout), +m_networkWatchdog(1000U, 0U, 1500U), +m_holdoffTimer(1000U, 0U, 500U), +m_rfFrames(0U), +m_netFrames(0U), +m_rfErrs(0U), +m_rfBits(0U), m_source(NULL), m_dest(NULL), m_payload(), @@ -43,8 +48,6 @@ m_fp(NULL) m_payload.setUplink(callsign); m_payload.setDownlink(callsign); - - m_interval.start(); } CYSFControl::~CYSFControl() @@ -57,11 +60,9 @@ bool CYSFControl::writeModem(unsigned char *data) unsigned char type = data[0U]; - if (type == TAG_LOST && m_state == RS_RF_AUDIO) { - LogMessage("YSF, transmission lost, %.1f seconds, BER: %.1f%%", float(m_frames) / 10.0F, float(m_errs * 100U) / float(m_bits)); - // if (m_parrot != NULL) - // m_parrot->end(); - writeEndOfTransmission(); + if (type == TAG_LOST && m_rfState == RS_RF_AUDIO) { + LogMessage("YSF, transmission lost, %.1f seconds, BER: %.1f%%", float(m_rfFrames) / 10.0F, float(m_rfErrs * 100U) / float(m_rfBits)); + writeEndRF(); return false; } @@ -71,51 +72,45 @@ bool CYSFControl::writeModem(unsigned char *data) CYSFFICH fich; bool valid = fich.decode(data + 2U); - if (valid && m_state == RS_RF_LISTENING) { + if (valid && m_rfState == RS_RF_LISTENING) { unsigned char fi = fich.getFI(); if (fi == YSF_FI_TERMINATOR) return false; - m_frames = 0U; - m_errs = 0U; - m_bits = 1U; - m_timeoutTimer.start(); + m_rfFrames = 0U; + m_rfErrs = 0U; + m_rfBits = 1U; + m_rfTimeoutTimer.start(); + m_holdoffTimer.stop(); m_payload.reset(); - m_state = RS_RF_AUDIO; + m_rfState = RS_RF_AUDIO; #if defined(DUMP_YSF) openFile(); #endif } - if (m_state != RS_RF_AUDIO) + if (m_rfState != RS_RF_AUDIO) return false; unsigned char fi = fich.getFI(); if (valid && fi == YSF_FI_HEADER) { CSync::addYSFSync(data + 2U); - m_frames++; + m_rfFrames++; valid = m_payload.processHeaderData(data + 2U); + data[0U] = TAG_DATA; + data[1U] = 0x00U; + + writeNetwork(data); + if (m_duplex) { fich.setMR(YSF_MR_BUSY); fich.encode(data + 2U); - - data[0U] = TAG_DATA; - data[1U] = 0x00U; - writeQueue(data); + writeQueueRF(data); } - // if (m_parrot != NULL) { - // fich.setMR(YSF_MR_NOT_BUSY); - // fich.encode(data + 2U); - // - // data[0U] = TAG_DATA; - // data[1U] = 0x00U; - // writeParrot(data); - // } - if (valid) m_source = m_payload.getSource(); @@ -131,45 +126,43 @@ bool CYSFControl::writeModem(unsigned char *data) writeFile(data + 2U); #endif - if (m_source != NULL && m_dest != NULL) - LogMessage("YSF, received header from %10.10s to %10.10s", m_source, m_dest); - else if (m_source == NULL && m_dest != NULL) - LogMessage("YSF, received header from ?????????? to %10.10s", m_dest); - else if (m_source != NULL && m_dest == NULL) - LogMessage("YSF, received header from %10.10s to ??????????", m_source); - else - LogMessage("YSF, received header from ?????????? to ??????????"); + if (m_source != NULL && m_dest != NULL) { + m_display->writeFusion((char*)m_source, (char*)m_dest, "R"); + LogMessage("YSF, received RF header from %10.10s to %10.10s", m_source, m_dest); + } else if (m_source == NULL && m_dest != NULL) { + m_display->writeFusion("??????????", (char*)m_dest, "R"); + LogMessage("YSF, received RF header from ?????????? to %10.10s", m_dest); + } else if (m_source != NULL && m_dest == NULL) { + m_display->writeFusion((char*)m_source, "??????????", "R"); + LogMessage("YSF, received RF header from %10.10s to ??????????", m_source); + } else { + m_display->writeFusion("??????????", "??????????", "R"); + LogMessage("YSF, received RF header from ?????????? to ??????????"); + } } else if (valid && fi == YSF_FI_TERMINATOR) { CSync::addYSFSync(data + 2U); - m_frames++; + m_rfFrames++; m_payload.processHeaderData(data + 2U); + data[0U] = TAG_EOT; + data[1U] = 0x00U; + + writeNetwork(data); + if (m_duplex) { fich.setMR(YSF_MR_BUSY); fich.encode(data + 2U); - - data[0U] = TAG_EOT; - data[1U] = 0x00U; - writeQueue(data); + writeQueueRF(data); } - // if (m_parrot != NULL) { - // fich.setMR(YSF_MR_NOT_BUSY); - // fich.encode(data + 2U); - // - // data[0U] = TAG_EOT; - // data[1U] = 0x00U; - // writeParrot(data); - // } - #if defined(DUMP_YSF) writeFile(data + 2U); #endif - LogMessage("YSF, received RF end of transmission, %.1f seconds, BER: %.1f%%", float(m_frames) / 10.0F, float(m_errs * 100U) / float(m_bits)); - writeEndOfTransmission(); + LogMessage("YSF, received RF end of transmission, %.1f seconds, BER: %.1f%%", float(m_rfFrames) / 10.0F, float(m_rfErrs * 100U) / float(m_rfBits)); + writeEndRF(); return false; } else if (valid) { @@ -179,19 +172,19 @@ bool CYSFControl::writeModem(unsigned char *data) unsigned char ft = fich.getFT(); unsigned char dt = fich.getDT(); - m_frames++; + m_rfFrames++; switch (dt) { case YSF_DT_VD_MODE1: valid = m_payload.processVDMode1Data(data + 2U, fn); - m_errs += m_payload.processVDMode1Audio(data + 2U); - m_bits += 235U; + m_rfErrs += m_payload.processVDMode1Audio(data + 2U); + m_rfBits += 235U; break; case YSF_DT_VD_MODE2: valid = m_payload.processVDMode2Data(data + 2U, fn); - m_errs += m_payload.processVDMode2Audio(data + 2U); - m_bits += 135U; + m_rfErrs += m_payload.processVDMode2Audio(data + 2U); + m_rfBits += 135U; break; case YSF_DT_DATA_FR_MODE: @@ -201,8 +194,8 @@ bool CYSFControl::writeModem(unsigned char *data) case YSF_DT_VOICE_FR_MODE: if (fn != 0U || ft != 1U) { // The first packet after the header is odd, don't try and regenerate it - m_errs += m_payload.processVoiceFRModeAudio(data + 2U); - m_bits += 720U; + m_rfErrs += m_payload.processVoiceFRModeAudio(data + 2U); + m_rfBits += 720U; } valid = false; break; @@ -233,56 +226,45 @@ bool CYSFControl::writeModem(unsigned char *data) if (change) { if (m_source != NULL && m_dest != NULL) { - m_display->writeFusion((char*)m_source, (char*)m_dest); - LogMessage("YSF, received transmission from %10.10s to %10.10s", m_source, m_dest); + m_display->writeFusion((char*)m_source, (char*)m_dest, "R"); + LogMessage("YSF, received RF data from %10.10s to %10.10s", m_source, m_dest); } if (m_source != NULL && m_dest == NULL) { - m_display->writeFusion((char*)m_source, "??????????"); - LogMessage("YSF, received transmission from %10.10s to ??????????", m_source); + m_display->writeFusion((char*)m_source, "??????????", "R"); + LogMessage("YSF, received RF data from %10.10s to ??????????", m_source); } if (m_source == NULL && m_dest != NULL) { - m_display->writeFusion("??????????", (char*)m_dest); - LogMessage("YSF, received transmission from ?????????? to %10.10s", m_dest); + m_display->writeFusion("??????????", (char*)m_dest, "R"); + LogMessage("YSF, received RF data from ?????????? to %10.10s", m_dest); } } + data[0U] = TAG_DATA; + data[1U] = 0x00U; + + writeNetwork(data); + if (m_duplex) { fich.setMR(YSF_MR_BUSY); fich.encode(data + 2U); - - data[0U] = TAG_DATA; - data[1U] = 0x00U; - writeQueue(data); + writeQueueRF(data); } - // if (m_parrot != NULL) { - // fich.setMR(YSF_MR_NOT_BUSY); - // fich.encode(data + 2U); - // - // data[0U] = TAG_DATA; - // data[1U] = 0x00U; - // writeParrot(data); - // } - #if defined(DUMP_YSF) writeFile(data + 2U); #endif } else { CSync::addYSFSync(data + 2U); - m_frames++; + m_rfFrames++; - if (m_duplex) { - data[0U] = TAG_DATA; - data[1U] = 0x00U; - writeQueue(data); - } + data[0U] = TAG_DATA; + data[1U] = 0x00U; + + writeNetwork(data); - // if (m_parrot != NULL) { - // data[0U] = TAG_DATA; - // data[1U] = 0x00U; - // writeParrot(data); - // } + if (m_duplex) + writeQueueRF(data); #if defined(DUMP_YSF) writeFile(data + 2U); @@ -299,6 +281,10 @@ unsigned int CYSFControl::readModem(unsigned char* data) if (m_queue.isEmpty()) return 0U; + // Don't relay data until the timer has stopped. + if (m_holdoffTimer.isRunning()) + return 0U; + unsigned char len = 0U; m_queue.getData(&len, 1U); @@ -307,51 +293,103 @@ unsigned int CYSFControl::readModem(unsigned char* data) return len; } -void CYSFControl::writeEndOfTransmission() +void CYSFControl::writeEndRF() { - m_state = RS_RF_LISTENING; + m_rfState = RS_RF_LISTENING; - m_timeoutTimer.stop(); + if (m_netState == RS_NET_IDLE) { + m_payload.reset(); - m_payload.reset(); + m_display->clearFusion(); - m_display->clearFusion(); + if (m_network != NULL) + m_network->reset(); - // These variables are free'd by YSFPayload - m_source = NULL; - m_dest = NULL; + // These variables are free'd by YSFPayload + m_source = NULL; + m_dest = NULL; + } else { + m_rfTimeoutTimer.stop(); + } #if defined(DUMP_YSF) closeFile(); #endif } -void CYSFControl::clock() +void CYSFControl::writeEndNet() +{ + m_netState = RS_NET_IDLE; + + m_netTimeoutTimer.stop(); + m_networkWatchdog.stop(); + + m_display->clearFusion(); + + if (m_network != NULL) + m_network->reset(); +} + +void CYSFControl::writeNetwork() +{ + unsigned char data[200U]; + unsigned int length = m_network->read(data); + if (length == 0U) + return; + + if (m_rfState != RS_RF_LISTENING && m_netState == RS_NET_IDLE) + return; + + m_networkWatchdog.start(); + + if (!m_netTimeoutTimer.isRunning()) { + m_display->writeFusion("??????????", "??????????", "N"); + LogMessage("YSF, received network data from ?????????? to ??????????"); + m_netTimeoutTimer.start(); + m_netState = RS_NET_AUDIO; + m_netFrames = 0U; + } + + m_netFrames++; + + writeQueueNet(data); + + if (data[0U] == TAG_EOT) { + LogMessage("YSF, received network end of transmission, %.1f seconds", float(m_netFrames) / 10.0F); + writeEndNet(); + } +} + +void CYSFControl::clock(unsigned int ms) { - unsigned int ms = m_interval.elapsed(); - m_interval.start(); - - m_timeoutTimer.clock(ms); - - // if (m_parrot != NULL) { - // m_parrot->clock(ms); - // - // unsigned int space = m_queue.freeSpace(); - // bool hasData = m_parrot->hasData(); - // - // if (space > (YSF_FRAME_LENGTH_BYTES + 2U) && hasData) { - // unsigned char data[YSF_FRAME_LENGTH_BYTES + 2U]; - // m_parrot->read(data); - // writeQueue(data); - // } - // } + if (m_network != NULL) + writeNetwork(); + + m_holdoffTimer.clock(ms); + if (m_holdoffTimer.isRunning() && m_holdoffTimer.hasExpired()) + m_holdoffTimer.stop(); + + m_rfTimeoutTimer.clock(ms); + m_netTimeoutTimer.clock(ms); + + if (m_netState == RS_NET_AUDIO) { + m_networkWatchdog.clock(ms); + + if (m_networkWatchdog.hasExpired()) { + LogMessage("YSF, network watchdog has expired, %.1f seconds", float(m_netFrames) / 10.0F); + writeEndNet(); + } + } } -void CYSFControl::writeQueue(const unsigned char *data) +void CYSFControl::writeQueueRF(const unsigned char *data) { assert(data != NULL); - if (m_timeoutTimer.isRunning() && m_timeoutTimer.hasExpired()) + if (m_netState != RS_NET_IDLE) + return; + + if (m_rfTimeoutTimer.isRunning() && m_rfTimeoutTimer.hasExpired()) return; unsigned char len = YSF_FRAME_LENGTH_BYTES + 2U; @@ -367,18 +405,38 @@ void CYSFControl::writeQueue(const unsigned char *data) m_queue.addData(data, len); } -// void CYSFControl::writeParrot(const unsigned char *data) -// { -// assert(data != NULL); -// -// if (m_timeoutTimer.isRunning() && m_timeoutTimer.hasExpired()) -// return; -// -// m_parrot->write(data); -// -// if (data[0U] == TAG_EOT) -// m_parrot->end(); -// } +void CYSFControl::writeQueueNet(const unsigned char *data) +{ + assert(data != NULL); + + if (m_netTimeoutTimer.isRunning() && m_netTimeoutTimer.hasExpired()) + return; + + unsigned char len = YSF_FRAME_LENGTH_BYTES + 2U; + + unsigned int space = m_queue.freeSpace(); + if (space < (len + 1U)) { + LogError("YSF, overflow in the System Fusion RF queue"); + return; + } + + m_queue.addData(&len, 1U); + + m_queue.addData(data, len); +} + +void CYSFControl::writeNetwork(const unsigned char *data) +{ + assert(data != NULL); + + if (m_network == NULL) + return; + + if (m_rfTimeoutTimer.isRunning() && m_rfTimeoutTimer.hasExpired()) + return; + + m_network->write(m_source, m_dest, data + 2U, data[0U] == TAG_EOT); +} bool CYSFControl::openFile() { diff --git a/YSFControl.h b/YSFControl.h index a9becd427..251c646e1 100644 --- a/YSFControl.h +++ b/YSFControl.h @@ -19,10 +19,10 @@ #if !defined(YSFControl_H) #define YSFControl_H +#include "YSFNetwork.h" #include "YSFDefines.h" #include "YSFPayload.h" #include "RingBuffer.h" -#include "StopWatch.h" #include "Display.h" #include "Defines.h" #include "Timer.h" @@ -32,33 +32,42 @@ class CYSFControl { public: - CYSFControl(const std::string& callsign, CDisplay* display, unsigned int timeout, bool duplex); + CYSFControl(const std::string& callsign, CYSFNetwork* network, CDisplay* display, unsigned int timeout, bool duplex); ~CYSFControl(); bool writeModem(unsigned char* data); unsigned int readModem(unsigned char* data); - void clock(); + void clock(unsigned int ms); private: + CYSFNetwork* m_network; CDisplay* m_display; bool m_duplex; CRingBuffer m_queue; - RPT_RF_STATE m_state; - CTimer m_timeoutTimer; - CStopWatch m_interval; - unsigned int m_frames; - unsigned int m_errs; - unsigned int m_bits; + RPT_RF_STATE m_rfState; + RPT_NET_STATE m_netState; + CTimer m_rfTimeoutTimer; + CTimer m_netTimeoutTimer; + CTimer m_networkWatchdog; + CTimer m_holdoffTimer; + unsigned int m_rfFrames; + unsigned int m_netFrames; + unsigned int m_rfErrs; + unsigned int m_rfBits; unsigned char* m_source; unsigned char* m_dest; CYSFPayload m_payload; FILE* m_fp; - void writeQueue(const unsigned char* data); + void writeQueueRF(const unsigned char* data); + void writeQueueNet(const unsigned char* data); + void writeNetwork(const unsigned char* data); + void writeNetwork(); - void writeEndOfTransmission(); + void writeEndRF(); + void writeEndNet(); bool openFile(); bool writeFile(const unsigned char* data); diff --git a/YSFDefines.h b/YSFDefines.h index 2241e0d56..348d1bced 100644 --- a/YSFDefines.h +++ b/YSFDefines.h @@ -28,6 +28,8 @@ const unsigned int YSF_FICH_LENGTH_BYTES = 25U; const unsigned char YSF_SYNC_OK = 0x01U; +const unsigned int YSF_CALLSIGN_LENGTH = 10U; + const unsigned char YSF_FI_HEADER = 0x00U; const unsigned char YSF_FI_COMMUNICATIONS = 0x01U; const unsigned char YSF_FI_TERMINATOR = 0x02U; diff --git a/YSFNetwork.cpp b/YSFNetwork.cpp new file mode 100644 index 000000000..f692cc9f1 --- /dev/null +++ b/YSFNetwork.cpp @@ -0,0 +1,204 @@ +/* + * Copyright (C) 2009-2014,2016 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 + * 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 "YSFDefines.h" +#include "YSFNetwork.h" +#include "Defines.h" +#include "Utils.h" +#include "Log.h" + +#include +#include +#include + +const unsigned int BUFFER_LENGTH = 200U; + +CYSFNetwork::CYSFNetwork(const std::string& address, unsigned int port, const std::string& callsign, bool debug) : +m_socket(), +m_address(), +m_port(port), +m_debug(debug), +m_callsign(), +m_enabled(false), +m_seq(0U), +m_buffer(1000U, "YSF Network"), +m_pollTimer(1000U, 60U), +m_tag(NULL) +{ + m_callsign = callsign; + m_callsign.resize(YSF_CALLSIGN_LENGTH, ' '); + + m_address = CUDPSocket::lookup(address); + + m_tag = new unsigned char[YSF_CALLSIGN_LENGTH]; + ::memset(m_tag, ' ', YSF_CALLSIGN_LENGTH); +} + +CYSFNetwork::~CYSFNetwork() +{ + delete[] m_tag; +} + +bool CYSFNetwork::open() +{ + LogMessage("Opening YSF network connection"); + + if (m_address.s_addr == INADDR_NONE) + return false; + + m_pollTimer.start(); + + return m_socket.open(); +} + +bool CYSFNetwork::write(const unsigned char* src, const unsigned char* dest, const unsigned char* data, bool end) +{ + assert(data != NULL); + + unsigned char buffer[200U]; + + buffer[0] = 'Y'; + buffer[1] = 'S'; + buffer[2] = 'F'; + buffer[3] = 'D'; + + for (unsigned int i = 0U; i < YSF_CALLSIGN_LENGTH; i++) + buffer[i + 4U] = m_callsign.at(i); + + if (src != NULL) + ::memcpy(buffer + 14U, src, YSF_CALLSIGN_LENGTH); + else + ::memset(buffer + 14U, ' ', YSF_CALLSIGN_LENGTH); + + if (dest != NULL) + ::memcpy(buffer + 24U, dest, YSF_CALLSIGN_LENGTH); + else + ::memset(buffer + 24U, ' ', YSF_CALLSIGN_LENGTH); + + buffer[34U] = end ? 0x01U : 0x00U; + + ::memcpy(buffer + 35U, data, YSF_FRAME_LENGTH_BYTES); + + if (m_debug) + CUtils::dump(1U, "YSF Network Data Sent", buffer, 155U); + + return m_socket.write(buffer, 155U, m_address, m_port); +} + +bool CYSFNetwork::writePoll() +{ + unsigned char buffer[20U]; + + buffer[0] = 'Y'; + buffer[1] = 'S'; + buffer[2] = 'F'; + buffer[3] = 'P'; + + for (unsigned int i = 0U; i < YSF_CALLSIGN_LENGTH; i++) + buffer[i + 4U] = m_callsign.at(i); + + if (m_debug) + CUtils::dump(1U, "YSF Network Poll Sent", buffer, 14U); + + return m_socket.write(buffer, 14U, m_address, m_port); +} + +void CYSFNetwork::clock(unsigned int ms) +{ + m_pollTimer.clock(ms); + if (m_pollTimer.hasExpired()) { + writePoll(); + m_pollTimer.start(); + } + + unsigned char buffer[BUFFER_LENGTH]; + + in_addr address; + unsigned int port; + int length = m_socket.read(buffer, BUFFER_LENGTH, address, port); + if (length <= 0) + return; + + // Check if the data is for us + if (m_address.s_addr != address.s_addr || m_port != port) { + LogMessage("YSF packet received from an invalid source, %08X != %08X and/or %u != %u", m_address.s_addr, address.s_addr, m_port, port); + return; + } + + // Ignore incoming polls + if (::memcmp(buffer, "YSFP", 4U) == 0) + return; + + // Invalid packet type? + if (::memcmp(buffer, "YSFD", 4U) != 0) + return; + + if (!m_enabled) + return; + + if (m_debug) + CUtils::dump(1U, "YSF Network Data Received", buffer, length); + + if (::memcmp(m_tag, " ", YSF_CALLSIGN_LENGTH) == 0) { + ::memcpy(m_tag, buffer + 4U, YSF_CALLSIGN_LENGTH); + } else { + if (::memcmp(m_tag, buffer + 4U, YSF_CALLSIGN_LENGTH) != 0) + return; + } + + bool end = buffer[34U] == 0x01U; + if (end) + ::memset(m_tag, ' ', YSF_CALLSIGN_LENGTH); + + buffer[33U] = end ? TAG_EOT : TAG_DATA; + buffer[34U] = 0x00U; + + m_buffer.addData(buffer + 33U, YSF_FRAME_LENGTH_BYTES + 2U); +} + +unsigned int CYSFNetwork::read(unsigned char* data) +{ + assert(data != NULL); + + if (m_buffer.isEmpty()) + return 0U; + + m_buffer.getData(data, YSF_FRAME_LENGTH_BYTES + 2U); + + return YSF_FRAME_LENGTH_BYTES + 2U; +} + +void CYSFNetwork::reset() +{ + ::memset(m_tag, ' ', YSF_CALLSIGN_LENGTH); +} + +void CYSFNetwork::close() +{ + m_socket.close(); + + LogMessage("Closing YSF network connection"); +} + +void CYSFNetwork::enable(bool enabled) +{ + if (enabled && !m_enabled) + reset(); + + m_enabled = enabled; +} diff --git a/YSFNetwork.h b/YSFNetwork.h new file mode 100644 index 000000000..ca5d4ab1c --- /dev/null +++ b/YSFNetwork.h @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2009-2014,2016 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 + * 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. + */ + +#ifndef YSFNetwork_H +#define YSFNetwork_H + +#include "YSFDefines.h" +#include "RingBuffer.h" +#include "UDPSocket.h" +#include "Timer.h" + +#include +#include + +class CYSFNetwork { +public: + CYSFNetwork(const std::string& address, unsigned int port, const std::string& callsign, bool debug); + ~CYSFNetwork(); + + bool open(); + + void enable(bool enabled); + + bool write(const unsigned char* src, const unsigned char* dest, const unsigned char* data, bool end); + + unsigned int read(unsigned char* data); + + void reset(); + + void close(); + + void clock(unsigned int ms); + +private: + CUDPSocket m_socket; + in_addr m_address; + unsigned int m_port; + std::string m_callsign; + bool m_debug; + bool m_enabled; + uint16_t m_seq; + CRingBuffer m_buffer; + CTimer m_pollTimer; + unsigned char* m_tag; + + bool writePoll(); +}; + +#endif