diff --git a/YSF2DMR/APRSWriter.cpp b/YSF2DMR/APRSWriter.cpp index be7fce7..9199870 100644 --- a/YSF2DMR/APRSWriter.cpp +++ b/YSF2DMR/APRSWriter.cpp @@ -1,5 +1,7 @@ /* * Copyright (C) 2010-2014,2016,2017 by Jonathan Naylor G4KLX + * Copyright (C) 2018 by Manuel Sanchez EA7EE + * Copyright (C) 2018 by Andy Uribe CA6JAU * * 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 @@ -70,7 +72,7 @@ bool CAPRSWriter::open() return m_thread->start(); } -void CAPRSWriter::write(const unsigned char* source, const char* type, unsigned char radio, float fLatitude, float fLongitude) +void CAPRSWriter::write(const unsigned char* source, const char* type, unsigned char radio, float fLatitude, float fLongitude, unsigned int tg_qrv) { assert(source != NULL); assert(type != NULL); @@ -99,29 +101,38 @@ void CAPRSWriter::write(const unsigned char* source, const char* type, unsigned ::sprintf(lon, "%08.2lf", longitude); char symbol; + char suffix[3]; switch (radio) { case 0x24U: case 0x28U: symbol = '['; + strcpy(suffix, "-7"); break; case 0x25U: case 0x29U: symbol = '>'; + strcpy(suffix, "-9"); break; case 0x26U: symbol = 'r'; + strcpy(suffix, "-1"); break; + case 0x27U: + symbol = '-'; + strcpy(suffix, "-2"); + break; default: symbol = '-'; + strcpy(suffix, "-2"); break; } char output[300U]; - ::sprintf(output, "%s-Y>APDPRS,C4FM*,qAR,%s:!%s%c/%s%c%c %s via MMDVM", - callsign, m_callsign.c_str(), + ::sprintf(output, "%s%s>APDPRS,C4FM*,qAR,%s:!%s%c/%s%c%c %s QRV TG %d via MMDVM", + callsign, suffix, m_callsign.c_str(), lat, (fLatitude < 0.0F) ? 'S' : 'N', lon, (fLongitude < 0.0F) ? 'W' : 'E', - symbol, type); + symbol, type, tg_qrv); m_thread->write(output); } diff --git a/YSF2DMR/APRSWriter.h b/YSF2DMR/APRSWriter.h index 442dd17..02f8f8f 100644 --- a/YSF2DMR/APRSWriter.h +++ b/YSF2DMR/APRSWriter.h @@ -1,5 +1,7 @@ /* * Copyright (C) 2010,2011,2012,2016,2017 by Jonathan Naylor G4KLX + * Copyright (C) 2018 by Manuel Sanchez EA7EE + * Copyright (C) 2018 by Andy Uribe CA6JAU * * 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 @@ -33,7 +35,7 @@ public: void setInfo(unsigned int txFrequency, unsigned int rxFrequency, float latitude, float longitude, int height, const std::string& desc); - void write(const unsigned char* source, const char* type, unsigned char radio, float latitude, float longitude); + void write(const unsigned char* source, const char* type, unsigned char radio, float latitude, float longitude, unsigned int tg_qrv); void clock(unsigned int ms); diff --git a/YSF2DMR/Conf.cpp b/YSF2DMR/Conf.cpp index 43886a5..d788179 100644 --- a/YSF2DMR/Conf.cpp +++ b/YSF2DMR/Conf.cpp @@ -86,6 +86,7 @@ m_aprsEnabled(false), m_aprsServer(), m_aprsPort(0U), m_aprsPassword(), +m_aprsCallsign(), m_aprsAPIKey(), m_aprsRefresh(120), m_aprsDescription() @@ -246,7 +247,12 @@ bool CConf::read() else if (::strcmp(key, "DisplayLevel") == 0) m_logDisplayLevel = (unsigned int)::atoi(value); } else if (section == SECTION_APRS_FI) { - if (::strcmp(key, "Enable") == 0) + if (::strcmp(key, "AprsCallsign") == 0) { + // Convert the callsign to upper case + for (unsigned int i = 0U; value[i] != 0; i++) + value[i] = ::toupper(value[i]); + m_aprsCallsign = value; + } else if (::strcmp(key, "Enable") == 0) m_aprsEnabled = ::atoi(value) == 1; else if (::strcmp(key, "Server") == 0) m_aprsServer = value; @@ -408,6 +414,11 @@ unsigned int CConf::getAPRSPort() const return m_aprsPort; } +std::string CConf::getAPRSCallsign() const +{ + return m_aprsCallsign; +} + std::string CConf::getAPRSPassword() const { return m_aprsPassword; diff --git a/YSF2DMR/Conf.h b/YSF2DMR/Conf.h index c7f0214..1e355f8 100644 --- a/YSF2DMR/Conf.h +++ b/YSF2DMR/Conf.h @@ -54,7 +54,7 @@ public: std::string getLocation() const; std::string getDescription() const; std::string getURL() const; - + // The DMR Network section unsigned int getDMRId() const; std::string getDMRXLXFile() const; @@ -90,9 +90,10 @@ public: std::string getAPRSServer() const; unsigned int getAPRSPort() const; std::string getAPRSPassword() const; + std::string getAPRSCallsign() const; std::string getAPRSAPIKey() const; - unsigned int getAPRSRefresh() const; - std::string getAPRSDescription() const; + unsigned int getAPRSRefresh() const; + std::string getAPRSDescription() const; private: std::string m_file; @@ -116,7 +117,7 @@ private: std::string m_location; std::string m_description; std::string m_url; - + unsigned int m_dmrId; std::string m_dmrXLXFile; std::string m_dmrXLXModule; @@ -148,6 +149,7 @@ private: std::string m_aprsServer; unsigned int m_aprsPort; std::string m_aprsPassword; + std::string m_aprsCallsign; std::string m_aprsAPIKey; unsigned int m_aprsRefresh; std::string m_aprsDescription; diff --git a/YSF2DMR/GPS.cpp b/YSF2DMR/GPS.cpp index efee7f0..3520151 100644 --- a/YSF2DMR/GPS.cpp +++ b/YSF2DMR/GPS.cpp @@ -1,5 +1,7 @@ /* * Copyright (C) 2016,2017 by Jonathan Naylor G4KLX +* Copyright (C) 2018 by Manuel Sanchez EA7EE +* Copyright (C) 2018 by Andy Uribe CA6JAU * * 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 @@ -59,7 +61,7 @@ bool CGPS::open() return m_writer.open(); } -void CGPS::data(const unsigned char* source, const unsigned char* data, unsigned char fi, unsigned char dt, unsigned char fn, unsigned char ft) +void CGPS::data(const unsigned char* source, const unsigned char* data, unsigned char fi, unsigned char dt, unsigned char fn, unsigned char ft, unsigned int tg_qrv) { if (m_sent) return; @@ -68,6 +70,7 @@ void CGPS::data(const unsigned char* source, const unsigned char* data, unsigned return; CYSFPayload payload; + m_tg_qrv = tg_qrv; if (dt == YSF_DT_VD_MODE1) { if (fn == 0U || fn == 1U || fn == 2U) @@ -93,12 +96,12 @@ void CGPS::data(const unsigned char* source, const unsigned char* data, unsigned if (valid) { if (::memcmp(m_buffer + 1U, SHRT_GPS, 2U) == 0) { - CUtils::dump("Short GPS data received", m_buffer, (fn - 2U) * 20U); + // CUtils::dump("Short GPS data received", m_buffer, (fn - 2U) * 20U); transmitGPS(source); } if (::memcmp(m_buffer + 1U, LONG_GPS, 2U) == 0) { - CUtils::dump("Long GPS data received", m_buffer, (fn - 2U) * 20U); + // CUtils::dump("Long GPS data received", m_buffer, (fn - 2U) * 20U); transmitGPS(source); } @@ -128,18 +131,56 @@ void CGPS::data(const unsigned char* source, const unsigned char* data, unsigned if (valid) { if (::memcmp(m_buffer + 1U, SHRT_GPS, 2U) == 0) { - CUtils::dump("Short GPS data received", m_buffer, (fn - 5U) * 10U); + // CUtils::dump("Short GPS data received", m_buffer, (fn - 5U) * 10U); transmitGPS(source); } if (::memcmp(m_buffer + 1U, LONG_GPS, 2U) == 0) { - CUtils::dump("Long GPS data received", m_buffer, (fn - 5U) * 10U); + // CUtils::dump("Long GPS data received", m_buffer, (fn - 5U) * 10U); transmitGPS(source); } m_sent = true; } } + } else if (dt == YSF_DT_DATA_FR_MODE) { + if (fn == 1U) { + bool valid = payload.readDataFRModeData2(data, m_buffer + 0U); + if (!valid) return; + } else { + bool valid = payload.readDataFRModeData1(data, m_buffer + (fn - 1U) * 20U + 0U); + if (!valid) return; + + valid = payload.readDataFRModeData2(data, m_buffer + (fn - 1U) * 20U + 20U); + if (!valid) return; + } + + if (fn == ft) { + bool valid = false; + // Find the end marker + for (unsigned int i = fn * 20U; i > 0U; i--) { + if (m_buffer[i] == 0x03U) { + unsigned char crc = CCRC::addCRC(m_buffer, i + 1U); + if (crc == m_buffer[i + 1U]) + valid = true; + break; + } + } + + if (valid) { + if (::memcmp(m_buffer + 1U, SHRT_GPS, 2U) == 0) { + // CUtils::dump("Short GPS data received", m_buffer, fn * 20U); + transmitGPS(source); + } + + if (::memcmp(m_buffer + 1U, LONG_GPS, 2U) == 0) { + // CUtils::dump("Long GPS data received", m_buffer, fn * 20U); + transmitGPS(source); + } + + m_sent = true; + } + } } } @@ -262,6 +303,9 @@ void CGPS::transmitGPS(const unsigned char* source) case 0x26U: ::strcpy(radio, "DR-1X"); break; + case 0x27U: + ::strcpy(radio, "FT-991"); + break; case 0x28U: ::strcpy(radio, "FT-2D"); break; @@ -275,7 +319,7 @@ void CGPS::transmitGPS(const unsigned char* source) LogMessage("GPS Position from %10.10s of radio=%s lat=%f long=%f", source, radio, latitude, longitude); - m_writer.write(source, radio, m_buffer[4U], latitude, longitude); + m_writer.write(source, radio, m_buffer[4U], latitude, longitude, m_tg_qrv); m_sent = true; } diff --git a/YSF2DMR/GPS.h b/YSF2DMR/GPS.h index f168dfd..3346748 100644 --- a/YSF2DMR/GPS.h +++ b/YSF2DMR/GPS.h @@ -1,5 +1,7 @@ /* * Copyright (C) 2016,2017 by Jonathan Naylor G4KLX +* Copyright (C) 2018 by Manuel Sanchez EA7EE +* Copyright (C) 2018 by Andy Uribe CA6JAU * * 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 @@ -32,7 +34,7 @@ public: bool open(); - void data(const unsigned char* source, const unsigned char* data, unsigned char fi, unsigned char dt, unsigned char fn, unsigned char ft); + void data(const unsigned char* source, const unsigned char* data, unsigned char fi, unsigned char dt, unsigned char fn, unsigned char ft, unsigned int tg_qrv); void clock(unsigned int ms); @@ -44,6 +46,7 @@ private: CAPRSWriter m_writer; unsigned char* m_buffer; bool m_sent; + unsigned int m_tg_qrv; void transmitGPS(const unsigned char* source); }; diff --git a/YSF2DMR/YSF2DMR.cpp b/YSF2DMR/YSF2DMR.cpp index 50cde26..18ba190 100644 --- a/YSF2DMR/YSF2DMR.cpp +++ b/YSF2DMR/YSF2DMR.cpp @@ -595,7 +595,7 @@ int CYSF2DMR::run() } if (m_gps != NULL) - m_gps->data(buffer + 14U, buffer + 35U, fi, dt, fn, ft); + m_gps->data(buffer + 14U, buffer + 35U, fi, dt, fn, ft, m_dstid); } @@ -1127,15 +1127,20 @@ void CYSF2DMR::createGPS() std::string hostname = m_conf.getAPRSServer(); unsigned int port = m_conf.getAPRSPort(); std::string password = m_conf.getAPRSPassword(); + std::string callsign = m_conf.getAPRSCallsign(); std::string desc = m_conf.getAPRSDescription(); + if (callsign.empty()) + callsign = m_callsign; + LogMessage("APRS Parameters"); + LogMessage(" Callsign: %s", callsign.c_str()); LogMessage(" Server: %s", hostname.c_str()); LogMessage(" Port: %u", port); LogMessage(" Passworwd: %s", password.c_str()); LogMessage(" Description: %s", desc.c_str()); - m_gps = new CGPS(m_callsign, m_suffix, password, hostname, port); + m_gps = new CGPS(callsign, m_suffix, password, hostname, port); unsigned int txFrequency = m_conf.getTxFrequency(); unsigned int rxFrequency = m_conf.getRxFrequency(); diff --git a/YSF2DMR/YSF2DMR.ini b/YSF2DMR/YSF2DMR.ini index bbc1bd7..c989393 100644 --- a/YSF2DMR/YSF2DMR.ini +++ b/YSF2DMR/YSF2DMR.ini @@ -55,6 +55,7 @@ FileRoot=YSF2DMR [aprs.fi] Enable=0 +AprsCallsign=G9BF # Server=noam.aprs2.net Server=euro.aprs2.net Port=14580 diff --git a/YSF2DMR/contrib/TGList.txt b/YSF2DMR/contrib/TGList.txt new file mode 100644 index 0000000..09fc750 --- /dev/null +++ b/YSF2DMR/contrib/TGList.txt @@ -0,0 +1,41 @@ +# Example TGList.txt file +# Enter only your preferred DMR ID list +# +# Format: +# Dest ID;Option;Name;Description +# +# Option: TG:0, REF:1, PC:2 +# +9;0;Local;Local +91;0;World-wide;World-wide +92;0;Europe;Europe +93;0;North America;North America +208;0;France;France +214;0;Spain;Spain +222;0;Italia;Italia +232;0;Austria;Austria +235;0;United Kingdom;United Kingdom +262;0;Germany;Germany +310;0;TAC-310 USA;TAC-310 USA +334;0;Mexico;Mexico +722;0;Argentina;Argentina +724;0;Brazil;Brazil +730;0;Chile;Chile +913;0;English;English +914;0;Spanish;Spanish +3100;0;USA Nationwide;USA Nationwide +4000;1;Ref Unlink;Ref Unlink +4300;1;Ref France;France +4326;1;Ref Canada;Canada +4373;1;Ref CT Catalana;CT Catalana +4374;1;Ref Spain 4;Ref Spain 4 +4400;1;Ref UK;United Kingdom +4426;1;Ref Allstar;Allstar Link +4639;1;Ref WorldWide;WorldWide +4640;1;Ref USA;USA - Area 0 +4790;1;Ref Mexico;XE (334) +5000;1;Ref Status; Ref Status +9990;2;Parrot;Parrot +21461;0;YSF-EA5SPAIN;YSF-EA5SPAIN +94790;1;Ref2 Mexico;DMRGateway + diff --git a/YSF2DMR/contrib/tg_generate.py b/YSF2DMR/contrib/tg_generate.py new file mode 100644 index 0000000..31e6c95 --- /dev/null +++ b/YSF2DMR/contrib/tg_generate.py @@ -0,0 +1,42 @@ +f=open('/tmp/group.txt') +group=f.readlines() +f.close() + +def tg_count(tg_num): + count=0 + global group + for line in group: + newlines=line.split('},') + for item in newlines: + if (item.find(','+str(tg_num)+',')>0): count+=1 + return count + +f = open('/tmp/data.json') +content = f.readlines() +f.close() + +content=[x.strip() for x in content] +content = content[1:] +content = content[:-1] +f = open("/tmp/TGList.txt","wb+") + +for line in content: + line=line.replace('"','') + line=line.replace(',','') + line=line.split(':') + i=line[0] + val=int(i) + if (val>=4000) and (val<=5000): + b='1' + else: + b='0' + if val==9990: b=2 + if (val>90) and (val!=4000) and (val!=5000) and (val!=9990): + c=tg_count(val) + else: + c=0 + line=line[0]+';'+ str(b)+';'+str(c)+';'+line[1]+';'+line[1] + line=line.replace(' ','') + f.write(line+'\n') + +f.close() diff --git a/YSF2DMR/contrib/updateTGList.sh b/YSF2DMR/contrib/updateTGList.sh new file mode 100644 index 0000000..a7b8fc8 --- /dev/null +++ b/YSF2DMR/contrib/updateTGList.sh @@ -0,0 +1,8 @@ +wget -O /tmp/group.txt http://master.brandmeister.es/status/status.php +wget -O /tmp/data.json http://api.brandmeister.network/v1.0/groups/ +/usr/bin/python /usr/local/bin/tg_generate.py +mount -o remount,rw / +rm /usr/local/etc/TGList.txt +mv /tmp/TGList.txt /usr/local/etc +mount -o remount,ro / +exit 0