diff options
Diffstat (limited to 'src')
| -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 | 
6 files changed, 611 insertions, 186 deletions
| 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) { | 
