summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile4
-rw-r--r--data/MccMncTable.csv1
-rw-r--r--include/mts/MTS_IO_CB610LRadio.h48
-rw-r--r--include/mts/MTS_IO_CellularRadioFactory.h1
-rw-r--r--include/mts/MTS_IO_SequansRadio.h83
-rw-r--r--src/MTS_IO_CB610LRadio.cpp58
-rw-r--r--src/MTS_IO_CellularRadioFactory.cpp10
-rw-r--r--src/MTS_IO_ICellularRadio.cpp8
-rw-r--r--src/MTS_IO_MccMncTable.cpp3
-rw-r--r--src/MTS_IO_SequansRadio.cpp553
10 files changed, 765 insertions, 4 deletions
diff --git a/Makefile b/Makefile
index 95b5e40..147623a 100644
--- a/Makefile
+++ b/Makefile
@@ -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;
+}