From 65399cdf14d352feebf0b2d218ea033143fe402a Mon Sep 17 00:00:00 2001 From: Adi Bier / DL1HRC Date: Fri, 24 Sep 2021 23:34:12 +0200 Subject: [PATCH] =?UTF-8?q?New=20param=20TETRA=5FUSER=5FINFOFILE=20reads?= =?UTF-8?q?=20the=20data=20of=20the=20tetra=20users=20from=20a=20new=20jso?= =?UTF-8?q?n=20file=20and=20not=20from=20a=20svxlink.conf=20section=20anym?= =?UTF-8?q?ore.=20Please=20move=20all=20tetra=20user=20data=20(normally=20?= =?UTF-8?q?configured=20in=20svxlink.conf-section=20[Tetra=5FUsers])=20to?= =?UTF-8?q?=20the=20new=20json=20file.=20The=20param=20TETRA=5FUSERS=20sho?= =?UTF-8?q?uld=20not=20be=20used=20anymore!=20An=20example=20of=20the=20ne?= =?UTF-8?q?w=20json=20file=20can=20be=20found=20here=20src/svxlink/svxlink?= =?UTF-8?q?/tetra=5Fusers.json).=20Better=20handling=20if=20TSI=E2=80=98s?= =?UTF-8?q?=20are=20shorter=20than=20expected=20(09011638300023456=20||=20?= =?UTF-8?q?901999900023456).=20Extended=20data=20transmission=20between=20?= =?UTF-8?q?node=20and=20reflector=20to=20support=20the=20work=20of=20the?= =?UTF-8?q?=20dashboard=20developers.=20Very=20important:=20You=20have=20t?= =?UTF-8?q?o=20update=20all=20nodes=20and=20reflectors!?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/svxlink/reflector/Reflector.cpp | 27 +- src/svxlink/reflector/Reflector.h | 1 + src/svxlink/svxlink/CMakeLists.txt | 1 + src/svxlink/svxlink/ReflectorLogic.cpp | 24 +- src/svxlink/svxlink/ReflectorLogic.h | 2 +- src/svxlink/svxlink/TetraLib.h | 41 +++ src/svxlink/svxlink/TetraLogic.cpp | 332 ++++++++++++++++++------- src/svxlink/svxlink/TetraLogic.h | 5 + src/svxlink/svxlink/svxlink.conf.in | 5 +- 9 files changed, 336 insertions(+), 102 deletions(-) diff --git a/src/svxlink/reflector/Reflector.cpp b/src/svxlink/reflector/Reflector.cpp index d4c03776..c48d6798 100644 --- a/src/svxlink/reflector/Reflector.cpp +++ b/src/svxlink/reflector/Reflector.cpp @@ -263,13 +263,13 @@ void Reflector::updateUserdata(Json::Value user_arr) m_user.issi = t_userdata.get("tsi", "").asString(); m_user.name = t_userdata.get("name","").asString(); m_user.call = t_userdata.get("call","").asString(); + m_user.location = t_userdata.get("location","").asString(); m_user.aprs_sym = static_cast(t_userdata.get("sym","").asInt()); m_user.aprs_tab = static_cast(t_userdata.get("tab","").asInt()); m_user.comment = t_userdata.get("comment","").asString(); - if (t_userdata.get("last_activity","").asString().length() > 0) + if (t_userdata.get("last_activity","").asUInt() > 0) { - m_user.last_activity = (time_t) strtol( - t_userdata.get("last_activity","").asString().c_str(), NULL, 10); + m_user.last_activity = (time_t) t_userdata.get("last_activity","").asUInt(); } std::map::iterator iu; @@ -280,6 +280,7 @@ void Reflector::updateUserdata(Json::Value user_arr) iu->second.aprs_sym = m_user.aprs_sym; iu->second.aprs_tab = m_user.aprs_tab; iu->second.comment = m_user.comment; + iu->second.location = m_user.location; if (m_user.last_activity) { iu->second.last_activity = m_user.last_activity; @@ -288,8 +289,8 @@ void Reflector::updateUserdata(Json::Value user_arr) if (debug) { cout << "UPDATE: call=" << m_user.call << ", issi=" << m_user.issi - << ", name=" << m_user.name << " (" << m_user.comment << ")" - << endl; + << ", name=" << m_user.name << ", location=" << m_user.location + << " (" << m_user.comment << ")" << endl; } } else @@ -298,8 +299,8 @@ void Reflector::updateUserdata(Json::Value user_arr) if (debug) { cout << "New user: call=" << m_user.call << ", issi=" << m_user.issi - << ", name=" << m_user.name << " (" << m_user.comment << ")" - << endl; + << ", name=" << m_user.name << ", location=" << m_user.location + << " (" << m_user.comment << ")" << endl; } } } @@ -908,11 +909,11 @@ bool Reflector::getUserData(void) m_user.issi = t_userdata.get("tsi", "").asString(); m_user.name = t_userdata.get("name","").asString(); m_user.call = t_userdata.get("call","").asString(); + m_user.location = t_userdata.get("location","").asString(); m_user.aprs_sym = static_cast(t_userdata.get("sym","").asInt()); m_user.aprs_tab = static_cast(t_userdata.get("tab","").asInt()); m_user.comment = t_userdata.get("comment","").asString(); - m_user.last_activity = (time_t) strtol( - t_userdata.get("last_activity","").asString().c_str(), NULL, 10); + m_user.last_activity = (time_t) t_userdata.get("last_activity","").asUInt(); userdata[m_user.issi] = m_user; } cout << "+++ " << cfg_root.size() << " users loaded from '" @@ -933,15 +934,11 @@ void Reflector::writeUserData(std::map userdata) t_userinfo["tsi"] = iu->second.issi; t_userinfo["call"] = iu->second.call; t_userinfo["name"] = iu->second.name; + t_userinfo["location"] = iu->second.location; t_userinfo["sym"] = iu->second.aprs_sym; t_userinfo["tab"] = iu->second.aprs_tab; t_userinfo["comment"] = iu->second.comment; - if (iu->second.last_activity) - { - stringstream la; - la << iu->second.last_activity; - t_userinfo["last_activity"] = la.str(); - } + t_userinfo["last_activity"] = static_cast(iu->second.last_activity); event.append(t_userinfo); } diff --git a/src/svxlink/reflector/Reflector.h b/src/svxlink/reflector/Reflector.h index 1207b7e7..7e65546d 100644 --- a/src/svxlink/reflector/Reflector.h +++ b/src/svxlink/reflector/Reflector.h @@ -224,6 +224,7 @@ class Reflector : public sigc::trackable std::string issi; std::string call; std::string name; + std::string location; std::string comment; float lat; float lon; diff --git a/src/svxlink/svxlink/CMakeLists.txt b/src/svxlink/svxlink/CMakeLists.txt index d3e80e3f..46d3d6f6 100644 --- a/src/svxlink/svxlink/CMakeLists.txt +++ b/src/svxlink/svxlink/CMakeLists.txt @@ -99,6 +99,7 @@ install_if_not_exists(${CMAKE_CURRENT_BINARY_DIR}/gpio.conf ${SVX_SYSCONF_INSTALL_DIR} ) install_if_not_exists(node_info.json ${SVX_SYSCONF_INSTALL_DIR}) +install_if_not_exists(tetra_users.json ${SVX_SYSCONF_INSTALL_DIR}) install(FILES events.tcl DESTINATION ${SVX_SHARE_INSTALL_DIR}) install(FILES RepeaterLogic.tcl SimplexLogic.tcl ReflectorLogic.tcl Module.tcl Logic.tcl CW.tcl SelCall.tcl TetraLogic.tcl locale.tcl diff --git a/src/svxlink/svxlink/ReflectorLogic.cpp b/src/svxlink/svxlink/ReflectorLogic.cpp index 3192cff7..aedee510 100644 --- a/src/svxlink/svxlink/ReflectorLogic.cpp +++ b/src/svxlink/svxlink/ReflectorLogic.cpp @@ -670,8 +670,19 @@ void ReflectorLogic::remoteReceivedPublishStateEvent( } sendMsg(msg); } - else if (event_name == "Sds:info" || event_name == "TetraUsers:info" || - event_name == "QsoInfo:state") + else if (event_name == "QsoInfo:state") + { + std::istringstream is(data); + Json::Value user_info; + is >> user_info; + user_info["TG"] = m_selected_tg; + string ud =jsonToString(user_info); + + // cout << "sende: " << event_name << "," << ud << endl; + MsgStateEvent msg(logic->name(), event_name, ud); + sendMsg(msg); + } + else if (event_name == "Sds:info" || event_name == "TetraUsers:info") { // cout << "sende: " << event_name << "," << data << endl; MsgStateEvent msg(logic->name(), event_name, data); @@ -1789,6 +1800,15 @@ void ReflectorLogic::handlePlayDtmf(const std::string& digit, int amp, } /* ReflectorLogic::handlePlayDtmf */ +string ReflectorLogic::jsonToString(Json::Value eventmessage) +{ + Json::FastWriter jsontoString; + std::string message = jsontoString.write(eventmessage); + message.erase(std::remove_if(message.begin(), message.end(), + [](unsigned char x){return std::iscntrl(x);})); + return message; +} /* ReflectorLogic::jsonToString */ + /* * This file has not been truncated */ diff --git a/src/svxlink/svxlink/ReflectorLogic.h b/src/svxlink/svxlink/ReflectorLogic.h index 0618a7f2..199a1855 100644 --- a/src/svxlink/svxlink/ReflectorLogic.h +++ b/src/svxlink/svxlink/ReflectorLogic.h @@ -305,7 +305,7 @@ class ReflectorLogic : public LogicBase void handlePlaySilence(int duration); void handlePlayTone(int fq, int amp, int duration); void handlePlayDtmf(const std::string& digit, int amp, int duration); - + std::string jsonToString(Json::Value eventmessage); }; /* class ReflectorLogic */ diff --git a/src/svxlink/svxlink/TetraLib.h b/src/svxlink/svxlink/TetraLib.h index d67deba8..a139b7b5 100644 --- a/src/svxlink/svxlink/TetraLib.h +++ b/src/svxlink/svxlink/TetraLib.h @@ -718,6 +718,47 @@ std::string getISSI(std::string tsi) } /* getISSI */ +bool splitTsi(std::string tsi, int &mcc, int &mnc, int &issi) +{ + bool ret = false; + size_t len = tsi.length(); + + if (len < 9) + { + issi = atoi(tsi.c_str()); + mcc = 0; + mnc = 0; + ret = true; + } + else + { + issi = atoi(tsi.substr(len-8,8).c_str()); + std::string t = tsi.substr(0, len-8); + + if (t.length() == 7) + { + mcc = atoi(t.substr(0,3).c_str()); + mnc = atoi(t.substr(3,4).c_str()); + ret = true; + } + else if (t.length() == 8) + { + mcc = atoi(t.substr(0,3).c_str()); + mnc = atoi(t.substr(3,5).c_str()); + ret = true; + } + else if (t.length() == 9) + { + mcc = atoi(t.substr(0,4).c_str()); + mnc = atoi(t.substr(4,5).c_str()); + ret = true; + } + else ret = false; + } + return ret; +} /* splitTsi */ + + unsigned int hex2int(std::string sds) { unsigned int t; diff --git a/src/svxlink/svxlink/TetraLogic.cpp b/src/svxlink/svxlink/TetraLogic.cpp index 6f4a8919..517fff97 100644 --- a/src/svxlink/svxlink/TetraLogic.cpp +++ b/src/svxlink/svxlink/TetraLogic.cpp @@ -39,6 +39,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA #include #include #include +#include /**************************************************************************** @@ -127,6 +128,7 @@ using namespace SvxLink; #define LOGINFO 2 #define LOGDEBUG 3 +#define TETRA_LOGIC_VERSION "24092021" /**************************************************************************** * @@ -197,7 +199,8 @@ TetraLogic::TetraLogic(Async::Config& cfg, const string& name) peiBreakCommandTimer(3000, Timer::TYPE_ONESHOT, false), proximity_warning(3.1), time_between_sds(3600), own_lat(0.0), own_lon(0.0), endCmd(""), new_sds(false), inTransmission(false), - cmgs_received(true), share_userinfo(true), current_cci(0) + cmgs_received(true), share_userinfo(true), current_cci(0), dmnc(0), + dmcc(0) { peiComTimer.expired.connect(mem_fun(*this, &TetraLogic::onComTimeout)); peiActivityTimer.expired.connect(mem_fun(*this, @@ -277,6 +280,8 @@ bool TetraLogic::initialize(void) value += mcc; mcc = value.substr(value.length()-4,4); } + dmcc = atoi(mcc.c_str()); + if (!cfg().getValue(name(), "APRSPATH", aprspath)) { aprspath = "APRS,qAR,"; @@ -299,6 +304,8 @@ bool TetraLogic::initialize(void) value += mnc; mnc = value.substr(value.length()-5,5); } + dmnc = atoi(mnc.c_str()); + // Welcome message to new users if (!cfg().getValue(name(), "INFO_SDS", infosds)) { @@ -359,6 +366,14 @@ bool TetraLogic::initialize(void) string user_section; if (cfg().getValue(name(), "TETRA_USERS", user_section)) { + cout + << "***************************************************************\n" + << "* WARNING: The parameter TETRA_USERS is outdated and will be *\n" + << "* removed soon. Use TETRA_USER_INFOFILE=tetra_users.json in- *\n" + << "* stead and transfer your tetra user data into the json file. *\n" + << "* You will find an example of tetra_users.json in *\n" + << "* src/svxlink/svxlink directory *\n" + << "***************************************************************\n"; list user_list = cfg().listSection(user_section); User m_user; @@ -399,6 +414,80 @@ bool TetraLogic::initialize(void) } } + std::string user_info_file; + if (cfg().getValue(name(), "TETRA_USER_INFOFILE", user_info_file)) + { + std::ifstream user_info_is(user_info_file.c_str(), std::ios::in); + if (user_info_is.good()) + { + try + { + if (!(user_info_is >> m_user_info)) + { + std::cerr << "*** ERROR: Failure while reading user information file " + "\"" << user_info_file << "\"" + << std::endl; + isok = false; + } + } + catch (const Json::Exception& e) + { + std::cerr << "*** ERROR: Failure while reading user information " + "file \"" << user_info_file << "\": " + << e.what() + << std::endl; + isok = false; + } + } + else + { + std::cerr << "*** ERROR: Could not open user information file " + "\"" << user_info_file << "\"" + << std::endl; + isok = false; + } + + User m_user; + for (Json::Value::ArrayIndex i = 0; i < m_user_info.size(); i++) + { + Json::Value& t_userdata = m_user_info[i]; + m_user.issi = t_userdata.get("tsi", "").asString(); + if (m_user.issi.length() != 17) + { + cout << "*** ERROR: The TSI must have a length of 17 digits.\n" + << "\" Check dataset " << i + 1 << " in \"" << user_info_file + << "\"" << endl; + isok = false; + } + m_user.name = t_userdata.get("name","").asString(); + m_user.call = t_userdata.get("call","").asString(); + m_user.location = t_userdata.get("location","").asString(); + if (t_userdata.get("symbol","").asString().length() != 2) + { + cout << "*** ERROR: Aprs symbol in \"" << user_info_file + << "\" dataset " << i + 1 << " is not correct, must have 2 digits!" + << endl; + isok = false; + } + else + { + m_user.aprs_sym = t_userdata.get("symbol","").asString()[0]; + m_user.aprs_tab = t_userdata.get("symbol","").asString()[1]; + } + m_user.comment = t_userdata.get("comment","").asString(); + struct tm mtime = {0}; // set default date/time 31.12.1899 + m_user.last_activity = mktime(&mtime); + m_user.sent_last_sds = mktime(&mtime); + userdata[m_user.issi] = m_user; + if (debug >= LOGINFO) + { + cout << "tsi=" << m_user.issi << ",call=" << m_user.call << ",name=" + << m_user.name << ",location=" << m_user.location << ",comment=" + << m_user.comment << endl; + } + } + } + // define sds messages send to user when received Sds's from him due to // state changes std::string sds_useractivity; @@ -582,6 +671,12 @@ bool TetraLogic::initialize(void) processEvent("startup"); + cout << ">>> Started SvxLink with special TetraLogic extension (v" + << TETRA_LOGIC_VERSION << ")" << endl; + cout << ">>> No guarantee! Please send a bug report to\n" + << ">>> Adi/DL1HRC or use the groups.io mailing list" + << endl; + return isok; } /* TetraLogic::initialize */ @@ -748,6 +843,8 @@ void TetraLogic::sendUserInfo(void) t_userinfo["tab"] = iu->second.aprs_tab; t_userinfo["sym"] = iu->second.aprs_sym; t_userinfo["comment"] = iu->second.comment; + t_userinfo["location"] = iu->second.location; + t_userinfo["last_activity"] = 0; event.append(t_userinfo); } publishInfo("TetraUsers:info", event); @@ -830,14 +927,14 @@ void TetraLogic::handlePeiAnswer(std::string m_message) case SDS: handleSds(m_message); break; - + case ACK_SDS: break; case TEXT_SDS: handleSdsMsg(m_message); break; - + case SIMPLE_TEXT_SDS: case STATE_SDS: handleSdsMsg(m_message); @@ -854,7 +951,7 @@ void TetraLogic::handlePeiAnswer(std::string m_message) // sds state send be MS handleCmgs(m_message); break; - + case TX_DEMAND: break; @@ -873,11 +970,11 @@ void TetraLogic::handlePeiAnswer(std::string m_message) case CTGS: handleCtgs(m_message); break; - + case CTDGR: cout << handleCtdgr(m_message); break; - + case CLVL: handleClvl(m_message); break; @@ -909,7 +1006,7 @@ void TetraLogic::initGroupCall(int gc_gssi) cmd = "ATD"; cmd += to_string(gc_gssi); sendPei(cmd); - + stringstream ss; ss << "init_group_call " << to_string(gc_gssi); processEvent(ss.str()); @@ -928,19 +1025,22 @@ TETRA Incoming Call Notification +CTICN Example: MCC| MNC| ISSI | MCC| MNC| GSSI | +CTICN: 1,0,0,5,09011638300023404,1,1,0,1,1,5,09011638300000001,0 + OR ISSI GSSI + +CTICN: 1,0,0,5,23404,1,1,0,1,1,5,1000,0 */ void TetraLogic::handleCallBegin(std::string message) { - - if (message.length() < 65) + // +CTICN: 1, 0, 0, 4, 1002, 1, 1, 0, 1, 1, 0, 1000, 1 + std::string reg = "\\+CTICN: [0-9],[0-9],[0-9],[0-9],[0-9]{1,17},[0-9],[0-9],[0-9],[0-9],[0-9],[0-9],[0-9]{1,17},[0-9]"; + + if (!rmatch(message, reg)) { if (debug >= LOGWARN) { - cout << "*** No valid +CTICN response, message to short" << endl; + cout << "*** Wrong +CTICN response (wrong format)" << endl; } return; - } - + } squelchOpen(true); // open the Squelch Callinfo t_ci; @@ -956,9 +1056,21 @@ void TetraLogic::handleCallBegin(std::string message) t_ci.origin_cpit = getNextVal(h); std::string o_tsi = getNextStr(h); - t_ci.o_mcc = atoi(o_tsi.substr(0,4).c_str()); - t_ci.o_mnc = atoi(o_tsi.substr(4,5).c_str()); - t_ci.o_issi = atoi(o_tsi.substr(9,8).c_str()); + + if (o_tsi.length() < 9) + { + t_ci.o_issi = atoi(o_tsi.c_str()); + string t = mcc; + t += mnc; + t += getISSI(o_tsi); + o_tsi = t; + t_ci.o_mnc = dmnc; + t_ci.o_mcc = dmcc; + } + else + { + splitTsi(o_tsi, t_ci.o_mcc, t_ci.o_mnc, t_ci.o_issi); + } t_ci.hook = getNextVal(h); t_ci.simplex = getNextVal(h); @@ -968,13 +1080,26 @@ void TetraLogic::handleCallBegin(std::string message) t_ci.dest_cpit = getNextVal(h); std::string d_tsi = getNextStr(h); - t_ci.d_mcc = atoi(d_tsi.substr(0,4).c_str()); - t_ci.d_mnc = atoi(d_tsi.substr(4,5).c_str()); - t_ci.d_issi = atoi(d_tsi.substr(9,8).c_str()); + + if (d_tsi.length() < 9) + { + t_ci.d_issi = atoi(d_tsi.c_str()); + string t = mcc; + t += mnc; + t += getISSI(d_tsi); + d_tsi = t; + t_ci.d_mnc = dmnc; + t_ci.d_mcc = dmcc; + } + else + { + splitTsi(d_tsi, t_ci.d_mcc, t_ci.d_mnc, t_ci.d_issi); + } + t_ci.prio = atoi(h.c_str()); // store call specific data into a Callinfo struct - callinfo[t_ci.o_issi] = t_ci; + callinfo[t_ci.instance] = t_ci; // check if the user is stored? no -> default std::map::iterator iu = userdata.find(o_tsi); @@ -1007,29 +1132,28 @@ void TetraLogic::handleCallBegin(std::string message) Qso.tsi = o_tsi; Qso.start = time(NULL); - // prepare array for tetra users to be send over the network - Json::Value event(Json::arrayValue); + // prepare event for tetra users to be send over the network Json::Value qsoinfo(Json::objectValue); - qsoinfo["active"] = true; + qsoinfo["qso_active"] = true; qsoinfo["gateway"] = callsign(); qsoinfo["dest_mcc"] = t_ci.d_mcc; qsoinfo["dest_mnc"] = t_ci.d_mnc; qsoinfo["dest_issi"] = t_ci.d_issi; + qsoinfo["aimode"] = t_ci.aistatus; + qsoinfo["cci"] = t_ci.instance; + uint32_t ti = time(NULL); + qsoinfo["last_activity"] = ti; std::list::iterator it; it = find(Qso.members.begin(), Qso.members.end(), iu->second.call); if (it == Qso.members.end()) { - qsoinfo["call"] = iu->second.call; - qsoinfo["tsi"] = Qso.tsi; - stringstream la; - la << userdata[o_tsi].last_activity; - qsoinfo["last_activity"] = la.str(); - event.append(qsoinfo); Qso.members.push_back(iu->second.call); } - publishInfo("QsoInfo:state", event); + + qsoinfo["qso_members"] = joinList(Qso.members); + publishInfo("QsoInfo:state", qsoinfo); // end of publish messages // callup tcl event @@ -1059,15 +1183,14 @@ void TetraLogic::handleCallBegin(std::string message) void TetraLogic::handleSds(std::string sds) { sds.erase(0,9); // remove "+CTSDSR: " - + // store header of sds for further handling - //pSDS.sdstype = getNextVal(sds); // type of SDS (12) - pSDS.aiservice = getNextVal(sds); // type of SDS (TypeOfService 0-12) + pSDS.aiservice = getNextVal(sds); // type of SDS (TypeOfService 0-12) pSDS.fromtsi = getTSI(getNextStr(sds)); // sender Tsi (23404) - getNextVal(sds); // (0) - pSDS.totsi = getNextVal(sds); // destination Issi - getNextVal(sds); // (0) - getNextVal(sds); // Sds length (112) + getNextVal(sds); // (0) + pSDS.totsi = getTSI(getNextStr(sds)); // destination Issi + getNextVal(sds); // (0) + getNextVal(sds); // Sds length (112) pSDS.last_activity = time(NULL); } /* TetraLogic::handleSds */ @@ -1221,12 +1344,13 @@ void TetraLogic::handleSdsMsg(std::string sds) return; } - stringstream la; - la << userdata[t_sds.tsi].last_activity; - sdsinfo["last_activity"] = la.str(); - sdsinfo["tsi"] = t_sds.tsi; + uint32_t ti = time(NULL); + sdsinfo["last_activity"] = ti; + sdsinfo["sendertsi"] = t_sds.tsi; sdsinfo["type"] = m_sdstype; - sdsinfo["call"] = userdata[t_sds.tsi].call; + sdsinfo["from"] = userdata[t_sds.tsi].call; + sdsinfo["to"] = userdata[pSDS.totsi].call; + sdsinfo["receivertsi"] = pSDS.totsi; sdsinfo["gateway"] = callsign(); event.append(sdsinfo); publishInfo("Sds:info", event); @@ -1263,11 +1387,12 @@ std::string TetraLogic::handleCtgs(std::string m_message) } /* TetraLogic::handleCtgs */ -// 6.14.10 TETRA DMO visible gateways/repeaters -// +CTDGR: [], [], [], -// [] -// TETRA DMO visible gateways/repeaters +CTDGR -// +CTDGR: 2,1001,90116383,0 +/* 6.14.10 TETRA DMO visible gateways/repeaters + * +CTDGR: [], [], [], + * [] + * TETRA DMO visible gateways/repeaters +CTDGR + * +CTDGR: 2,1001,90116383,0 + */ std::string TetraLogic::handleCtdgr(std::string m_message) { m_message.erase(0,8); @@ -1479,9 +1604,10 @@ void TetraLogic::handleStateSds(unsigned int isds) } /* TetraLogic::handleStateSds */ -// 6.15.11 Down Transmission Ceased +CDTXC -// +CDTXC: , -// +CDTXC: 1,0 +/* 6.15.11 Down Transmission Ceased +CDTXC + * +CDTXC: , + * +CDTXC: 1,0 + */ void TetraLogic::handleTransmissionEnd(std::string message) { squelchOpen(false); // close Squelch @@ -1500,7 +1626,8 @@ void TetraLogic::handleCallReleased(std::string message) Qso.stop = time(NULL); stringstream ss; - getNextStr(message); + message.erase(0,7); + int cci = getNextVal(message); if (tetra_modem_sql->isOpen()) { @@ -1513,32 +1640,28 @@ void TetraLogic::handleCallReleased(std::string message) } processEvent(ss.str()); - // prepare array for tetra users to be send over the network - Json::Value event(Json::arrayValue); - std::list::iterator it; - // send call/qso end to aprs network std::string m_aprsmesg = aprspath; if (!Qso.members.empty()) { m_aprsmesg += ">Qso ended ("; - for (const auto &it : Qso.members) - { - m_aprsmesg += it; - m_aprsmesg += ","; - } - m_aprsmesg.pop_back(); + m_aprsmesg += joinList(Qso.members); m_aprsmesg += ")"; - // send userinfo to SvxReflector when - for (it=Qso.members.begin(); it!=Qso.members.end(); it++) - { - Json::Value qsoinfo(Json::objectValue); - qsoinfo["qso_active"] = false; - qsoinfo["members"] = *it; - event.append(qsoinfo); - } - publishInfo("QsoInfo:state", event); + // prepare event for tetra users to be send over the network + Json::Value qsoinfo(Json::objectValue); + + uint32_t ti = time(NULL); + qsoinfo["last_activity"] = ti; + qsoinfo["qso_active"] = false; + qsoinfo["qso_members"] = joinList(Qso.members); + qsoinfo["gateway"] = callsign(); + qsoinfo["cci"] = cci; + qsoinfo["aimode"] = callinfo[cci].aistatus; + qsoinfo["dest_mcc"] = callinfo[cci].d_mcc; + qsoinfo["dest_mnc"] = callinfo[cci].d_mnc; + qsoinfo["dest_issi"] = callinfo[cci].d_issi; + publishInfo("QsoInfo:state", qsoinfo); } else { @@ -1555,6 +1678,18 @@ void TetraLogic::handleCallReleased(std::string message) } /* TetraLogic::handleCallReleased */ +std::string TetraLogic::joinList(std::list members) +{ + std::string qi; + for (const auto &it : members) + { + qi += it; + qi += ","; + } + return qi.substr(0,qi.length()-1); +} /* TetraLogic::joinList */ + + void TetraLogic::sendPei(std::string cmd) { // a sdsmsg must end with 0x1a @@ -1639,33 +1774,53 @@ void TetraLogic::cfmTxtSdsReceived(std::string message, std::string tsi) void TetraLogic::handleCnumf(std::string m_message) { - size_t f = m_message.find("+CNUMF: "); if (f != string::npos) { m_message.erase(0,8); } - // e.g. +CNUMF: 6,09011638300023401 + // e.g. +CNUMF: 6,09011638300023401 + int t_mnc, t_mcc, t_issi; short m_numtype = getNextVal(m_message); + if (debug >= LOGINFO) cout << " is " << m_numtype << " (" << NumType[m_numtype] << ")" << endl; - if (m_numtype == 6) + + if (m_numtype == 6 || m_numtype == 0) { - if (mcc != m_message.substr(0,4) && debug >= LOGWARN) + // get the tsi and split it into mcc,mnc,issi + splitTsi(m_message, t_mcc, t_mnc, t_issi); + + // check if the configured MCC fits to MCC in MS + if (t_mcc != atoi(mcc.c_str())) { - cout << "*** ERROR: wrong MCC in MS, will not work! " << mcc << "!=" - << m_message.substr(0,4) << endl; + if (debug >= LOGWARN) + { + cout << "*** ERROR: wrong MCC in MS, will not work! " + << mcc << "!=" << t_mcc << endl; + } } - if (mnc != m_message.substr(4,5) && debug >= LOGWARN) + + // check if the configured MNC fits to MNC in MS + if (t_mnc != atoi(mnc.c_str())) { - cout << "*** ERROR: wrong MNC in MS, will not work! " << mnc << "!=" - << m_message.substr(4,5) << endl; + if (debug >= LOGWARN) + { + cout << "*** ERROR: wrong MNC in MS, will not work! " + << mnc << "!=" << t_mnc << endl; + } } - if (atoi(issi.c_str()) != atoi(m_message.substr(9,8).c_str()) && debug >= LOGWARN) + dmcc = t_mcc; + dmnc = t_mnc; + + if (atoi(issi.c_str()) != t_issi) { - cout << "*** ERROR: wrong ISSI in MS, will not work! " << issi <<"!=" - << atoi(m_message.substr(9,8).c_str()) << endl; + if (debug >= LOGWARN) + { + cout << "*** ERROR: wrong ISSI in MS, will not work! " + << issi <<"!=" << t_issi << endl; + } } } @@ -1889,7 +2044,10 @@ void TetraLogic::onPublishStateEvent(const string &event_name, const string &msg if (event_name == "TetraUsers:info") { - if (debug >= LOGINFO) cout << "Download userdata from Reflector (TetraUsers:info):" << endl; + if (debug >= LOGINFO) + { + cout << "Download userdata from Reflector (TetraUsers:info):" << endl; + } for (Json::Value::ArrayIndex i = 0; i != user_arr.size(); i++) { User m_user; @@ -1897,14 +2055,18 @@ void TetraLogic::onPublishStateEvent(const string &event_name, const string &msg m_user.issi = t_userdata.get("tsi", "").asString(); m_user.name = t_userdata.get("name","").asString(); m_user.call = t_userdata.get("call","").asString(); + m_user.location = t_userdata.get("location","").asString(); m_user.aprs_sym = static_cast(t_userdata.get("sym","").asInt()); m_user.aprs_tab = static_cast(t_userdata.get("tab","").asInt()); m_user.comment = t_userdata.get("comment","").asString(); + m_user.last_activity = t_userdata.get("last_activity","").asUInt(); + userdata[m_user.issi] = m_user; if (debug >= LOGINFO) { cout << "tsi:" << m_user.issi << ",call=" << m_user.call << ",name=" - << m_user.name << ",comment=" << m_user.comment << endl; + << m_user.name << ",location=" << m_user.location + << ", comment=" << m_user.comment << endl; } } } @@ -2034,6 +2196,10 @@ void TetraLogic::sendWelcomeSds(string tsi, short r4s) } /* TetraLogic::sendWelcomeSds */ +/* + * @param: a message, e.g. +CTCC: 1,1,1,0,0,1,1 + * @return: the current caller identifier + */ int TetraLogic::handleCci(std::string m_message) { size_t f = m_message.find("+CTCC: "); diff --git a/src/svxlink/svxlink/TetraLogic.h b/src/svxlink/svxlink/TetraLogic.h index bb435ac5..26cc5030 100644 --- a/src/svxlink/svxlink/TetraLogic.h +++ b/src/svxlink/svxlink/TetraLogic.h @@ -269,6 +269,7 @@ class TetraLogic : public Logic std::string call; std::string name; std::string comment; + std::string location; float lat; float lon; std::string state; @@ -365,7 +366,10 @@ class TetraLogic : public Logic bool inTransmission; bool cmgs_received; bool share_userinfo; + Json::Value m_user_info; int current_cci; + int dmnc; + int dmcc; void initPei(void); void onCharactersReceived(char *buf, int count); @@ -409,6 +413,7 @@ class TetraLogic : public Logic void onDapnetMessage(std::string, std::string message); void sendAprs(std::string call, std::string aprsmessage); bool checkIfDapmessage(std::string message); + std::string joinList(std::list members); }; /* class TetraLogic */ diff --git a/src/svxlink/svxlink/svxlink.conf.in b/src/svxlink/svxlink/svxlink.conf.in index a71acca2..8ec162e9 100644 --- a/src/svxlink/svxlink/svxlink.conf.in +++ b/src/svxlink/svxlink/svxlink.conf.in @@ -137,12 +137,13 @@ PROXIMITY_WARNING=3.1 TIME_BETWEEN_SDS=3600 INFO_SDS=Welcome new user TETRA_USERS=Tetra_Users +TETRA_USER_INFOFILE=@SVX_SYSCONF_INSTALL_DIR@/tetra_users.json TETRA_STATUS=Tetra_Status SDS_ON_USERACTIVITY=SdsOnUserActivity SDS_TO_OTHERS_ON_ACTIVITY=DMO_ON,DMO_OFF,PROXIMITY SDS_TO_COMMAND=SdsToCommand END_CMD=ATH -SHARE_USERINFO=1 +#SHARE_USERINFO=0 DAPNET_SERVER=dapnet.afu.rwth-aachen.de DAPNET_PORT=43434 DAPNET_CALLSIGN=MYCALL @@ -165,6 +166,8 @@ DAPNET_RUBRIC_REGISTRATION=RicRegistration 1234=1024,1051 23451=1028,1051 +# please move the next section to +# @SVX_SYSCONF_INSTALL_DIR@/tetra_users.json [Tetra_Users] # ISSI = Call,Name,comment 09011638300023401=DM0SVX,DMO-Repeater,\\r,Halle/Saale