diff options
-rw-r--r-- | include/mts/MTS_IO_CellularRadio.h | 17 | ||||
-rw-r--r-- | include/mts/MTS_IO_EG95Radio.h | 44 | ||||
-rw-r--r-- | include/mts/MTS_IO_QuectelRadio.h | 55 | ||||
-rw-r--r-- | include/mts/MTS_IO_TelitRadio.h | 11 | ||||
-rw-r--r-- | src/MTS_IO_CellularRadio.cpp | 145 | ||||
-rw-r--r-- | src/MTS_IO_CellularRadioFactory.cpp | 4 | ||||
-rw-r--r-- | src/MTS_IO_EG95Radio.cpp | 44 | ||||
-rw-r--r-- | src/MTS_IO_ICellularRadio.cpp | 24 | ||||
-rw-r--r-- | src/MTS_IO_QuectelRadio.cpp | 495 | ||||
-rw-r--r-- | src/MTS_IO_TelitRadio.cpp | 85 |
10 files changed, 713 insertions, 211 deletions
diff --git a/include/mts/MTS_IO_CellularRadio.h b/include/mts/MTS_IO_CellularRadio.h index acb235b..899ceeb 100644 --- a/include/mts/MTS_IO_CellularRadio.h +++ b/include/mts/MTS_IO_CellularRadio.h @@ -66,15 +66,12 @@ namespace MTS { CODE getMsid(std::string& sMsid) override; CODE getType(std::string& sType) override; CODE getCarrier(std::string& sCarrier) override; - CODE getNetwork(std::string& sNetwork) override; CODE getTower(std::string& sTower) override; CODE getTime(std::string& sDate, std::string& sTime, std::string& sTimeZone) override; CODE getRoaming(bool& bRoaming) override; CODE getSignalStrength(int32_t& iRssi) override; CODE getModemLocation(std::string& sLocation) override; - CODE convertSignalStrengthTodBm(const int32_t& iRssi, int32_t& dBm) override; - CODE convertdBmToSignalStrength(const int32_t& dBm, int32_t& iRssi) override; CODE getRegistration(REGISTRATION& eRegistration) override; CODE convertRegistrationToString(REGISTRATION eRegistration, std::string& sRegistration) override; @@ -120,12 +117,18 @@ namespace MTS { CellularRadio(const std::string& sName, const std::string& sRadioPort); - virtual bool getCarrierFromFirmware(const std::string& sFirmware, std::string& sCarrier); - virtual bool getHardwareVersionFromFirmware(const std::string& sFirmware, std::string& sHardware); + virtual bool getCarrierFromFirmware(const std::string& sFirmware, std::string& sCarrier) = 0; + virtual bool getHardwareVersionFromFirmware(const std::string& sFirmware, std::string& sHardware) = 0; + virtual void getCommonNetworkStats(Json::Value& jData); + void initMipProfile(Json::Value& jData); bool splitAndAssign(const std::string& sLine, const std::string& sKey, Json::Value& jParent, const std::string& sJsonKey, Json::ValueType eType = Json::ValueType::stringValue); + virtual std::string queryLteLac(); + virtual std::string queryCGREGstring(); + virtual void setCGREG(std::string value); + class RadioBandMap : public MTS::NonCopyable { public: RadioBandMap() @@ -167,10 +170,6 @@ namespace MTS { bool m_bEchoEnabled; bool m_bEnableEchoOnClose; - - std::string queryLteLac(); - std::string queryCGREGstring(); - void setCGREG(std::string value); }; } } diff --git a/include/mts/MTS_IO_EG95Radio.h b/include/mts/MTS_IO_EG95Radio.h index 206d15d..f3021cc 100644 --- a/include/mts/MTS_IO_EG95Radio.h +++ b/include/mts/MTS_IO_EG95Radio.h @@ -1,17 +1,47 @@ -#ifndef MTS_IO_EG95RADIO_H -#define MTS_IO_EG95RADIO_H +/* + * Copyright (C) 2019 by Multi-Tech Systems + * + * This file is part of libmts-io. + * + * libmts-io is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * libmts-io 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with libmts-io. If not, see <http://www.gnu.org/licenses/>. + * + */ + + +#ifndef MTS_IO_EG95RADIO_H_ +#define MTS_IO_EG95RADIO_H_ #include "mts/MTS_IO_QuectelRadio.h" namespace MTS { namespace IO { - class EG95Radio : public QuectelRadio - { - public: - static const std::string MODEL_NAME; + + class EG95Radio : public QuectelRadio { + + public: + static const std::string MODEL_NAME; + + EG95Radio(const std::string& sPort); + virtual ~EG95Radio(); + CODE setRxDiversity(const Json::Value& jArgs); + + protected: + + private: }; } } -#endif +#endif /* MTS_IO_EG95RADIO_H_ */ diff --git a/include/mts/MTS_IO_QuectelRadio.h b/include/mts/MTS_IO_QuectelRadio.h index 55eac60..b486323 100644 --- a/include/mts/MTS_IO_QuectelRadio.h +++ b/include/mts/MTS_IO_QuectelRadio.h @@ -1,15 +1,60 @@ -#ifndef MTS_IO_QUECTELRADIO_H -#define MTS_IO_QUECTELRADIO_H +/* + * Copyright (C) 2019 by Multi-Tech Systems + * + * This file is part of libmts-io. + * + * libmts-io is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * libmts-io 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with libmts-io. If not, see <http://www.gnu.org/licenses/>. + * + */ + +#ifndef MTS_IO_QUECTELRADIO_H_ +#define MTS_IO_QUECTELRADIO_H_ #include <mts/MTS_IO_CellularRadio.h> namespace MTS { namespace IO { - class QuectelRadio : public CellularRadio - { + + class QuectelRadio : public CellularRadio { + + public: + bool resetRadio(uint32_t iTimeoutMillis = 5000) override; + + CODE getModel(std::string& sModel) override; + CODE getIccid(std::string& sIccid) override; + CODE getService(std::string& sService) override; + CODE getNetwork(std::string& sNetwork) override; + CODE getNetworkStatus(Json::Value& jData) override; + + CODE convertSignalStrengthTodBm(const int32_t& iRssi, int32_t& dBm) override; + CODE convertdBmToSignalStrength(const int32_t& dBm, int32_t& iRssi) override; + + CODE setMdn(const Json::Value& jArgs) override; + + protected: + QuectelRadio(const std::string& sName, const std::string& sRadioPort); + + bool getCarrierFromFirmware(const std::string& sFirmware, std::string& sCarrier) override; + bool getHardwareVersionFromFirmware(const std::string& sFirmware, std::string& sHardware) override; + + virtual CODE getServiceDomain(SERVICEDOMAIN& sd); + virtual CODE convertToActiveBand(const std::string& sQuectelBand, ACTIVEBAND& band); + + private: }; } } -#endif +#endif /* MTS_IO_QUECTELRADIO_H_ */ diff --git a/include/mts/MTS_IO_TelitRadio.h b/include/mts/MTS_IO_TelitRadio.h index 0c95f98..70ff8a9 100644 --- a/include/mts/MTS_IO_TelitRadio.h +++ b/include/mts/MTS_IO_TelitRadio.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015 by Multi-Tech Systems + * Copyright (C) 2019 by Multi-Tech Systems * * This file is part of libmts-io. * @@ -32,7 +32,12 @@ namespace MTS { CODE getModel(std::string& sModel) override; CODE getIccid(std::string& sIccid) override; CODE getService(std::string& sService) override; + CODE getNetwork(std::string& sNetwork) override; CODE getNetworkStatus(Json::Value& jData) override; + + CODE convertSignalStrengthTodBm(const int32_t& iRssi, int32_t& dBm) override; + CODE convertdBmToSignalStrength(const int32_t& dBm, int32_t& iRssi) override; + CODE setMdn(const Json::Value& jArgs) override; protected: @@ -42,9 +47,7 @@ namespace MTS { bool getHardwareVersionFromFirmware(const std::string& sFirmware, std::string& sHardware) override; private: - std::string queryLteLac(); - std::string queryCGREGstring(); - void setCGREG(std::string value); + }; } } diff --git a/src/MTS_IO_CellularRadio.cpp b/src/MTS_IO_CellularRadio.cpp index 20cb93a..dbb64a5 100644 --- a/src/MTS_IO_CellularRadio.cpp +++ b/src/MTS_IO_CellularRadio.cpp @@ -488,21 +488,6 @@ CellularRadio::CODE CellularRadio::getRoaming(bool& bRoaming) { return FAILURE; } -CellularRadio::CODE CellularRadio::getNetwork(std::string& sNetwork) { - Json::Value jData; - - printTrace("%s| Get Network", m_sName.c_str()); - sNetwork = ICellularRadio::VALUE_NOT_SUPPORTED; - - if(getNetworkStatus(jData) == SUCCESS) { - if(jData.isMember(ICellularRadio::KEY_NETWORK)) { - sNetwork = jData[ICellularRadio::KEY_NETWORK].asString(); - return SUCCESS; - } - } - return FAILURE; -} - CellularRadio::CODE CellularRadio::getSignalStrength(int32_t& rssi) { printTrace("%s| Get Signal Strength", m_sName.c_str()); std::string sCmd("AT+CSQ"); @@ -531,47 +516,6 @@ CellularRadio::CODE CellularRadio::getModemLocation(std::string&) { return FAILURE; } -CellularRadio::CODE CellularRadio::convertSignalStrengthTodBm(const int32_t& iRssi, int32_t& iDbm) { - - //Telit Conversion - if(iRssi < 0 || iRssi == 99) { - return FAILURE; - } - - if(iRssi == 0) { - iDbm = -113; - } else if(iRssi == 1) { - iDbm = -111; - } else if(iRssi <= 30) { - //28 steps between 2 and 30 - //54 dbm between 53 and 109 - float stepSize = 54.0 / 28.0; - iDbm = -109 + (int)(stepSize * (iRssi-2)); - } else { - iDbm = -51; - } - - return SUCCESS; -} - -CellularRadio::CODE CellularRadio::convertdBmToSignalStrength(const int32_t& iDBm, int32_t& iRssi) { - //Telit Conversion - if(iDBm <= -113) { - iRssi = 0; - } else if(iDBm <= -111) { - iRssi = 1; - } else if(iDBm <= -53) { - //54 dbm between -109 and -53 - //28 steps between 2 and 30 - float stepSize = 28.0/54.0; - iRssi = ((iDBm + 109)*stepSize) + 2; - } else { - iRssi = 31; - } - - return SUCCESS; -} - CellularRadio::CODE CellularRadio::getEcho(bool& bEnabled) { printTrace("%s| Echo Test", m_sName.c_str()); std::string sResult = sendCommand("AT"); @@ -685,7 +629,7 @@ CellularRadio::CODE CellularRadio::getStaticInformation(Json::Value& jData) { <ABND> - */ -// Get the LAC for the LTE radio that's not in the #RFSTS response +// Get the LAC for the LTE radio that's not in the #RFSTS or +QENG response std::string CellularRadio::queryLteLac() { std::string CGREGstring; std::string originalCGREG; @@ -705,7 +649,7 @@ std::string CellularRadio::queryLteLac() { if (CGREGstring == ICellularRadio::RSP_ERROR) { result = ICellularRadio::VALUE_UNKNOWN; } else { - size_t start = CGREGstring.find(":") + 1; //Position right after "#RFSTS:" + size_t start = CGREGstring.find(":") + 1; //Position right after "+CGREG:" std::vector<std::string> vParts = MTS::Text::split(MTS::Text::trim(CGREGstring.substr(start)), ","); if(vParts.size() < 3) { result = ICellularRadio::VALUE_UNAVAILABLE; @@ -723,7 +667,7 @@ void CellularRadio::setCGREG(std::string value) { std::string sCmd("AT+CGREG=" + value); std::string cmdResult(sendCommand(sCmd)); if (cmdResult.find("OK") == std::string::npos) { - printDebug("%s| AT#CGREG=%s returned unexpected response: [%s][%s]", m_sName.c_str(), value.c_str(), sCmd.c_str(), cmdResult.c_str()); + printDebug("%s| AT+CGREG=%s returned unexpected response: [%s][%s]", m_sName.c_str(), value.c_str(), sCmd.c_str(), cmdResult.c_str()); } } @@ -731,7 +675,7 @@ std::string CellularRadio::queryCGREGstring() { std::string sCmd("AT+CGREG?"); std::string cmdResult(sendCommand(sCmd)); if (cmdResult.find("+CGREG:") == std::string::npos) { - printDebug("%s| AT#CGREG? returned unexpected response: [%s][%s]", m_sName.c_str(), sCmd.c_str(), cmdResult.c_str()); + printDebug("%s| AT+CGREG? returned unexpected response: [%s][%s]", m_sName.c_str(), sCmd.c_str(), cmdResult.c_str()); return ICellularRadio::RSP_ERROR; } return cmdResult; @@ -1006,87 +950,6 @@ bool CellularRadio::splitAndAssign(const std::string& sLine, const std::string& return true; } -bool CellularRadio::getCarrierFromFirmware(const std::string& sFirmware, std::string& sCarrier) { - // Telit Radios - // H.ab.zyx => 3 Main Components - // "H" = Hardware -> 15 = DE910 family, 18 = CE910 family, 12 = HE910 family - // "a" = Hardware version - // "b" = Software Major Version - // "z" = is the product type, i.e. DUAL or SC - // "y" = is the carrier variant - // "x" = is the firmware version - // Telit will do their best to keep the carrier variant as "0" for Sprint, "1" for Aeris, "2" for Verizon, and "3" for U.S. Cellular. - - const uint32_t CARRIER_INDEX = 1; //y in [zyx] - - bool bResult = false; - std::vector<std::string> vParts = MTS::Text::split(sFirmware, '.'); - - if(vParts.size() == 3) { - //CDMA firmware version notation - if(vParts[0] == "15" || vParts[0] == "18") { - //DE910 or CE910 -> Good good - std::string sID = vParts[2]; - if(sID.size() == 3) { - char cId = sID[CARRIER_INDEX]; - - //Good good - if(cId == '0') { - sCarrier = ICellularRadio::VALUE_CARRIER_SPRINT; - bResult = true; - } else - if(cId == '1') { - sCarrier = ICellularRadio::VALUE_CARRIER_AERIS; - bResult = true; - } else - if(cId == '2') { - sCarrier = ICellularRadio::VALUE_CARRIER_VERIZON; - bResult = true; - } else - if(cId == '3') { - sCarrier = ICellularRadio::VALUE_CARRIER_USCELLULAR; - bResult = true; - } - } - } - } - - return bResult; -} - -bool CellularRadio::getHardwareVersionFromFirmware(const std::string& sFirmware, std::string& sHardware) { - // Telit Radios - // H.ab.zyx => 3 Main Components - // "H" = Hardware -> 15 = DE910 family, 18 = CE910 family, 12 = HE910 family - // "a" = Hardware version - // "b" = Software Major Version - // "z" = is the product type, i.e. DUAL or SC - // "y" = is the carrier variant - // "x" = is the firmware version - // Telit will do their best to keep the carrier variant as "0" for Sprint, "1" for Aeris, and "2" for Verizon. - - const uint32_t HARDWARE_INDEX = 0; //a in [ab] - - bool bResult = false; - std::vector<std::string> vParts = MTS::Text::split(sFirmware, '.'); - - if(vParts.size() == 3) { - //GSM Hardware Version - if(!(vParts[0] == "15" || vParts[0] == "18")) { - //Not DE910 or CE910 -> Good good - std::string sVersion = vParts[1]; - if(sVersion.size() == 2) { - sHardware = "1."; - sHardware += sVersion[HARDWARE_INDEX]; - bResult = true; - } - } - } - - return bResult; - -} - const char *CellularRadio::RadioBandMap::getLTEBand(const int32_t channel) { for (unsigned int ii = 0; ii < NUM_LTE_BANDS; ii++) diff --git a/src/MTS_IO_CellularRadioFactory.cpp b/src/MTS_IO_CellularRadioFactory.cpp index 8ebd8dc..4ee8756 100644 --- a/src/MTS_IO_CellularRadioFactory.cpp +++ b/src/MTS_IO_CellularRadioFactory.cpp @@ -199,7 +199,5 @@ ICellularRadio* CellularRadioFactory::createLE866A1JS(const std::string &sPort) ICellularRadio* CellularRadioFactory::createEG95Radio(const std::string& sPort) const { - // TODO: return new EG95Radio(sPort); - printError("TODO: EG95Radio"); - return new HE910DRadio(sPort); + return new EG95Radio(sPort); } diff --git a/src/MTS_IO_EG95Radio.cpp b/src/MTS_IO_EG95Radio.cpp index 6feee41..8469454 100644 --- a/src/MTS_IO_EG95Radio.cpp +++ b/src/MTS_IO_EG95Radio.cpp @@ -1,5 +1,47 @@ -#include "mts/MTS_IO_EG95Radio.h" +/* + * Copyright (C) 2019 by Multi-Tech Systems + * + * This file is part of libmts-io. + * + * libmts-io is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * libmts-io 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with libmts-io. If not, see <http://www.gnu.org/licenses/>. + * + */ + + +#include <mts/MTS_IO_EG95Radio.h> using namespace MTS::IO; const std::string EG95Radio::MODEL_NAME("EG95"); + +EG95Radio::EG95Radio(const std::string& sPort) +: QuectelRadio(MODEL_NAME, sPort) +{ + +} + +EG95Radio::~EG95Radio() { + +} + +EG95Radio::CODE EG95Radio::setRxDiversity(const Json::Value& jArgs) { + /* Command string for EG95 radios: AT+QCFG="diversity",(0-1) */ + if (jArgs["enabled"].asString() != "1" && jArgs["enabled"].asString() != "0") { + return FAILURE; + } + std::string sCmd = "AT+QCFG=\"diversity\","; + sCmd += jArgs["enabled"].asString(); + + return sendBasicCommand(sCmd); +} diff --git a/src/MTS_IO_ICellularRadio.cpp b/src/MTS_IO_ICellularRadio.cpp index 2f19f3a..7f216d0 100644 --- a/src/MTS_IO_ICellularRadio.cpp +++ b/src/MTS_IO_ICellularRadio.cpp @@ -181,6 +181,27 @@ MTS::IO::ICellularRadio::CODE MTS::IO::ICellularRadio::convertModelToMtsShortCod } else if (sModel.find("DE910") == 0) { sCode = "EV3"; eCode = SUCCESS; + } else if (sModel.find("EG95") == 0) { + if (NULL == radioObject) { + sCode = VALUE_NOT_SUPPORTED; + eCode = ERROR; + } else { + std::string sValue; + eCode = radioObject->getFirmware(sValue); + if (eCode != SUCCESS) { + sCode = VALUE_NOT_SUPPORTED; + eCode = ERROR; + } else if (sValue.find("EG95E") != std::string::npos) { + sCode = "LEU7"; + eCode = SUCCESS; + } else if (sValue.find("EG95NA") != std::string::npos) { + sCode = "LNA7"; + eCode = SUCCESS; + } else { + sCode = VALUE_NOT_SUPPORTED; + eCode = ERROR; + } + } } else { sCode = VALUE_NOT_SUPPORTED; printError("RADIO| Could not identify MTS short code from model. [%s]", sModel.c_str()); @@ -274,6 +295,9 @@ MTS::IO::ICellularRadio::CODE MTS::IO::ICellularRadio::convertModelToType(const } else if (sModel.find("DE910") == 0) { sType = VALUE_TYPE_CDMA; eCode = SUCCESS; + } else if (sModel.find("EG95") == 0) { + sType = VALUE_TYPE_LTE; + eCode = SUCCESS; } else { sType = VALUE_TYPE_GSM; eCode = ERROR; diff --git a/src/MTS_IO_QuectelRadio.cpp b/src/MTS_IO_QuectelRadio.cpp index 365bbca..af7fc7c 100644 --- a/src/MTS_IO_QuectelRadio.cpp +++ b/src/MTS_IO_QuectelRadio.cpp @@ -1 +1,496 @@ +/* + * Copyright (C) 2019 by Multi-Tech Systems + * + * This file is part of libmts-io. + * + * libmts-io is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * libmts-io 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with libmts-io. If not, see <http://www.gnu.org/licenses/>. + * + */ + #include "mts/MTS_IO_QuectelRadio.h" + +#include <mts/MTS_Logger.h> +#include <mts/MTS_Thread.h> +#include <mts/MTS_Text.h> + +using namespace MTS::IO; + +QuectelRadio::QuectelRadio(const std::string& sName, const std::string& sRadioPort) +: CellularRadio (sName, sRadioPort) +{ +} + +bool QuectelRadio::resetRadio(uint32_t iTimeoutMillis) { + printInfo("%s| Rebooting radio", getName().c_str()); + if(sendBasicCommand("AT+CFUN=1,1") == SUCCESS) { + if(iTimeoutMillis > 5000) { + MTS::Thread::sleep(5000); + iTimeoutMillis -= 5000; + } + return resetConnection(iTimeoutMillis); + } + + return false; +} + +CellularRadio::CODE QuectelRadio::getModel(std::string& sModel) { + printTrace("%s| Get Model", getName().c_str()); + //Always returns SUCCESS because the model should be m_sName + sModel = getName(); + std::string sCmd("AT+GMM"); + std::string sResult = sendCommand(sCmd); + if (sResult.find("OK") == std::string::npos) { + printWarning("%s| Unable to get model from radio. Returning [%s]", getName().c_str(), getName().c_str()); + return SUCCESS; + } else { + sModel = extractModelFromResult(sResult); + if(sModel.size() == 0) { + printWarning("%s| Unable to get model from radio. Returning [%s]", getName().c_str(), getName().c_str()); + return SUCCESS; + } + } + + printDebug("%s| Extracted [%s] from [%s] query", getName().c_str(), sModel.c_str(), sCmd.c_str()); + if(sModel != getName()) { + printWarning("%s| Model identified [%s] does not match expected [%s]. Returning [%s]", + getName().c_str(), sModel.c_str(), getName().c_str(), sModel.c_str()); + } + + return SUCCESS; +} + +CellularRadio::CODE QuectelRadio::getIccid(std::string& sIccid) { + printTrace("%s| Get ICCID", getName().c_str()); + sIccid = VALUE_NOT_SUPPORTED; + std::string sCmd("AT+QCCID"); + std::string sResult = CellularRadio::sendCommand(sCmd); + size_t end = sResult.find(RSP_OK); + if (end == std::string::npos) { + printWarning("%s| Unable to get ICCID from radio using command [%s]", getName().c_str(), sCmd.c_str()); + return FAILURE; + } + + size_t start = sResult.find("+QCCID:"); + if(start != std::string::npos) { + start += sizeof("+QCCID:"); + sIccid = MTS::Text::trim(sResult.substr(start, end-start)); + if(sIccid.size() == 0) { + printWarning("%s| Unable to get ICCID from radio using command [%s]", getName().c_str(), sCmd.c_str()); + return FAILURE; + } + } + return SUCCESS; +} + +CellularRadio::CODE QuectelRadio::getService(std::string& sService) { + printTrace("%s| Get Service", getName().c_str()); + sService = VALUE_NOT_SUPPORTED; + std::string sCmd("AT+COPS?"); + std::string sResult = CellularRadio::sendCommand(sCmd); + size_t end = sResult.find(RSP_OK); + if (end == std::string::npos) { + printWarning("%s| Unable to get Service from radio using command [%s]", getName().c_str(), sCmd.c_str()); + return FAILURE; + } + + size_t start = sResult.find(":") + 1; //Position right after "+COPS:" + std::vector<std::string> vParts = MTS::Text::split(MTS::Text::trim(sResult.substr(start, end-start)), ','); + + int32_t iAccessTechnology; + + // +COPS: <mode>[,<format>[,<oper>][,<Act>]] + if (vParts.size() < 4 || !MTS::Text::parse(iAccessTechnology, vParts[3])) { + printWarning("%s| Unable to get Service from radio using command [%s]", getName().c_str(), sCmd.c_str()); + return FAILURE; + } + + switch(iAccessTechnology) { + case 0 : sService = "GPRS" ; break; // GSM + case 2 : sService = "WCDMA" ; break; // UTRAN + case 3 : sService = "EGPRS" ; break; // GSM W/EGPRS + case 4 : sService = "HSDPA" ; break; // UTRAN W/HSDPA + case 5 : sService = "WCDMA" ; break; // UTRAN W/HSUPA + case 6 : sService = "HSDPA" ; break; // UTRAN W/HSDPA and HSUPA + case 7 : sService = "LTE" ; break; // E-UTRAN + case 100 : sService = "CDMA" ; break; // CDMA + + default: sService = VALUE_UNKNOWN; break; + } + + printDebug("%s| Service ID: [%d][%s]", getName().c_str(), iAccessTechnology, sService.c_str()); + + return SUCCESS; +} + +CellularRadio::CODE QuectelRadio::getNetwork(std::string& sNetwork) { + /* + * TODO: Refactor using MccMncTable once it'll be corrected. + * + * The proper way to determine the current network is to do that + * by MCC and MNC fetched from the `getNetworkStatus` and `AT+QENG` command. + * By using MCC and MNC from `AT+QENG` we can fetch the name of the network + * reported by a currently connected base station even if the SIM card is + * not installed or if we are currently working is a roaming mode. + * + * Until MccMncTable implementation is not fixed, we are using the name + * of a currently selected operator (AT+COPS). + */ + printTrace("%s| Get Network", getName().c_str()); + sNetwork = VALUE_NOT_SUPPORTED; + std::string sCmd("AT+COPS?"); + std::string sResult = CellularRadio::sendCommand(sCmd); + size_t end = sResult.find(RSP_OK); + + if (end == std::string::npos) { + printWarning("%s| Unable to get network name from radio using command [%s]", getName().c_str(), sCmd.c_str()); + return FAILURE; + } + + // +COPS: <mode>[, <format>, <oper>,<AcT>] + // +COPS: vParts[0],vParts[1],vParts[2],vParts[3] + size_t start = sResult.find(":") + 1; //Position right after "+COPS:" + std::vector<std::string> vParts = MTS::Text::split(MTS::Text::trim(sResult.substr(start)), ","); + + if(vParts.size() > 3) { + const std::string sValue = vParts[2]; + + // +COPS: 0,0,"CHN-UNICOM UNICOM",7 + // ^start ^end + size_t start = sValue.find("\"") + 1; + size_t end = sValue.find(" ", start); + sNetwork = sValue.substr(start, end-start); + } else { + sNetwork = ""; // Not connected to any network + } + + return SUCCESS; +} + +/* AT+QENG="servingcell" - Query the information of serving cells + + (GSM network) + +QENG:"servingscell",<state>,"GSM",<mcc>,<mnc>,<lac>,<cellid>,<bsic>,<arfcn>,<band>,<rxlev>,<txp>,<rla>,<drx>,<c1>,<c2>,<gprs>,<tch>,<ts>,<ta>,<maio>,<hsn>,<rxlevsub>,<rxlevfull>,<rxqualsub>,<rxqualfull>,<voicecodec> + + (WCDMA network) + +QENG:"servingcell",<state>,"WCDMA",<mcc>,<mnc>,<lac>,<cellid>,<uarfcn>,<psc>,<rac>,<rscp>,<ecio>,<phych>,<sf>,<slot>,<speech_code>,<comMod> + + (LTE Network) + +QENG:"servingcell",<state>,"LTE",<is_tdd>,<mcc>,<mnc>,<cellid>,<pcid>,<earfcn>,<freq_band_ind>,<ul_bandwidth>,<dl_bandwidth>,<tac>,<rsrp>,<rsrq>,<rssi>,<sinr>,<srxlev> + + The following modes are NOT currently handled: + - TD-SCDMA mode; + - CDMA mode; + - HDR mode; + - SRLTE mode. + + In the case of TD-SCDMA mode: + +QENG:"servingscell",<state>,"TDSCDMA",<mcc>,<mnc>,<lac>,<cellid>,<pfreq>,<rssi>,<rscp>,<ecio> + + In the case of CDMA mode or CDMA+HDR mode: + +QENG:"servingscell",<state>,"CDMA",<mcc>,<mnc>,<lac>,<cellid>,<bcch>,<rxpwr>,<ecio>,<txpwr> + [+QENG:"servingscell",<state>,"HDR",<mcc>,<mnc>,<lac>,<cellid>,<bcch>,<rxpwr>,<ecio>,<txpwr>] + + In the case of SRLTE mode: + +QENG:"servingscell",<state>,"CDMA",<mcc>,<mnc>,<lac>,<cellid>,<bcch>,<rxpwr>,<ecio>,<txpwr> + +QENG:"servingcell",<state>,"LTE",<is_tdd>,<mcc>,<mnc>,<cellid>,<pcid>,<earfcn>,<freq_band_ind>,<ul_bandwidth>,<dl_bandwidth>,<tac>,<rsrp>,<rsrq>,<rssi>,<sinr><srxlev> +*/ +CellularRadio::CODE QuectelRadio::getNetworkStatus(Json::Value& jData) { + int32_t iValue; + ACTIVEBAND abnd; + SERVICEDOMAIN sd; + std::string sValue; + const uint32_t GSM_NETWORK_FORMAT = 27; + const uint32_t WCDMA_NETWORK_FORMAT = 17; + const uint32_t LTE_NETWORK_FORMAT = 18; + + printTrace("%s| Get Network Status", getName().c_str()); + + //Always get common network stats because this should never fail + //This way the basic stats are always returned even if AT+QENG fails below + getCommonNetworkStats(jData); + + // IMSI is not provided by AT+QENG. Fetch it separately to keep the same interface + if (getImsi(sValue) == SUCCESS) { + jData[KEY_IMSI] = sValue; + } + + // Network Name is not explicitly provided by AT+QENG. Fetch it separately to keep the same interface + // TODO: Replace with lookup by MCC and MNC once MccMncTable is fixed. + if (getNetwork(sValue) == SUCCESS) { + jData[KEY_NETWORK] = sValue; + } + + std::string sCmd; + std::string sResult; + + sCmd = "AT+QENG=\"servingcell\""; + sResult = sendCommand(sCmd, DEFAULT_BAIL_STRINGS, 200); + if (sResult.find("+QENG: \"servingcell\"") == std::string::npos) { + printDebug("%s| Network Status command returned unexpected response: [%s][%s]", getName().c_str(), sCmd.c_str(), sResult.c_str()); + printTrace("%s| Network Status:\n%s\n", getName().c_str(), jData.toStyledString().c_str()); + return SUCCESS; //return SUCCESS because getCommonNetworkStats() succeeded at top of this function + } + + size_t start = sResult.find(":") + 1; //Position right after "+QENG:" + size_t end = sResult.rfind(RSP_OK); + std::vector<std::string> vParts = MTS::Text::split(MTS::Text::trim(sResult.substr(start, end-start)), ","); + Json::Value jDebug; + Json::Value jQuectelDebug; + + if (vParts.size() < 3) { + printDebug("%s| Network Status command reponse is an unknown format: [%s][%s]", getName().c_str(), sCmd.c_str(), sResult.c_str()); + printTrace("%s| Network Status:\n%s\n", getName().c_str(), jData.toStyledString().c_str()); + return SUCCESS; //return SUCCESS because getCommonNetworkStats() succeeded at top of this function + } else { + // UE state and Access technology, Quectel-specific information + jQuectelDebug["state"] = vParts[1]; + jQuectelDebug["rat"] = vParts[2]; + } + + // +QENG:"servingscell",<state>,"GSM",<mcc>,<mnc>,<lac>,<cellid>,<bsic>,<arfcn>,<band>,<rxlev>,<txp>,<rla>,<drx>,<c1>,<c2>,<gprs>,<tch>,<ts>,<ta>,<maio>,<hsn>,<rxlevsub>,<rxlevfull>,<rxqualsub>,<rxqualfull>,<voicecodec> + // +QENG: [0], [1], [2], [3], [4], [5], [6], [7], [8], [9], [10], [11], [12], [13],[14],[15], [16], [17],[18],[19], [20], [21], [22], [23], [24], [25], [26] + if (vParts.size() == GSM_NETWORK_FORMAT ) { + //Parse as GSM Network Format + jData[KEY_MCC] = vParts[3]; + jData[KEY_MNC] = vParts[4]; + jData[KEY_LAC] = vParts[5]; + jData[KEY_CID] = vParts[6]; + jQuectelDebug["bsic"] = vParts[7]; + jData[KEY_CHANNEL] = vParts[8]; + + if (convertToActiveBand(vParts[9], abnd) == SUCCESS && convertActiveBandToString(abnd, sValue) == SUCCESS) { + jData[KEY_ABND] = sValue; + } + + jData[KEY_RSSIDBM] = vParts[10]; // Values already negative. No need to substract 111 as stated in a datasheet + jData[KEY_TXPWR] = vParts[11]; + jQuectelDebug["rla"] = vParts[12]; + jQuectelDebug["drx"] = vParts[13]; + jQuectelDebug["c1"] = vParts[14]; + jQuectelDebug["c2"] = vParts[15]; + jQuectelDebug["gprs"] = vParts[16]; + jQuectelDebug["tch"] = vParts[17]; + jQuectelDebug["ts"] = vParts[18]; + jQuectelDebug["ta"] = vParts[19]; + jQuectelDebug["maio"] = vParts[20]; + jQuectelDebug["hsn"] = vParts[21]; + jQuectelDebug["rxlevsub"] = vParts[22]; + jQuectelDebug["rxlevfull"] = vParts[23]; + jQuectelDebug["rxqualsub"] = vParts[24]; + jQuectelDebug["rxqualfull"] = vParts[25]; + jQuectelDebug["voicecodec"] = vParts[26]; + + // Service Domain is not provided by AT+QENG. Fetch it separately to keep the same interface + if (getServiceDomain(sd) == SUCCESS && convertServiceDomainToString(sd, sValue) == SUCCESS) { + jData[KEY_SD] = sValue; + } + + // The following fields can NOT be fetched for Quectel in GSM mode: RAC, MM, RR, NOM + + jData["quectelDebug"] = jQuectelDebug; + } + + // +QENG:"servingcell",<state>,"WCDMA",<mcc>,<mnc>,<lac>,<cellid>,<uarfcn>,<psc>,<rac>,<rscp>,<ecio>,<phych>,<sf>,<slot>,<speech_code>,<comMod> + // +QENG: [0], [1], [2], [3], [4], [5], [6], [7], [8], [9], [10], [11], [12],[13], [14], [15], [16] + else if((vParts.size() == WCDMA_NETWORK_FORMAT)) { + //Parse as WCDMA Network Format + jData[KEY_MCC] = vParts[3]; + jData[KEY_MNC] = vParts[4]; + jData[KEY_LAC] = vParts[5]; + jData[KEY_CID] = vParts[6]; + jData[KEY_CHANNEL] = vParts[7]; + jDebug[KEY_PSC] = vParts[8]; + jData[KEY_RAC] = vParts[9]; + jDebug[KEY_RSCP] = vParts[10]; + jDebug[KEY_ECIO] = vParts[11]; + jQuectelDebug["phych"] = vParts[12]; + jQuectelDebug["sf"] = vParts[13]; + jQuectelDebug["slot"] = vParts[14]; + jQuectelDebug["speechCode"] = vParts[15]; + jQuectelDebug["comMod"] = vParts[16]; + + // The following fields can NOT be fetched for Quectel in WCDMA mode: TXPWR, DRX, MM, RR, NOM, BLER + + // RSSI is not provided by AT+QENG in WCDMA mode. It was filled above by the getCommonNetworkStats + + // Service Domain is not provided by AT+QENG. Fetch it separately to keep the same interface + if (getServiceDomain(sd) == SUCCESS && convertServiceDomainToString(sd, sValue) == SUCCESS) { + jDebug[KEY_SD] = sValue; + } + + // BLER is not provided by AT+QENG. Set to constant + jDebug[KEY_BLER] = "000"; + + // Get the radio band given the channel (UARFCN) + RadioBandMap radioBandMap(vParts[7], CellularRadio::VALUE_TYPE_CDMA); + jData[KEY_ABND] = radioBandMap.getRadioBandName(); + + jData["quectelDebug"] = jQuectelDebug; + jData[KEY_DEBUG] = jDebug; + } + + // +QENG:"servingcell",<state>,"LTE",<is_tdd>,<mcc>,<mnc>,<cellid>,<pcid>,<earfcn>,<freq_band_ind>,<ul_bandwidth>,<dl_bandwidth>,<tac>,<rsrp>,<rsrq>,<rssi>,<sinr>,<srxlev> + // +QENG: [0], [1], [2], [3], [4], [5], [6], [7], [8], [9], [10], [11], [12], [13], [14], [15], [16], [17] + else if(vParts.size() == LTE_NETWORK_FORMAT) { + //Parse as LTE Network Format + jQuectelDebug["isTdd"] = vParts[3]; + jData[KEY_MCC] = vParts[4]; + jData[KEY_MNC] = vParts[5]; + jData[KEY_CID] = vParts[6]; + jQuectelDebug["pcid"] = vParts[7]; + jData[KEY_CHANNEL] = vParts[8]; + jQuectelDebug["freqBandInd"] = vParts[9]; + jQuectelDebug["ulBandwidth"] = vParts[10]; + jQuectelDebug["dlBandwidth"] = vParts[11]; + jData["tac"] = vParts[12]; + jDebug["rsrp"] = vParts[13]; + jDebug["rsrq"] = vParts[14]; + jData[KEY_RSSIDBM] = vParts[15]; + jQuectelDebug["sinr"] = vParts[16]; + jQuectelDebug["srxlev"] = vParts[17]; + + // Get the radio band given the channel (EARFCN) + RadioBandMap radioBandMap(vParts[8], CellularRadio::VALUE_TYPE_LTE); + jData[KEY_ABND] = radioBandMap.getRadioBandName(); + + // Service Domain is not provided by AT+QENG. Fetch it separately to keep the same interface + if (getServiceDomain(sd) == SUCCESS && convertServiceDomainToString(sd, sValue) == SUCCESS) { + jDebug[KEY_SD] = sValue; + } + + // LAC is not provided by AT+QENG in WCDMA mode. Use another command instead + jData[KEY_LAC] = queryLteLac(); + + jData["quectelDebug"] = jQuectelDebug; + jData[KEY_DEBUG] = jDebug; + } + + printTrace("%s| Network Status:\n%s\n", getName().c_str(), jData.toStyledString().c_str()); + return SUCCESS; +} + +CellularRadio::CODE QuectelRadio::convertSignalStrengthTodBm(const int32_t& iRssi, int32_t& iDbm) { + int dbmSteps, minValue, maxValue, rssiOffset; + int rawDbm; + + if(iRssi >= 0 && iRssi < 99) { + // normal scaling + dbmSteps = 2; + minValue = -113; + maxValue = -51; + rssiOffset = 0; + } else if(iRssi >= 100 && iRssi < 199) { + // TD-SCDMA scaling + dbmSteps = 1; + minValue = -116; + maxValue = -25; + rssiOffset = 100; + } else { + return FAILURE; // invalid, not known or not detectable + } + + rawDbm = minValue + ((iRssi - rssiOffset) * dbmSteps); + iDbm = std::min(maxValue, rawDbm); + + return SUCCESS; +} + +CellularRadio::CODE QuectelRadio::convertdBmToSignalStrength(const int32_t& iDBm, int32_t& iRssi) { + //Quectel Conversion FOR NORMAL SCALING + const int dbmSteps = 2; + const int minValue = -113; + const int rssiOffset = 0; + + if (iDBm < -113) { + iRssi = 0; + } else if (iDBm > -51) { + iRssi = 31; + } else { + iRssi = ((iDBm - minValue) / dbmSteps) + rssiOffset; + } + + return SUCCESS; +} + +CellularRadio::CODE QuectelRadio::setMdn(const Json::Value& jArgs) { + printTrace("%s| Set MDN", getName().c_str()); + return NOT_APPLICABLE; +} + +CellularRadio::CODE QuectelRadio::getServiceDomain(CellularRadio::SERVICEDOMAIN& sd) { + printTrace("%s| Get Service Domain", getName().c_str()); + + std::string sCmd("AT+QCFG=\"servicedomain\""); + std::string sResult = CellularRadio::sendCommand(sCmd); + size_t end = sResult.find(RSP_OK); + + if (end == std::string::npos) { + printWarning("%s| Unable to get service domain using command [%s]", getName().c_str(), sCmd.c_str()); + return FAILURE; + } + + // +QCFG: "servicedomain",<service> + size_t start = sResult.find(",") + 1; // Position right after comma + std::string sServiceDomain = MTS::Text::trim(sResult.substr(start, end-start)); + int iValue = -1; + + if (!MTS::Text::parse(iValue, sServiceDomain)) { + printWarning("%s| Failed to parse service domain from command output [%s]", getName().c_str(), sCmd.c_str()); + return FAILURE; + } + + switch (iValue) { + case 0: sd = SERVICEDOMAIN::CS_ONLY; break; + case 1: sd = SERVICEDOMAIN::PS_ONLY; break; + case 2: sd = SERVICEDOMAIN::CSPS; break; + + default: return FAILURE; // Unknown + } + + return SUCCESS; +} + +bool QuectelRadio::getCarrierFromFirmware(const std::string& sFirmware, std::string& sCarrier) { + // TODO: Implement properly if needed + // This function is used only in CdmaRadio implementation but was defined in + // the CellularRadio class before refactoring. + + // Find out if and how we can determine the operator by firmware version. + + return false; +} + +bool QuectelRadio::getHardwareVersionFromFirmware(const std::string& sFirmware, std::string& sHardware) { + // TODO: Implement properly + // Find out if and how we can extract the hardware version from firmware version on Quectel devices + + return false; +} + +CellularRadio::CODE QuectelRadio::convertToActiveBand(const std::string& sQuectelBand, CellularRadio::ACTIVEBAND& band) { + int iQuectelBand = -1; + + if (!MTS::Text::parse(iQuectelBand, sQuectelBand)) { + return FAILURE; // probably "-", other band + } + + switch (iQuectelBand) { + case 0: band = ACTIVEBAND::DCS_1800; break; + case 1: band = ACTIVEBAND::PCS_1900; break; + + default: return FAILURE; // actually, this case should never happen + } + + return SUCCESS; +} diff --git a/src/MTS_IO_TelitRadio.cpp b/src/MTS_IO_TelitRadio.cpp index a2ade7c..4fcf836 100644 --- a/src/MTS_IO_TelitRadio.cpp +++ b/src/MTS_IO_TelitRadio.cpp @@ -127,6 +127,21 @@ CellularRadio::CODE TelitRadio::getService(std::string& sService) { return SUCCESS; } +CellularRadio::CODE TelitRadio::getNetwork(std::string& sNetwork) { + Json::Value jData; + + printTrace("%s| Get Network", getName().c_str()); + sNetwork = ICellularRadio::VALUE_NOT_SUPPORTED; + + if(getNetworkStatus(jData) == SUCCESS) { + if(jData.isMember(ICellularRadio::KEY_NETWORK)) { + sNetwork = jData[ICellularRadio::KEY_NETWORK].asString(); + return SUCCESS; + } + } + return FAILURE; +} + /* AT#RFSTS - NETWORK STATUS (GSM network) @@ -381,57 +396,45 @@ CellularRadio::CODE TelitRadio::getNetworkStatus(Json::Value& jData) { return SUCCESS; } +CellularRadio::CODE TelitRadio::convertSignalStrengthTodBm(const int32_t& iRssi, int32_t& iDbm) { -// Get the LAC for the LTE radio that's not in the #RFSTS response -std::string TelitRadio::queryLteLac() { - std::string CGREGstring; - std::string originalCGREG; - std::string result; - - CGREGstring = queryCGREGstring(); - if (CGREGstring == ICellularRadio::RSP_ERROR) { - originalCGREG = "0"; - } else { - originalCGREG = CGREGstring.at(CGREGstring.find(",") - 1); //Position right before first comma ("+CGREG: 0,1") + //Telit Conversion + if(iRssi < 0 || iRssi == 99) { + return FAILURE; } - // Temporarily set CGREG=2 to get more info - setCGREG("2"); - - CGREGstring = queryCGREGstring(); - if (CGREGstring == ICellularRadio::RSP_ERROR) { - result = ICellularRadio::VALUE_UNKNOWN; + if(iRssi == 0) { + iDbm = -113; + } else if(iRssi == 1) { + iDbm = -111; + } else if(iRssi <= 30) { + //28 steps between 2 and 30 + //54 dbm between 53 and 109 + float stepSize = 54.0 / 28.0; + iDbm = -109 + (int)(stepSize * (iRssi-2)); } else { - size_t start = CGREGstring.find(":") + 1; //Position right after "#RFSTS:" - std::vector<std::string> vParts = MTS::Text::split(MTS::Text::trim(CGREGstring.substr(start)), ","); - if(vParts.size() < 3) { - result = ICellularRadio::VALUE_UNAVAILABLE; - } else { - result = MTS::Text::strip(vParts[2], '"'); - } + iDbm = -51; } - setCGREG(originalCGREG); - - return result; + return SUCCESS; } -void TelitRadio::setCGREG(std::string value) { - std::string sCmd("AT+CGREG=" + value); - std::string cmdResult(sendCommand(sCmd)); - if (cmdResult.find("OK") == std::string::npos) { - printDebug("%s| AT#CGREG=%s returned unexpected response: [%s][%s]", getName().c_str(), value.c_str(), sCmd.c_str(), cmdResult.c_str()); +CellularRadio::CODE TelitRadio::convertdBmToSignalStrength(const int32_t& iDBm, int32_t& iRssi) { + //Telit Conversion + if(iDBm <= -113) { + iRssi = 0; + } else if(iDBm <= -111) { + iRssi = 1; + } else if(iDBm <= -53) { + //54 dbm between -109 and -53 + //28 steps between 2 and 30 + float stepSize = 28.0/54.0; + iRssi = ((iDBm + 109)*stepSize) + 2; + } else { + iRssi = 31; } -} -std::string TelitRadio::queryCGREGstring() { - std::string sCmd("AT+CGREG?"); - std::string cmdResult(sendCommand(sCmd)); - if (cmdResult.find("+CGREG:") == std::string::npos) { - printDebug("%s| AT#CGREG? returned unexpected response: [%s][%s]", getName().c_str(), sCmd.c_str(), cmdResult.c_str()); - return ICellularRadio::RSP_ERROR; - } - return cmdResult; + return SUCCESS; } CellularRadio::CODE TelitRadio::setMdn(const Json::Value& jArgs) { |