diff options
| author | Yevhen Mykhno <yevhen.mykhno@globallogic.com> | 2023-01-31 13:39:31 +0200 | 
|---|---|---|
| committer | John Klug <john.klug@multitech.com> | 2023-03-13 10:52:41 -0500 | 
| commit | c2481cf0773c1550fdb9a28d1a0b407c577ac34e (patch) | |
| tree | 37849e3cb1a65a949dca6461269748413d310ed0 | |
| parent | a6f0f7e7b341d6ce29ad71ee534031e9d8e9630b (diff) | |
| download | libmts-io-c2481cf0773c1550fdb9a28d1a0b407c577ac34e.tar.gz libmts-io-c2481cf0773c1550fdb9a28d1a0b407c577ac34e.tar.bz2 libmts-io-c2481cf0773c1550fdb9a28d1a0b407c577ac34e.zip | |
[GP-1733] mPower R.6.3.X: L6G1 Support - libmts-io, radio-cmd, radio-query
Implementing CB610L radio modem support
| -rw-r--r-- | Makefile | 4 | ||||
| -rw-r--r-- | data/MccMncTable.csv | 1 | ||||
| -rw-r--r-- | include/mts/MTS_IO_CB610LRadio.h | 48 | ||||
| -rw-r--r-- | include/mts/MTS_IO_CellularRadioFactory.h | 1 | ||||
| -rw-r--r-- | include/mts/MTS_IO_SequansRadio.h | 83 | ||||
| -rw-r--r-- | src/MTS_IO_CB610LRadio.cpp | 58 | ||||
| -rw-r--r-- | src/MTS_IO_CellularRadioFactory.cpp | 10 | ||||
| -rw-r--r-- | src/MTS_IO_ICellularRadio.cpp | 8 | ||||
| -rw-r--r-- | src/MTS_IO_MccMncTable.cpp | 3 | ||||
| -rw-r--r-- | src/MTS_IO_SequansRadio.cpp | 553 | 
10 files changed, 765 insertions, 4 deletions
| @@ -38,7 +38,9 @@ OBJS += \  	src/MTS_IO_ME910Radio.o \  	src/MTS_IO_LockFile.o \  	src/MTS_IO_MccMncTable.o \ -	src/MTS_IO_SerialConnection.o +	src/MTS_IO_SerialConnection.o \ +	src/MTS_IO_SequansRadio.o \ +	src/MTS_IO_CB610LRadio.o  CC := $(CXX)  CXXFLAGS += -Wall -Werror -std=c++0x -fmessage-length=0 -fPIC -fvisibility=hidden -fvisibility-inlines-hidden diff --git a/data/MccMncTable.csv b/data/MccMncTable.csv index e79ef10..4c6f986 100644 --- a/data/MccMncTable.csv +++ b/data/MccMncTable.csv @@ -718,6 +718,7 @@  "313","130","us","United States","1","AT&T Wireless Inc.","att-first"  "313","140","us","United States","1","FirstNet","att-first"  "313","210","us","United States","1","AT&T Wireless Inc.","att" +"315","010","us","United States","1","Generic CBRS","cbrs-generic"  "316","010","us","United States","1","Sprint Spectrum",  "316","011","us","United States","1","Southern Communications Services Inc.",  "330","11","pr","Puerto Rico","1787","Puerto Rico Telephone Company Inc. (PRTC)", diff --git a/include/mts/MTS_IO_CB610LRadio.h b/include/mts/MTS_IO_CB610LRadio.h new file mode 100644 index 0000000..b802a2b --- /dev/null +++ b/include/mts/MTS_IO_CB610LRadio.h @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2023 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_CB610LRADIO_H_ +#define MTS_IO_CB610LRADIO_H_ + +#include <mts/MTS_IO_SequansRadio.h> + +namespace MTS { +    namespace IO { + +        class CB610LRadio : public SequansRadio { + +            public: +                static const std::string MODEL_NAME; + +                CB610LRadio(const std::string& sPort); +                virtual ~CB610LRadio(); + +                CODE setCellularMode(CELLULAR_MODES networks) override; +                CODE getSupportedCellularModes(CELLULAR_MODES &networks) override; + +            protected: + +            private: + +        }; +    } +} + +#endif /* MTS_IO_CB610LRADIO_H_ */
\ No newline at end of file diff --git a/include/mts/MTS_IO_CellularRadioFactory.h b/include/mts/MTS_IO_CellularRadioFactory.h index 13f3993..127be65 100644 --- a/include/mts/MTS_IO_CellularRadioFactory.h +++ b/include/mts/MTS_IO_CellularRadioFactory.h @@ -56,6 +56,7 @@ namespace MTS {                  ICellularRadio* createLE866A1JS(const std::string& sPort = ICellularRadio::DEFAULT_RADIO_PORT) const;                  ICellularRadio* createEG95Radio(const std::string& sPort = ICellularRadio::DEFAULT_RADIO_PORT) const;                  ICellularRadio* createEG25Radio(const std::string& sPort = ICellularRadio::DEFAULT_RADIO_PORT) const; +                ICellularRadio* createCB610LRadio(const std::string& sPort = ICellularRadio::DEFAULT_RADIO_PORT) const;              protected:                  typedef MTS::IO::ICellularRadio* (CellularRadioFactory::*CREATEFUNCPTR)(const std::string& sPort) const; diff --git a/include/mts/MTS_IO_SequansRadio.h b/include/mts/MTS_IO_SequansRadio.h new file mode 100644 index 0000000..11f2c91 --- /dev/null +++ b/include/mts/MTS_IO_SequansRadio.h @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2023 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_SEQUANSRADIO +#define MTS_IO_SEQUANSRADIO + +#include <mts/MTS_IO_CellularRadio.h> + +namespace MTS { +    namespace IO { + +        class SequansRadio : public CellularRadio { + +            public: +                bool resetRadio(uint32_t iTimeoutMillis = 5000) override; + +                CODE getVendorFirmware(std::string& sVendorFirmware) 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& iDBm) override; +                CODE convertdBmToSignalStrength(const int32_t& dBm, int32_t& iRssi) override; + +                CODE setMdn(const Json::Value& jArgs) override; + +                CODE setUeModeOfOperation(ICellularRadio::UE_MODES_OF_OPERATION mode) override; +                CODE getUeModeOfOperation(ICellularRadio::UE_MODES_OF_OPERATION& mode) override; + +                CODE setRxDiversity(const Json::Value& jArgs) override; + +                const std::vector<std::string> getRegistrationCommands() override; + +            protected: +                SequansRadio(const std::string& sName, const std::string& sRadioPort); + +                CODE getIsSimInserted(bool& bData) override; +                CODE getSimLockAttempts(int& iAttemptsPin, int& iAttemptsPuk) override; + +                const std::vector<std::string>& getDiagCommands(bool bIsSimReady = true) override; + +            private: +                /* +                * @brief getSimLockAttemptsByType - get the number of remaining attempts to +                *        enter the specified SIM password type +                * +                * @param sType - SIM password type, "SIM PIN", "SIM PUK", "SIM PIN2" or "SIM PUK2". +                * @param iAttempts - the number of remaining attempts for this password type. +                * @return CODE::SUCCESS when fetched successfully, +                *         CODE::NOT_APPLICABLE when the modem doesn't support this feature, +                *         CODE::NO_RESPONSE when the modem doesn't respond, +                *         CODE::ERROR when the radio returns "ERROR" (SIM card removed, etc), +                *         CODE::FAILURE otherwise (modem is inaccessible, etc). +                */ +                CODE getSimLockAttemptsByType(const std::string& sType, int& iAttempts); +                CODE getNetworkStatusSQN(Json::Value& jData, Json::Value& jDebug); +                CODE getNetworkStatusSignal(Json::Value& jData, Json::Value& jDebug); +                CODE getNetworkStatusTxPower(Json::Value& jData, Json::Value& jDebug); + +        }; +    } +} + +#endif /* MTS_IO_SEQUANSRADIO */ diff --git a/src/MTS_IO_CB610LRadio.cpp b/src/MTS_IO_CB610LRadio.cpp new file mode 100644 index 0000000..7fbd884 --- /dev/null +++ b/src/MTS_IO_CB610LRadio.cpp @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2023 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_CB610LRadio.h> + +#include <mts/MTS_Logger.h> +#include <mts/MTS_Thread.h> +#include <mts/MTS_Text.h> +#include <mts/MTS_Timer.h> + +using namespace MTS::IO; + +const std::string CB610LRadio::MODEL_NAME("CB610L"); + +CB610LRadio::CB610LRadio(const std::string& sPort) +: SequansRadio(MODEL_NAME, sPort) { + +} + +CB610LRadio::~CB610LRadio() { + +} + +ICellularRadio::CODE CB610LRadio::setCellularMode(CELLULAR_MODES networks) { +    if (networks != CELLULAR_MODE_4G) { +        printError("%s| The CB610L radios support only the 4G cellular mode", getName().c_str()); +        return FAILURE; +    } +    std::string sCmd("AT+WS46=28"); +    std::string cmdResult = sendCommand(sCmd); +    if (cmdResult.find(ICellularRadio::RSP_OK) == std::string::npos) { +        printError("%s| AT+WS46=28 returned unexpected response: [%s][%s]", getName().c_str(), sCmd.c_str(), cmdResult.c_str()); +        return FAILURE; +    } +    return SUCCESS; +} + +ICellularRadio::CODE CB610LRadio::getSupportedCellularModes(CELLULAR_MODES &networks) { +    networks = CELLULAR_MODE_4G; +    return SUCCESS; +}
\ No newline at end of file diff --git a/src/MTS_IO_CellularRadioFactory.cpp b/src/MTS_IO_CellularRadioFactory.cpp index 1a2cee2..f545599 100644 --- a/src/MTS_IO_CellularRadioFactory.cpp +++ b/src/MTS_IO_CellularRadioFactory.cpp @@ -38,6 +38,7 @@  #include <mts/MTS_IO_DE910Radio.h>  #include "mts/MTS_IO_EG95Radio.h"  #include "mts/MTS_IO_EG25Radio.h" +#include "mts/MTS_IO_CB610LRadio.h"  #include <mts/MTS_Logger.h>  using namespace MTS::IO; @@ -62,6 +63,7 @@ CellularRadioFactory::CellularRadioFactory() {      m_mCreationMap[LE866A1JSRadio::MODEL_NAME]    = &CellularRadioFactory::createLE866A1JS;      m_mCreationMap[EG95Radio::MODEL_NAME]         = &CellularRadioFactory::createEG95Radio;      m_mCreationMap[EG25Radio::MODEL_NAME]         = &CellularRadioFactory::createEG25Radio; +    m_mCreationMap[CB610LRadio::MODEL_NAME]       = &CellularRadioFactory::createCB610LRadio;  }  ICellularRadio* CellularRadioFactory::create(const std::string& sModel, const std::string& sPort) { @@ -101,7 +103,7 @@ std::string CellularRadioFactory::identifyRadio(const std::string& sPort) {      //Get model      int count = 0; -    std::string sCmd("AT+GMM"); +    std::string sCmd("AT+CGMM");      std::string sResult;      do {          sResult = ICellularRadio::sendCommand(apIo, sCmd, ICellularRadio::DEFAULT_BAIL_STRINGS, 1000, ICellularRadio::CR); @@ -120,7 +122,7 @@ std::string CellularRadioFactory::identifyRadio(const std::string& sPort) {      }      std::string sModel = ICellularRadio::extractModelFromResult(sResult); -    printDebug("RADIO| Extracted [%s] from AT+GMM query", sModel.c_str()); +    printDebug("RADIO| Extracted [%s] from AT+CGMM query", sModel.c_str());      apIo->close();      return sModel;  } @@ -200,3 +202,7 @@ ICellularRadio* CellularRadioFactory::createEG95Radio(const std::string& sPort)  ICellularRadio* CellularRadioFactory::createEG25Radio(const std::string& sPort) const {      return new EG25Radio(sPort);  } + +ICellularRadio* CellularRadioFactory::createCB610LRadio(const std::string& sPort) const { +    return new CB610LRadio(sPort); +}
\ No newline at end of file diff --git a/src/MTS_IO_ICellularRadio.cpp b/src/MTS_IO_ICellularRadio.cpp index 05af28d..e42713d 100644 --- a/src/MTS_IO_ICellularRadio.cpp +++ b/src/MTS_IO_ICellularRadio.cpp @@ -239,6 +239,9 @@ MTS::IO::ICellularRadio::CODE MTS::IO::ICellularRadio::convertModelToMtsShortCod                  eCode = ERROR;              }          } +    } else if (sModel.find("CB610L") == 0) { +        sCode = "L6G1"; +        eCode = SUCCESS;      } else {          sCode = VALUE_NOT_SUPPORTED;          printError("RADIO| Could not identify MTS short code from model. [%s]", sModel.c_str()); @@ -335,6 +338,9 @@ MTS::IO::ICellularRadio::CODE MTS::IO::ICellularRadio::convertModelToType(const      } else if (sModel.find("EG25") == 0) {          sType = VALUE_TYPE_LTE;          eCode = SUCCESS; +    } else if (sModel.find("CB610L") == 0) { +        sType = VALUE_TYPE_LTE; +        eCode = SUCCESS;      } else {          sType = VALUE_TYPE_GSM;          eCode = ERROR; @@ -504,6 +510,8 @@ std::string MTS::IO::ICellularRadio::extractModelFromResult(const std::string& s          sModel = "EG95";      } else if(sResult.find("EG25") != std::string::npos) {          sModel = "EG25"; +    } else if(sResult.find("CB610L") != std::string::npos) { +        sModel = "CB610L";      }      return sModel;  } diff --git a/src/MTS_IO_MccMncTable.cpp b/src/MTS_IO_MccMncTable.cpp index f1940be..6b45e77 100644 --- a/src/MTS_IO_MccMncTable.cpp +++ b/src/MTS_IO_MccMncTable.cpp @@ -21,7 +21,7 @@  /*!   \file MTS_IO_MccMncTable.cpp   \brief Auto-Generated MCC-MNC Lookup Table - \date 2023-01-06 + \date 2023-01-26   \author sgodinez   An Auto-Generated MCC-MNC Lookup Table @@ -798,6 +798,7 @@ void MccMncTable::createTable() {      m_mTable[787][304] = "us,United States,1,AT&T Wireless Inc.,att-first";      m_mTable[787][320] = "us,United States,1,FirstNet,att-first";      m_mTable[787][528] = "us,United States,1,AT&T Wireless Inc.,att"; +    m_mTable[789][16] = "us,United States,1,Generic CBRS,cbrs-generic";      m_mTable[790][16] = "us,United States,1,Sprint Spectrum,";      m_mTable[790][17] = "us,United States,1,Southern Communications Services Inc.,";      m_mTable[816][287] = "pr,Puerto Rico,1787,Puerto Rico Telephone Company Inc. (PRTC),"; diff --git a/src/MTS_IO_SequansRadio.cpp b/src/MTS_IO_SequansRadio.cpp new file mode 100644 index 0000000..f3b32fb --- /dev/null +++ b/src/MTS_IO_SequansRadio.cpp @@ -0,0 +1,553 @@ +/* + * Copyright (C) 2023 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_SequansRadio.h> + +#include <mts/MTS_Logger.h> +#include <mts/MTS_Thread.h> +#include <mts/MTS_Text.h> +#include <mts/MTS_Timer.h> + +using namespace MTS::IO; + +bool SequansRadio::resetRadio(uint32_t iTimeoutMillis) { +    printInfo("%s| Rebooting radio", getName().c_str()); +    if (sendBasicCommand("AT^RESET") == SUCCESS) { +        if (iTimeoutMillis > 5000) { +            MTS::Thread::sleep(5000); +            iTimeoutMillis -= 5000; +        } +        return resetConnection(iTimeoutMillis); +    } + +    return false; +} + +ICellularRadio::CODE SequansRadio::getVendorFirmware(std::string& sVendorFirmware) { +    return getFirmware(sVendorFirmware); +} + +ICellularRadio::CODE SequansRadio::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+CGMM"); +    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; +} + +ICellularRadio::CODE SequansRadio::getIccid(std::string& sIccid) { +    printTrace("%s| Get ICCID", getName().c_str()); + +    const int iEFiccidId = 0x2FE2; +    const uint8_t iOffsetHigh = 0; +    const uint8_t iOffsetLow = 0; +    const uint8_t iNumBytes = 10; + +    CODE rc; +    std::string sEFiccidContent; + +    rc = simAccessReadBinary(iEFiccidId, iOffsetLow, iOffsetHigh, iNumBytes, sEFiccidContent); +    if (rc != SUCCESS) { +        printError("%s| Failed to determine the SIM ICCID", getName().c_str()); +        return rc; +    } + +    int iEFiccidLen = sEFiccidContent.size(); +    if (iEFiccidLen != 20) { +        printError("%s| Invalid length of the raw ICCID value: expected [20], actual: [%d]", getName().c_str(), iEFiccidLen); +        return FAILURE; +    } + +    // sEFiccidContent is a raw Binary-Coded Decimal string with swapped nibbles. +    for (int i = 0; i < iEFiccidLen; i+=2) { +        std::string sPart = sEFiccidContent.substr(i, 2); +        std::swap(sPart[0], sPart[1]); +        sIccid.append(sPart); +    } + +    printDebug("%s| Got ICCID [%s]", getName().c_str(), sIccid.c_str()); + +    return SUCCESS; +} + +ICellularRadio::CODE SequansRadio::getService(std::string& sService) { +    printTrace("%s| Get Service", getName().c_str()); +    sService = ICellularRadio::VALUE_NOT_SUPPORTED; +    std::string sCmd("AT+COPS?"); +    std::string sPrefix("+COPS:"); +    std::string sResult; +    CODE rc; + +    rc = sendBasicQuery(sCmd, sPrefix, sResult, 100); +    if (SUCCESS != rc) { +        printWarning("%s| Unable to get Service from radio using command [%s]", getName().c_str(), sCmd.c_str()); +        return rc; +    } + +    std::vector<std::string> vParts = MTS::Text::split(sResult, ','); +    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   1 : sService = "GPRS"  ; break;  // GSM Compact +        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   8 : sService = "EGPRS" ; break;  // EC-GSM-IoT  +        case   9 : sService = "LTE"   ; break;  // E-UTRAN (NB-S1 mode)  + +        default: sService = ICellularRadio::VALUE_UNKNOWN; break; +    } + +    printDebug("%s| Service ID: [%d][%s]", getName().c_str(), iAccessTechnology, sService.c_str()); + +    return SUCCESS; +} + +ICellularRadio::CODE SequansRadio::getNetwork(std::string& sNetwork) { +    printTrace("%s| Get Network", getName().c_str()); +    sNetwork = ICellularRadio::VALUE_NOT_SUPPORTED; +    std::string sCmd("AT+COPS?"); +    std::string sPrefix("+COPS:"); +    std::string sResult; +    CODE rc; + +    rc = sendBasicQuery(sCmd, sPrefix, sResult, 100); +    if (SUCCESS != rc) { +        printWarning("%s| Unable to get network name from radio using command [%s]", getName().c_str(), sCmd.c_str()); +        return rc; +    } +    std::vector<std::string> vParts = MTS::Text::split(sResult, ","); + +    if (vParts.size() > 3) { +        const std::string sValue = vParts[2]; + +        // +COPS: 0,2,"315010",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+SQNQRCCI? -- This command queries and reports several information from camped cell. +// +SQNQRCCI: [<mcc>,<mnc>,<cellId>,<pci>,<tac>,<dlBw>,<specialSubframeConfig>,<subframeAssignment>,<dlEarfcn>,<dlTxMode>,<band>] +ICellularRadio::CODE SequansRadio::getNetworkStatusSQN(Json::Value& jData, Json::Value& jDebug) { +    std::string sCmd; +    std::string sResult; +    std::string sPrefix; +    std::vector<std::string> vParts; +    CODE rc; + +    sCmd = "AT+SQNQRCCI?"; +    sPrefix = "+SQNQRCCI:"; + +    rc = sendBasicQuery(sCmd, sPrefix, sResult, 200); +    if (SUCCESS != rc) { +        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 rc; +    } + +    // <mcc>,<mnc>,<cellId>,<pci>,<tac>,<dlBw>,<specialSubframeConfig>,<subframeAssignment>,<dlEarfcn>,<dlTxMode>,<band> +    //  [0]   [1]     [2]    [3]   [4]   [5]            [6]                    [7]               [8]       [9]     [10] +    vParts = MTS::Text::split(sResult, ","); + +    if (vParts.size() < 11) { +        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 FAILURE; +    } + +    jData[ICellularRadio::KEY_MCC]     = vParts[0]; +    jData[ICellularRadio::KEY_MNC]     = vParts[1]; +    jData[ICellularRadio::KEY_CID]     = vParts[2]; +    jData["tac"]                       = vParts[4]; +    jData[ICellularRadio::KEY_CHANNEL] = vParts[8]; +    jData[ICellularRadio::KEY_ABND]    = "EUTRAN BAND" + vParts[10]; // concat string to convert to common format + +    return SUCCESS; +} + +// AT+CESQ? -- Execution command returns received signal quality parameters. +// +CESQ: <rxlev>,<ber>,<rscp>,<ecno>,<rsrq>,<rsrp> +ICellularRadio::CODE SequansRadio::getNetworkStatusSignal(Json::Value& jData, Json::Value& jDebug) { +    std::string sCmd; +    std::string sResult; +    std::string sPrefix; +    std::vector<std::string> vParts; +    CODE rc; + +    sCmd = "AT+CESQ"; +    sPrefix = "+CESQ:"; + +    rc = sendBasicQuery(sCmd, sPrefix, sResult, 200); +    if (SUCCESS != rc) { +        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 rc; +    } + +    // <rxlev>,<ber>,<rscp>,<ecno>,<rsrq>,<rsrp> +    //   [0]    [1]   [2]    [3]    [4]    [5] +    vParts = MTS::Text::split(sResult, ","); + +    if (vParts.size() < 6) { +        printDebug("%s| Network Status command response 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 FAILURE; +    } + +    int iValue; +    if (MTS::Text::parse(iValue, vParts[5])) { +        do { +            if (255 == iValue) { +                break; // invalid, not known or not detectable +            } +            if (iValue > 97 || iValue < 0) { +                printDebug("%s| Network Status command returned unexpected value of RSRP: [%s][%d]", getName().c_str(), sCmd.c_str(), iValue); +                printTrace("%s| Network Status:\n%s\n", getName().c_str(), jData.toStyledString().c_str()); +                break; +            } +            // converting value to rsrp dBm according to documentation +            jDebug["rsrp"] = std::to_string(iValue - 141); +        } while (false); +    } + +    if (MTS::Text::parse(iValue, vParts[4])) { +        do { +            if (255 == iValue) { +                break; // invalid, not known or not detectable +            } +            if (iValue > 34 || iValue < 0) { +                printDebug("%s| Network Status command returned unexpected value of RSRQ: [%s][%d]", getName().c_str(), sCmd.c_str(), iValue); +                printTrace("%s| Network Status:\n%s\n", getName().c_str(), jData.toStyledString().c_str()); +                break; +            } +            // converting value to rsrq in dBm according to documentation +            jDebug["rsrq"] = std::to_string((iValue-40.0)/2.0); +        } while (false); +    } + +    return SUCCESS; +} + +// AT+SQNQRUP? -- This command queries and reports the uplink power of the current LTE network. +// +SQNQRUP: <txPower>,<maxTxPower> +ICellularRadio::CODE SequansRadio::getNetworkStatusTxPower(Json::Value& jData, Json::Value& jDebug) { +    std::string sCmd; +    std::string sResult; +    std::string sPrefix; +    std::vector<std::string> vParts; +    int iValue; +    CODE rc; + +    sCmd = "AT+SQNQRUP?"; +    sPrefix = "+SQNQRUP:"; + +    // the key must be present +    jDebug[ICellularRadio::KEY_TXPWR] = ""; + +    rc = sendBasicQuery(sCmd, sPrefix, sResult, 200); +    if (SUCCESS != rc) { +        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 rc; +    } +    // <txPower>,<maxTxPower> +    //    [0]        [1] +    vParts = MTS::Text::split(sResult, ","); + +    if (vParts.size() < 2) { +        printDebug("%s| Network Status command response 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 FAILURE; +    } + +    double fTxPow; +    if (MTS::Text::parse(fTxPow, vParts[0])) { +        do { +            if (-21474836.0 == fTxPow) { +                break; // invalid, not known or not detectable +            } +            if (fTxPow < -255 || fTxPow > 99) { +                printDebug("%s| Network Status command returned unexpected value of txPower: [%s][%d]", getName().c_str(), sCmd.c_str(), iValue); +                printTrace("%s| Network Status:\n%s\n", getName().c_str(), jData.toStyledString().c_str()); +                break; +            } +            jDebug[ICellularRadio::KEY_TXPWR] = vParts[0]; +        } while (false); +    } + +    return SUCCESS; +} + +ICellularRadio::CODE SequansRadio::getNetworkStatus(Json::Value& jData) { +    printTrace("%s| Get Network Status", getName().c_str()); + +    // NOTE: Only the LTE radios from Sequans are supported at the moment + +    Json::Value jDebug; +    std::string sResult; + +    getCommonNetworkStats(jData); +    getNetworkStatusSQN(jData, jDebug); +    getNetworkStatusSignal(jData, jDebug); +    getNetworkStatusTxPower(jData, jDebug); + +    if (jData.isMember(ICellularRadio::KEY_RSSIDBM)) { +        jDebug[ICellularRadio::KEY_RSSIDBM] = jData[ICellularRadio::KEY_RSSIDBM]; +    } + +    if (SUCCESS == getImsi(sResult)) { +        jData[ICellularRadio::KEY_IMSI] = sResult; +    } + +    if (SUCCESS == getNetwork(sResult)) { +        jData[ICellularRadio::KEY_NETWORK] = sResult; +    } + +    jDebug[ICellularRadio::KEY_SD] = "PS"; + +    jData[ICellularRadio::KEY_DEBUG] = jDebug; + +    printTrace("%s| Network Status:\n%s\n", getName().c_str(), jData.toStyledString().c_str()); +    return SUCCESS; + +} + +ICellularRadio::CODE MTS::IO::SequansRadio::convertSignalStrengthTodBm(const int32_t &iRssi, int32_t &iDBm) { +    const int dbmSteps   =  2; +    const int minValue   = -113; +    const int maxValue   = -51; +    const int rssiOffset =  0; +    int rawDbm; + +    if (iRssi < 0 || iRssi > 99) { +        return FAILURE;  // invalid, not known or not detectable +    } + +    rawDbm = minValue + ((iRssi - rssiOffset) * dbmSteps); +    iDBm = std::min(maxValue, rawDbm); + +    return SUCCESS; +} + +ICellularRadio::CODE SequansRadio::convertdBmToSignalStrength(const int32_t& iDBm, int32_t& iRssi) { +    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; +} + +ICellularRadio::CODE SequansRadio::setMdn(const Json::Value& jArgs) { +    printTrace("%s| Set MDN", getName().c_str()); +    return NOT_APPLICABLE; +} + +ICellularRadio::CODE SequansRadio::setUeModeOfOperation(ICellularRadio::UE_MODES_OF_OPERATION mode) { +    printTrace("%s| Set UE Mode Of Operation", getName().c_str()); + +    std::string sValue; + +    switch (mode) { +        case ICellularRadio::UE_MODES_OF_OPERATION::PS_MODE1: +            sValue = "3"; +            break; +        case ICellularRadio::UE_MODES_OF_OPERATION::PS_MODE2: +            sValue = "0"; +            break; +        case ICellularRadio::UE_MODES_OF_OPERATION::CS_PS_MODE1: +            sValue = "1"; +            break; +        case ICellularRadio::UE_MODES_OF_OPERATION::CS_PS_MODE2: +            sValue = "2"; +            break; +        default: +            printError("%s| Set UE Mode Of Operation: invalid argument", getName().c_str()); +            return INVALID_ARGS; +    } + +    const int dTimeout = 1000; // ms +    const std::string sCmd = "AT+CEMODE=" + sValue; + +    return sendBasicCommand(sCmd, dTimeout); +} + +ICellularRadio::CODE SequansRadio::getUeModeOfOperation(ICellularRadio::UE_MODES_OF_OPERATION& mode) { +    printTrace("%s| Get UE Mode Of Operation", getName().c_str()); + +    std::string sCmd = "AT+CEMODE?"; +    std::string sPrefix = "+CEMODE:"; +    std::string sResult; + +    if (SUCCESS != sendBasicQuery(sCmd, sPrefix, sResult, 1000)) { +        printError("%s| Unable to get UE Mode Of Operation from radio using command [%s]", getName().c_str(), sCmd.c_str()); +        return FAILURE; +    } + +    uint8_t uiValue; + +    if (!MTS::Text::parse(uiValue, sResult)) { +        printError("%s| Unable to parse CEMODE from response [%s]", getName().c_str(), sResult.c_str()); +        return FAILURE; +    } + +    CODE rc; +    switch (uiValue) { +        case 0: +            mode = ICellularRadio::UE_MODES_OF_OPERATION::PS_MODE2; +            rc = SUCCESS; +            break; +        case 1: +            mode = ICellularRadio::UE_MODES_OF_OPERATION::CS_PS_MODE1; +            rc = SUCCESS; +            break; +        case 2: +            mode = ICellularRadio::UE_MODES_OF_OPERATION::CS_PS_MODE2; +            rc = SUCCESS; +            break; +        case 3: +            mode = ICellularRadio::UE_MODES_OF_OPERATION::PS_MODE1; +            rc = SUCCESS; +            break; +        default: +            printError("%s| Unable to parse CEMODE from response [%s]", getName().c_str(), sResult.c_str()); +            mode = ICellularRadio::UE_MODES_OF_OPERATION::UNKNOWN_MODE; +            rc = FAILURE; +            break; +    } +    return rc; +} + +ICellularRadio::CODE SequansRadio::setRxDiversity(const Json::Value& jArgs) { +    return NOT_APPLICABLE; +} + +const std::vector<std::string> SequansRadio::getRegistrationCommands() { +    return { "CEREG" }; +} + +SequansRadio::SequansRadio(const std::string& sName, const std::string& sRadioPort) +: CellularRadio (sName, sRadioPort) +{ +} + +ICellularRadio::CODE SequansRadio::getIsSimInserted(bool& bData) { +    printTrace("%s| Get SIM insertion status", getName().c_str()); +    // there is no command to check SIM presence +    return NOT_APPLICABLE; +} + +ICellularRadio::CODE SequansRadio::getSimLockAttempts(int& iAttemptsPin, int& iAttemptsPuk) { +    printTrace("%s| Get SIM unlock attempts left", getName().c_str()); +    CODE rc = FAILURE; + +    rc = getSimLockAttemptsByType("SIM PIN", iAttemptsPin); +    if (rc != SUCCESS) { +       return rc; +    } + +    rc = getSimLockAttemptsByType("SIM PUK", iAttemptsPuk); +    if (rc != SUCCESS) { +        return rc; +    } + +    return SUCCESS; +} + +ICellularRadio::CODE SequansRadio::getSimLockAttemptsByType(const std::string& sArg, int& iAttempts) { +    std::string sCmd("AT+CPINR"); +    std::string sPrefix("+CPINR:"); +    std::string sResult; +    int iValue; +    CODE rc = FAILURE; + +    sCmd += "=\""; +    sCmd += sArg; +    sCmd += "\""; + +    printDebug("%s| Sending SIM lock attempt query [%s]", getName().c_str(), sCmd.c_str()); + +    rc = sendBasicQuery(sCmd, sPrefix, sResult, 500); +    if (SUCCESS != rc) { +        return rc; +    } + +    std::vector<std::string> vParts = MTS::Text::split(sResult, ','); + +    if (vParts.size() < 3 || ! MTS::Text::parse(iValue, vParts[1])) { +        printWarning("%s| Unable to parse SIM unlock attempts from response [%s]", getName().c_str(), sResult.c_str()); +        return FAILURE; +    } + +    iAttempts = iValue; +    return SUCCESS; +} + +const std::vector<std::string>& SequansRadio::getDiagCommands(bool) { +    // TODO: Fill the list of commands in scope of the "L6G1 - Cellular Diagnostics" feature + +    // Declare as static to initialize only when used, but cache the results. +    const static std::vector<std::string> vCommands { +    }; + +    return vCommands; +} | 
