diff --git a/OLED.cpp b/OLED.cpp index 6be9583..e2f80b0 100644 --- a/OLED.cpp +++ b/OLED.cpp @@ -217,6 +217,7 @@ bool COLED::open() } // init done + m_display.setTextWrap(false); // disable text wrap as default m_display.clearDisplay(); // clears the screen buffer m_display.display(); // display it (clear display) @@ -271,8 +272,10 @@ void COLED::setErrorInt(const char* text) m_display.clearDisplay(); OLED_statusbar(); + m_display.setTextWrap(true); // text wrap temorally enable m_display.setCursor(0,OLED_LINE1); m_display.printf("%s\n",text); + m_display.setTextWrap(false); m_display.display(); } @@ -360,6 +363,16 @@ void COLED::clearDStarInt() } void COLED::writeDMRInt(unsigned int slotNo,const std::string& src,bool group,const std::string& dst,const char* type) +{ + CUserDBentry tmp; + + tmp.set(keyCALLSIGN, src); + writeDMRIntEx(slotNo, tmp, group, dst, type); +} + +#define CALLandNAME(u) ((u).get(keyCALLSIGN) + " " + (u).get(keyFIRST_NAME)) + +int COLED::writeDMRIntEx(unsigned int slotNo, const class CUserDBentry& src, bool group, const std::string& dst, const char* type) { if (m_mode != MODE_DMR) { @@ -368,13 +381,13 @@ void COLED::writeDMRInt(unsigned int slotNo,const std::string& src,bool group,co clearDMRInt(slotNo); } // if both slots, use lines 2-3 for slot 1, lines 4-5 for slot 2 - // if single slot, use lines 3-4 + // if single slot, use lines 2-3 if ( m_slot1Enabled && m_slot2Enabled ) { if (slotNo == 1U) { m_display.fillRect(0,OLED_LINE2,m_display.width(),40,BLACK); m_display.setCursor(0,OLED_LINE2); - m_display.printf("%s",src.c_str()); + m_display.printf("%s",CALLandNAME(src).c_str()); m_display.setCursor(0,OLED_LINE3); m_display.printf("Slot: %i %s %s%s",slotNo,type,group ? "TG: " : "",dst.c_str()); } @@ -382,34 +395,41 @@ void COLED::writeDMRInt(unsigned int slotNo,const std::string& src,bool group,co { m_display.fillRect(0,OLED_LINE4,m_display.width(),40,BLACK); m_display.setCursor(0,OLED_LINE4); - m_display.printf("%s",src.c_str()); + m_display.printf("%s",CALLandNAME(src).c_str()); m_display.setCursor(0,OLED_LINE5); m_display.printf("Slot: %i %s %s%s",slotNo,type,group ? "TG: " : "",dst.c_str()); } + m_display.fillRect(0,OLED_LINE6,m_display.width(),20,BLACK); + m_display.setCursor(0,OLED_LINE6); + m_display.printf("%s",m_ipaddress.c_str()); } else { - m_display.fillRect(0,OLED_LINE3,m_display.width(),20,BLACK); + m_display.fillRect(0,OLED_LINE2,m_display.width(),m_display.height(),BLACK); + m_display.setCursor(0,OLED_LINE2); + m_display.printf("%s",CALLandNAME(src).c_str()); m_display.setCursor(0,OLED_LINE3); - m_display.printf("%s",src.c_str()); - m_display.setCursor(0,OLED_LINE4); m_display.printf("Slot: %i %s %s%s",slotNo,type,group ? "TG: " : "",dst.c_str()); + m_display.setCursor(0,OLED_LINE4); + m_display.printf("%s",src.get(keyCITY).c_str()); + m_display.setCursor(0,OLED_LINE5); + m_display.printf("%s",src.get(keySTATE).c_str()); + m_display.setCursor(0,OLED_LINE6); + m_display.printf("%s",src.get(keyCOUNTRY).c_str()); } - m_display.fillRect(0,OLED_LINE6,m_display.width(),20,BLACK); - m_display.setCursor(0,OLED_LINE6); - m_display.printf("%s",m_ipaddress.c_str()); - OLED_statusbar(); m_display.display(); + // must be 0, to avoid calling writeDMRInt() from CDisplay::writeDMR() + return 0; } void COLED::clearDMRInt(unsigned int slotNo) { // if both slots, use lines 2-3 for slot 1, lines 4-5 for slot 2 - // if single slot, use lines 3-4 + // if single slot, use lines 2-3 if ( m_slot1Enabled && m_slot2Enabled ){ if (slotNo == 1U) { m_display.fillRect(0, OLED_LINE3, m_display.width(), 40, BLACK); @@ -423,8 +443,8 @@ void COLED::clearDMRInt(unsigned int slotNo) } } else { - m_display.fillRect(0, OLED_LINE4, m_display.width(), 40, BLACK); - m_display.setCursor(0,OLED_LINE4); + m_display.fillRect(0, OLED_LINE2, m_display.width(), m_display.height(), BLACK); + m_display.setCursor(0,OLED_LINE3); m_display.printf("Slot: %i Listening",slotNo); } @@ -497,28 +517,47 @@ void COLED::clearP25Int() } void COLED::writeNXDNInt(const char* source, bool group, unsigned int dest, const char* type) +{ + CUserDBentry tmp; + + tmp.set(keyCALLSIGN, source); + writeNXDNIntEx(tmp, group, dest, type); +} + +int COLED::writeNXDNIntEx(const class CUserDBentry& source, bool group, unsigned int dest, const char* type) { m_mode = MODE_NXDN; m_display.clearDisplay(); m_display.fillRect(0, OLED_LINE2, m_display.width(), m_display.height(), BLACK); + m_display.setCursor(0,OLED_LINE2); + m_display.printf("%s %s", type, CALLandNAME(source).c_str()); + m_display.setCursor(0,OLED_LINE3); - m_display.printf("%s %.10s", type, source); + m_display.printf(" %s%u", group ? "TG" : "", dest); + + m_display.setCursor(0,OLED_LINE4); + m_display.printf("%s",source.get(keyCITY).c_str()); m_display.setCursor(0,OLED_LINE5); - m_display.printf(" %s%u", group ? "TG" : "", dest); + m_display.printf("%s",source.get(keySTATE).c_str()); + + m_display.setCursor(0,OLED_LINE6); + m_display.printf("%s",source.get(keyCOUNTRY).c_str()); OLED_statusbar(); m_display.display(); + // must be 0, to avoid calling writeNXDNInt() from CDisplay::writeNXDN() + return 0; } void COLED::clearNXDNInt() { m_display.fillRect(0, OLED_LINE2, m_display.width(), m_display.height(), BLACK); - m_display.setCursor(40,OLED_LINE4); + m_display.setCursor(40,OLED_LINE3); m_display.print("Listening"); m_display.setCursor(0,OLED_LINE6); @@ -537,8 +576,10 @@ void COLED::writePOCSAGInt(uint32_t ric, const std::string& message) m_display.setCursor(0,OLED_LINE3); m_display.printf("RIC: %u", ric); + m_display.setTextWrap(true); // text wrap temorally enable m_display.setCursor(0,OLED_LINE5); m_display.printf("MSG: %s", message.c_str()); + m_display.setTextWrap(false); OLED_statusbar(); m_display.display(); diff --git a/OLED.h b/OLED.h index e11a50a..5f3ead0 100644 --- a/OLED.h +++ b/OLED.h @@ -29,6 +29,7 @@ #include "Display.h" #include "Defines.h" +#include "UserDBentry.h" #include @@ -56,6 +57,7 @@ public: virtual void clearDStarInt(); virtual void writeDMRInt(unsigned int slotNo, const std::string& src, bool group, const std::string& dst, const char* type); + virtual int writeDMRIntEx(unsigned int slotNo, const class CUserDBentry& 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, unsigned char dgid, const char* type, const char* origin); @@ -65,6 +67,7 @@ public: virtual void clearP25Int(); virtual void writeNXDNInt(const char* source, bool group, unsigned int dest, const char* type); + virtual int writeNXDNIntEx(const class CUserDBentry& source, bool group, unsigned int dest, const char* type); virtual void clearNXDNInt(); virtual void writePOCSAGInt(uint32_t ric, const std::string& message); diff --git a/Version.h b/Version.h index 3e08686..28b79b8 100644 --- a/Version.h +++ b/Version.h @@ -19,6 +19,6 @@ #if !defined(VERSION_H) #define VERSION_H -const char* VERSION = "20200920"; +const char* VERSION = "20200924"; #endif diff --git a/YSFControl.cpp b/YSFControl.cpp index 408b819..ac16e55 100644 --- a/YSFControl.cpp +++ b/YSFControl.cpp @@ -321,9 +321,16 @@ bool CYSFControl::processVWData(bool valid, unsigned char *data) unsigned char fn = fich.getFN(); unsigned char ft = fich.getFT(); - if (fn != 0U || ft != 1U) { - // The first packet after the header is odd, don't try and regenerate it - unsigned int errors = m_rfPayload.processVoiceFRModeAudio(data + 2U); + if (fn == 0U && ft == 1U) { + // The first packet after the header is odd + m_rfPayload.processVoiceFRModeData(data + 2U); + unsigned int errors = m_rfPayload.processVoiceFRModeAudio2(data + 2U); + m_rfErrs += errors; + m_rfBits += 288U; + m_display->writeFusionBER(float(errors) / 2.88F); + LogDebug("YSF, V Mode 3, seq %u, AMBE FEC %u/288 (%.1f%%)", m_rfFrames % 128, errors, float(errors) / 2.88F); + } else { + unsigned int errors = m_rfPayload.processVoiceFRModeAudio5(data + 2U); m_rfErrs += errors; m_rfBits += 720U; m_display->writeFusionBER(float(errors) / 7.2F); @@ -991,9 +998,14 @@ void CYSFControl::writeNetwork() break; 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 - unsigned int errors = m_netPayload.processVoiceFRModeAudio(data + 35U); + if (fn == 0U && ft == 1U) { + // The first packet after the header is odd + m_netPayload.processVoiceFRModeData(data + 35U); + unsigned int errors = m_netPayload.processVoiceFRModeAudio2(data + 35U); + m_netErrs += errors; + m_netBits += 288U; + } else { + unsigned int errors = m_netPayload.processVoiceFRModeAudio5(data + 35U); m_netErrs += errors; m_netBits += 720U; } diff --git a/YSFPayload.cpp b/YSFPayload.cpp index 41e4d17..898e321 100644 --- a/YSFPayload.cpp +++ b/YSFPayload.cpp @@ -1,5 +1,5 @@ /* -* Copyright (C) 2016,2017 Jonathan Naylor, G4KLX +* Copyright (C) 2016,2017,2020 Jonathan Naylor, G4KLX * Copyright (C) 2016 Mathias Weyland, HB9FRV * * This program is free software; you can redistribute it and/or modify @@ -326,23 +326,23 @@ bool CYSFPayload::processVDMode1Data(unsigned char* data, unsigned char fn, bool break; case 3U: - CUtils::dump(1U, "V/D Mode 1 Data, DT1", output, 20U); + // CUtils::dump(1U, "V/D Mode 1 Data, DT1", output, 20U); break; case 4U: - CUtils::dump(1U, "V/D Mode 1 Data, DT2", output, 20U); + // CUtils::dump(1U, "V/D Mode 1 Data, DT2", output, 20U); break; case 5U: - CUtils::dump(1U, "V/D Mode 1 Data, DT3", output, 20U); + // CUtils::dump(1U, "V/D Mode 1 Data, DT3", output, 20U); break; case 6U: - CUtils::dump(1U, "V/D Mode 1 Data, DT4", output, 20U); + // CUtils::dump(1U, "V/D Mode 1 Data, DT4", output, 20U); break; case 7U: - CUtils::dump(1U, "V/D Mode 1 Data, DT5", output, 20U); + // CUtils::dump(1U, "V/D Mode 1 Data, DT5", output, 20U); break; default: @@ -526,11 +526,11 @@ bool CYSFPayload::processVDMode2Data(unsigned char* data, unsigned char fn, bool break; case 6U: - CUtils::dump(1U, "V/D Mode 2 Data, DT1", output, YSF_CALLSIGN_LENGTH); + // CUtils::dump(1U, "V/D Mode 2 Data, DT1", output, YSF_CALLSIGN_LENGTH); break; case 7U: - CUtils::dump(1U, "V/D Mode 2 Data, DT2", output, YSF_CALLSIGN_LENGTH); + // CUtils::dump(1U, "V/D Mode 2 Data, DT2", output, YSF_CALLSIGN_LENGTH); break; default: @@ -612,7 +612,7 @@ bool CYSFPayload::processDataFRModeData(unsigned char* data, unsigned char fn, b switch (fn) { case 0U: - CUtils::dump(1U, "FR Mode Data, CSD1", output, 20U); + // CUtils::dump(1U, "FR Mode Data, CSD1", output, 20U); if (m_dest == NULL) { m_dest = new unsigned char[YSF_CALLSIGN_LENGTH]; @@ -627,31 +627,31 @@ bool CYSFPayload::processDataFRModeData(unsigned char* data, unsigned char fn, b break; case 1U: - CUtils::dump(1U, "FR Mode Data, CSD3", output, 20U); + // CUtils::dump(1U, "FR Mode Data, CSD3", output, 20U); break; case 2U: - CUtils::dump(1U, "FR Mode Data, DT2", output, 20U); + // CUtils::dump(1U, "FR Mode Data, DT2", output, 20U); break; case 3U: - CUtils::dump(1U, "FR Mode Data, DT4", output, 20U); + // CUtils::dump(1U, "FR Mode Data, DT4", output, 20U); break; case 4U: - CUtils::dump(1U, "FR Mode Data, DT6", output, 20U); + // CUtils::dump(1U, "FR Mode Data, DT6", output, 20U); break; case 5U: - CUtils::dump(1U, "FR Mode Data, DT8", output, 20U); + // CUtils::dump(1U, "FR Mode Data, DT8", output, 20U); break; case 6U: - CUtils::dump(1U, "FR Mode Data, DT10", output, 20U); + // CUtils::dump(1U, "FR Mode Data, DT10", output, 20U); break; case 7U: - CUtils::dump(1U, "FR Mode Data, DT12", output, 20U); + // CUtils::dump(1U, "FR Mode Data, DT12", output, 20U); break; default: @@ -720,7 +720,7 @@ bool CYSFPayload::processDataFRModeData(unsigned char* data, unsigned char fn, b switch (fn) { case 0U: - CUtils::dump(1U, "FR Mode Data, CSD2", output, 20U); + // CUtils::dump(1U, "FR Mode Data, CSD2", output, 20U); if (m_downlink != NULL && !gateway) ::memcpy(output + 0U, m_downlink, YSF_CALLSIGN_LENGTH); @@ -731,31 +731,31 @@ bool CYSFPayload::processDataFRModeData(unsigned char* data, unsigned char fn, b break; case 1U: - CUtils::dump(1U, "FR Mode Data, DT1", output, 20U); + // CUtils::dump(1U, "FR Mode Data, DT1", output, 20U); break; case 2U: - CUtils::dump(1U, "FR Mode Data, DT3", output, 20U); + // CUtils::dump(1U, "FR Mode Data, DT3", output, 20U); break; case 3U: - CUtils::dump(1U, "FR Mode Data, DT5", output, 20U); + // CUtils::dump(1U, "FR Mode Data, DT5", output, 20U); break; case 4U: - CUtils::dump(1U, "FR Mode Data, DT7", output, 20U); + // CUtils::dump(1U, "FR Mode Data, DT7", output, 20U); break; case 5U: - CUtils::dump(1U, "FR Mode Data, DT9", output, 20U); + // CUtils::dump(1U, "FR Mode Data, DT9", output, 20U); break; case 6U: - CUtils::dump(1U, "FR Mode Data, DT11", output, 20U); + // CUtils::dump(1U, "FR Mode Data, DT11", output, 20U); break; case 7U: - CUtils::dump(1U, "FR Mode Data, DT13", output, 20U); + // CUtils::dump(1U, "FR Mode Data, DT13", output, 20U); break; default: @@ -799,7 +799,21 @@ bool CYSFPayload::processDataFRModeData(unsigned char* data, unsigned char fn, b return ret1 && (fn == 0U); } -unsigned int CYSFPayload::processVoiceFRModeAudio(unsigned char* data) +unsigned int CYSFPayload::processVoiceFRModeAudio2(unsigned char* data) +{ + assert(data != NULL); + + data += YSF_SYNC_LENGTH_BYTES + YSF_FICH_LENGTH_BYTES; + + // Regenerate the IMBE FEC + unsigned int errors = 0U; + errors += m_fec.regenerateIMBE(data + 54U); + errors += m_fec.regenerateIMBE(data + 72U); + + return errors; +} + +unsigned int CYSFPayload::processVoiceFRModeAudio5(unsigned char* data) { assert(data != NULL); @@ -816,6 +830,62 @@ unsigned int CYSFPayload::processVoiceFRModeAudio(unsigned char* data) return errors; } +bool CYSFPayload::processVoiceFRModeData(unsigned char* data) +{ + assert(data != NULL); + + data += YSF_SYNC_LENGTH_BYTES + YSF_FICH_LENGTH_BYTES; + + unsigned char dch[45U]; + ::memcpy(dch, data, 45U); + + CYSFConvolution conv; + conv.start(); + + for (unsigned int i = 0U; i < 180U; i++) { + unsigned int n = INTERLEAVE_TABLE_9_20[i]; + uint8_t s0 = READ_BIT1(dch, n) ? 1U : 0U; + + n++; + uint8_t s1 = READ_BIT1(dch, n) ? 1U : 0U; + + conv.decode(s0, s1); + } + + unsigned char output[23U]; + conv.chainback(output, 176U); + + bool ret = CCRC::checkCCITT162(output, 22U); + if (ret) { + CCRC::addCCITT162(output, 22U); + output[22U] = 0x00U; + + unsigned char convolved[45U]; + conv.encode(output, convolved, 180U); + + unsigned char bytes[45U]; + unsigned int j = 0U; + for (unsigned int i = 0U; i < 180U; i++) { + unsigned int n = INTERLEAVE_TABLE_9_20[i]; + + bool s0 = READ_BIT1(convolved, j) != 0U; + j++; + + bool s1 = READ_BIT1(convolved, j) != 0U; + j++; + + WRITE_BIT1(bytes, n, s0); + + n++; + WRITE_BIT1(bytes, n, s1); + } + + ::memcpy(data, bytes, 45U); + } + + return ret; +} + void CYSFPayload::writeHeader(unsigned char* data, const unsigned char* csd1, const unsigned char* csd2) { assert(data != NULL); diff --git a/YSFPayload.h b/YSFPayload.h index 1baac99..ab9e6ca 100644 --- a/YSFPayload.h +++ b/YSFPayload.h @@ -1,5 +1,5 @@ /* -* Copyright (C) 2016,2017 by Jonathan Naylor G4KLX +* Copyright (C) 2016,2017,2020 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 @@ -38,7 +38,10 @@ public: bool processDataFRModeData(unsigned char* bytes, unsigned char fn, bool gateway = false); - unsigned int processVoiceFRModeAudio(unsigned char* bytes); + bool processVoiceFRModeData(unsigned char* bytes); + + unsigned int processVoiceFRModeAudio2(unsigned char* bytes); + unsigned int processVoiceFRModeAudio5(unsigned char* bytes); void writeHeader(unsigned char* data, const unsigned char* csd1, const unsigned char* csd2);