Commit eab12cc8 authored by aligungr's avatar aligungr

UE cell coverage detection mechanism

parent cf9d1ebb
......@@ -23,6 +23,7 @@ add_subdirectory(src/app)
add_subdirectory(src/urs)
add_subdirectory(src/crypt)
add_subdirectory(src/ue)
add_subdirectory(src/sas)
#################### GNB EXECUTABLE ####################
......@@ -42,6 +43,7 @@ target_link_libraries(nr-gnb udp)
target_link_libraries(nr-gnb app)
target_link_libraries(nr-gnb urs)
target_link_libraries(nr-gnb crypt)
target_link_libraries(nr-gnb sas)
#################### UE EXECUTABLE ####################
......@@ -58,6 +60,7 @@ target_link_libraries(nr-ue app)
target_link_libraries(nr-ue urs)
target_link_libraries(nr-ue crypt)
target_link_libraries(nr-ue ue)
target_link_libraries(nr-ue sas)
###################### IF BINDER ######################
add_library(devbnd SHARED src/binder.cpp)
......
cmake_minimum_required(VERSION 3.17)
file(GLOB_RECURSE HDR_FILES *.hpp)
file(GLOB_RECURSE SRC_FILES *.cpp)
add_library(sas ${HDR_FILES} ${SRC_FILES})
target_compile_options(sas PRIVATE -Wall -Wextra -pedantic -Wno-unused-parameter)
target_link_libraries(sas utils)
target_link_libraries(sas udp)
//
// This file is a part of UERANSIM open source project.
// Copyright (c) 2021 ALİ GÜNGÖR.
//
// The software and all associated files are licensed under GPL-3.0
// and subject to the terms and conditions defined in LICENSE file.
//
#include "pdu.hpp"
#include <utils/constants.hpp>
namespace sas
{
static void AppendPlmn(const Plmn &plmn, OctetString &stream)
{
stream.appendOctet2(plmn.mcc);
stream.appendOctet2(plmn.mnc);
stream.appendOctet(plmn.isLongMnc ? 1 : 0);
}
static void AppendGlobalNci(const GlobalNci &nci, OctetString &stream)
{
AppendPlmn(nci.plmn, stream);
stream.appendOctet8(nci.nci);
}
static Plmn DecodePlmn(const OctetView &stream)
{
Plmn res{};
res.mcc = stream.read2I();
res.mnc = stream.read2I();
res.isLongMnc = stream.readI() != 0;
return res;
}
static GlobalNci DecodeGlobalNci(const OctetView &stream)
{
GlobalNci res{};
res.plmn = DecodePlmn(stream);
res.nci = stream.read8L();
return res;
}
void EncodeSasMessage(const SasMessage &msg, OctetString &stream)
{
stream.appendOctet(0x03); // (Just for old RLS compatibility)
stream.appendOctet(cons::Major);
stream.appendOctet(cons::Minor);
stream.appendOctet(cons::Patch);
stream.appendOctet(static_cast<uint8_t>(msg.msgType));
if (msg.msgType == SasMessageType::CELL_INFO_REQUEST)
{
auto m = (const SasCellInfoRequest &)msg;
stream.appendOctet4(m.simPos);
}
else if (msg.msgType == SasMessageType::CELL_INFO_RESPONSE)
{
auto m = (const SasCellInfoResponse &)msg;
AppendGlobalNci(m.cellId, stream);
stream.appendOctet4(m.tac);
stream.appendOctet4(m.dbm);
}
}
std::unique_ptr<SasMessage> DecodeSasMessage(const OctetView &stream)
{
auto first = stream.readI(); // (Just for old RLS compatibility)
if (first != 3)
return nullptr;
if (stream.read() != cons::Major)
return nullptr;
if (stream.read() != cons::Minor)
return nullptr;
if (stream.read() != cons::Patch)
return nullptr;
auto msgType = static_cast<SasMessageType>(stream.readI());
if (msgType == SasMessageType::CELL_INFO_REQUEST)
{
auto res = std::make_unique<SasCellInfoRequest>();
res->simPos = stream.read4I();
return res;
}
else if (msgType == SasMessageType::CELL_INFO_RESPONSE)
{
auto res = std::make_unique<SasCellInfoResponse>();
res->cellId = DecodeGlobalNci(stream);
res->tac = stream.read4I();
res->dbm = stream.read4I();
return res;
}
return nullptr;
}
} // namespace sas
//
// This file is a part of UERANSIM open source project.
// Copyright (c) 2021 ALİ GÜNGÖR.
//
// The software and all associated files are licensed under GPL-3.0
// and subject to the terms and conditions defined in LICENSE file.
//
#pragma once
#include <cstdint>
#include <memory>
#include <utils/common_types.hpp>
#include <utils/octet_string.hpp>
#include <utils/octet_view.hpp>
namespace sas
{
enum class SasMessageType : uint8_t
{
RESERVED = 0,
CELL_INFO_REQUEST,
CELL_INFO_RESPONSE,
};
struct SasMessage
{
const SasMessageType msgType;
explicit SasMessage(SasMessageType msgType) : msgType(msgType)
{
}
};
struct SasCellInfoRequest : SasMessage
{
int32_t simPos{};
SasCellInfoRequest() : SasMessage(SasMessageType::CELL_INFO_REQUEST)
{
}
};
struct SasCellInfoResponse : SasMessage
{
GlobalNci cellId{};
int32_t tac{};
int32_t dbm{};
SasCellInfoResponse() : SasMessage(SasMessageType::CELL_INFO_RESPONSE)
{
}
};
void EncodeSasMessage(const SasMessage &msg, OctetString &stream);
std::unique_ptr<SasMessage> DecodeSasMessage(const OctetView &stream);
} // namespace sas
\ No newline at end of file
//
// This file is a part of UERANSIM open source project.
// Copyright (c) 2021 ALİ GÜNGÖR.
//
// The software and all associated files are licensed under GPL-3.0
// and subject to the terms and conditions defined in LICENSE file.
//
#include "task.hpp"
#include <utils/common.hpp>
static int DBM_STRONG_STRENGTH_THRESHOLD = -70;
namespace nr::ue
{
void UeSasTask::onMeasurement()
{
evaluatePendingMeasurements();
requestCellInfo();
}
void UeSasTask::evaluatePendingMeasurements()
{
std::vector<GlobalNci> entered{};
std::vector<GlobalNci> exited{};
// compare active and pending measurements
for (auto &m : m_activeMeasurements)
{
bool newStrong = m.second.dbm >= DBM_STRONG_STRENGTH_THRESHOLD;
if (m_pendingMeasurements.count(m.first))
{
bool oldStrong = m_pendingMeasurements[m.first].dbm >= DBM_STRONG_STRENGTH_THRESHOLD;
if (newStrong ^ oldStrong)
(newStrong ? entered : exited).push_back(m.first);
}
else if (newStrong)
entered.push_back(m.first);
}
for (auto &m : m_pendingMeasurements)
{
bool oldStrong = m_pendingMeasurements[m.first].dbm >= DBM_STRONG_STRENGTH_THRESHOLD;
if (!m_activeMeasurements.count(m.first) && oldStrong)
exited.push_back(m.first);
}
if (!entered.empty() && !exited.empty())
onCoverageChange(entered, exited);
// copy from pending to active measurements
m_activeMeasurements = m_pendingMeasurements;
// clear pending measurements
m_pendingMeasurements = {};
}
void UeSasTask::requestCellInfo()
{
for (auto &ip : m_cellSearchSpace)
{
sas::SasCellInfoRequest req{};
sendSasMessage(ip, req);
}
}
void UeSasTask::receiveCellInfoResponse(const sas::SasCellInfoResponse &msg)
{
UeCellMeasurement meas{};
meas.cellId = msg.cellId;
meas.tac = msg.tac;
meas.dbm = msg.dbm;
meas.time = utils::CurrentTimeMillis();
m_pendingMeasurements[meas.cellId] = meas;
}
void UeSasTask::onCoverageChange(const std::vector<GlobalNci> &entered, const std::vector<GlobalNci> &exited)
{
m_logger->debug("Coverage change detected. [%d] cell entered, [%d] exited to/from coverage",
static_cast<int>(entered.size()), static_cast<int>(exited.size()));
// TODO
}
} // namespace nr::ue
//
// This file is a part of UERANSIM open source project.
// Copyright (c) 2021 ALİ GÜNGÖR.
//
// The software and all associated files are licensed under GPL-3.0
// and subject to the terms and conditions defined in LICENSE file.
//
#include "task.hpp"
#include <utils/constants.hpp>
static const int TIMER_ID_MEASUREMENT = 1;
static const int TIMER_PERIOD_MEASUREMENT = 2000;
namespace nr::ue
{
UeSasTask::UeSasTask(TaskBase *base)
: m_base{base}, m_udpTask{}, m_cellSearchSpace{}, m_pendingMeasurements{}, m_activeMeasurements{}
{
m_logger = m_base->logBase->makeUniqueLogger(m_base->config->getLoggerPrefix() + "sas");
for (auto &addr : m_base->config->gnbSearchList)
m_cellSearchSpace.emplace_back(addr, cons::PortalPort);
}
void UeSasTask::onStart()
{
m_udpTask = new udp::UdpServerTask(this);
std::vector<InetAddress> gnbSearchList{};
for (auto &ip : m_base->config->gnbSearchList)
gnbSearchList.emplace_back(ip, cons::PortalPort);
m_udpTask->start();
setTimer(TIMER_ID_MEASUREMENT, TIMER_PERIOD_MEASUREMENT);
onMeasurement();
}
void UeSasTask::onLoop()
{
NtsMessage *msg = take();
if (!msg)
return;
switch (msg->msgType)
{
case NtsMessageType::TIMER_EXPIRED: {
auto *w = dynamic_cast<NwTimerExpired *>(msg);
if (w->timerId == TIMER_ID_MEASUREMENT)
{
setTimer(TIMER_ID_MEASUREMENT, TIMER_PERIOD_MEASUREMENT);
onMeasurement();
}
break;
}
case NtsMessageType::UDP_SERVER_RECEIVE: {
auto *w = dynamic_cast<udp::NwUdpServerReceive *>(msg);
auto sasMsg = sas::DecodeSasMessage(OctetView{w->packet});
if (sasMsg == nullptr)
{
m_logger->err("Unable to decode SAS message");
break;
}
receiveSasMessage(w->fromAddress, *sasMsg);
break;
}
default:
m_logger->unhandledNts(msg);
break;
}
delete msg;
}
void UeSasTask::onQuit()
{
m_udpTask->quit();
delete m_udpTask;
}
} // namespace nr::ue
//
// This file is a part of UERANSIM open source project.
// Copyright (c) 2021 ALİ GÜNGÖR.
//
// The software and all associated files are licensed under GPL-3.0
// and subject to the terms and conditions defined in LICENSE file.
//
#pragma once
#include <memory>
#include <sas/pdu.hpp>
#include <thread>
#include <udp/server_task.hpp>
#include <ue/types.hpp>
#include <unordered_map>
#include <utils/common_types.hpp>
#include <utils/logger.hpp>
#include <utils/nts.hpp>
#include <vector>
namespace nr::ue
{
class UeSasTask : public NtsTask
{
private:
TaskBase *m_base;
std::unique_ptr<Logger> m_logger;
udp::UdpServerTask *m_udpTask;
std::vector<InetAddress> m_cellSearchSpace;
std::unordered_map<GlobalNci, UeCellMeasurement> m_pendingMeasurements;
std::unordered_map<GlobalNci, UeCellMeasurement> m_activeMeasurements;
friend class UeCmdHandler;
public:
explicit UeSasTask(TaskBase *base);
~UeSasTask() override = default;
protected:
void onStart() override;
void onLoop() override;
void onQuit() override;
private: /* Transport */
void receiveSasMessage(const InetAddress &address, const sas::SasMessage &msg);
void sendSasMessage(const InetAddress &address, const sas::SasMessage &msg);
private: /* Measurement */
void onMeasurement();
void evaluatePendingMeasurements();
void requestCellInfo();
void receiveCellInfoResponse(const sas::SasCellInfoResponse &msg);
void onCoverageChange(const std::vector<GlobalNci> &entered, const std::vector<GlobalNci> &exited);
};
} // namespace nr::ue
\ No newline at end of file
//
// This file is a part of UERANSIM open source project.
// Copyright (c) 2021 ALİ GÜNGÖR.
//
// The software and all associated files are licensed under GPL-3.0
// and subject to the terms and conditions defined in LICENSE file.
//
#include "task.hpp"
namespace nr::ue
{
void UeSasTask::receiveSasMessage(const InetAddress &address, const sas::SasMessage &msg)
{
switch (msg.msgType)
{
case sas::SasMessageType::CELL_INFO_RESPONSE: {
receiveCellInfoResponse((const sas::SasCellInfoResponse &)msg);
break;
default:
m_logger->err("Unhandled SAS message type[%d]", static_cast<int>(msg.msgType));
break;
}
}
}
void UeSasTask::sendSasMessage(const InetAddress &address, const sas::SasMessage &msg)
{
OctetString stream{};
sas::EncodeSasMessage(msg, stream);
m_udpTask->send(address, stream);
}
} // namespace nr::ue
......@@ -27,6 +27,7 @@ class UeAppTask;
class UeMrTask;
class NasTask;
class UeRrcTask;
class UeSasTask;
class UserEquipment;
struct SupportedAlgs
......@@ -125,6 +126,7 @@ struct TaskBase
UeMrTask *mrTask{};
NasTask *nasTask{};
UeRrcTask *rrcTask{};
UeSasTask *sasTask{};
};
struct UeTimers
......
......@@ -12,6 +12,7 @@
#include "mr/task.hpp"
#include "nas/task.hpp"
#include "rrc/task.hpp"
#include "sas/task.hpp"
namespace nr::ue
{
......@@ -31,6 +32,7 @@ UserEquipment::UserEquipment(UeConfig *config, app::IUeController *ueController,
base->rrcTask = new UeRrcTask(base);
base->mrTask = new UeMrTask(base);
base->appTask = new UeAppTask(base);
base->sasTask = new UeSasTask(base);
taskBase = base;
}
......@@ -40,11 +42,13 @@ UserEquipment::~UserEquipment()
taskBase->nasTask->quit();
taskBase->rrcTask->quit();
taskBase->mrTask->quit();
taskBase->sasTask->quit();
taskBase->appTask->quit();
delete taskBase->nasTask;
delete taskBase->rrcTask;
delete taskBase->mrTask;
delete taskBase->sasTask;
delete taskBase->appTask;
delete taskBase->logBase;
......@@ -57,6 +61,7 @@ void UserEquipment::start()
taskBase->nasTask->start();
taskBase->rrcTask->start();
taskBase->mrTask->start();
taskBase->sasTask->start();
taskBase->appTask->start();
}
......
......@@ -66,4 +66,11 @@ static std::string IntToHex(T i)
return stream.str();
}
template <class T>
inline void HashCombine(std::size_t &seed, const T &v)
{
std::hash<T> hasher{};
seed ^= hasher(v) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
}
} // namespace utils
\ No newline at end of file
......@@ -7,6 +7,7 @@
//
#include "common_types.hpp"
#include "common.hpp"
#include <algorithm>
#include <iomanip>
#include <sstream>
......@@ -85,8 +86,39 @@ bool operator==(const SingleSlice &lhs, const SingleSlice &rhs)
return ((int)*lhs.sd) == ((int)*rhs.sd);
}
bool operator==(const Plmn &lhs, const Plmn &rhs)
{
if (lhs.mcc != rhs.mcc)
return false;
if (lhs.mnc != rhs.mnc)
return false;
return lhs.isLongMnc == rhs.isLongMnc;
}
bool operator==(const GlobalNci &lhs, const GlobalNci &rhs)
{
return lhs.plmn == rhs.plmn && lhs.nci == rhs.nci;
}
void NetworkSlice::addIfNotExists(const SingleSlice &slice)
{
if (!std::any_of(slices.begin(), slices.end(), [&slice](auto &s) { return s == slice; }))
slices.push_back(slice);
}
std::size_t std::hash<Plmn>::operator()(const Plmn &v) const noexcept
{
std::size_t h = 0;
utils::HashCombine(h, v.mcc);
utils::HashCombine(h, v.mnc);
utils::HashCombine(h, v.isLongMnc);
return h;
}
std::size_t std::hash<GlobalNci>::operator()(const GlobalNci &v) const noexcept
{
std::size_t h = 0;
utils::HashCombine(h, v.plmn);
utils::HashCombine(h, v.nci);
return h;
}
......@@ -12,9 +12,9 @@
#include "octet.hpp"
#include <memory>
#include <optional>
#include <stdexcept>
#include <utility>
#include <vector>
#include <stdexcept>
enum class EPagingDrx
{
......@@ -126,4 +126,43 @@ Json ToJson(const NetworkSlice &v);
Json ToJson(const PlmnSupport &v);
Json ToJson(const EDeregCause &v);
struct GlobalNci
{
Plmn plmn{};
int64_t nci{};
GlobalNci() = default;
GlobalNci(const Plmn &plmn, int64_t nci) : plmn(plmn), nci(nci)
{
}
};
struct UeCellMeasurement
{
GlobalNci cellId{};
int tac{};
int dbm{};
uint64_t time{};
};
bool operator==(const SingleSlice &lhs, const SingleSlice &rhs);
bool operator==(const Plmn &lhs, const Plmn &rhs);
bool operator==(const GlobalNci &lhs, const GlobalNci &rhs);
namespace std
{
template <>
struct hash<Plmn>
{
std::size_t operator()(const Plmn &v) const noexcept;
};
template <>
struct hash<GlobalNci>
{
std::size_t operator()(const GlobalNci &v) const noexcept;
};
} // namespace std
\ No newline at end of file
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment