Skip to content

Commit

Permalink
lib: location: Add support for GCI cell
Browse files Browse the repository at this point in the history
Support added for GCI (surrounding) cells in cellular positioning.
NCELLMEAS=2 is used by default. If we don’t find X cells,
do NCELLMEAS=4,Y, where Y=5-X.
X is requested cell count and run-time configurable through
cell_count member of struct location_cellular_config and
build time configurable through
CONFIG_LOCATION_REQUEST_DEFAULT_CELLULAR_CELL_COUNT.

Cellular timeout will change to apply only for NCELLMEAS
operation ignoring the cloud communication.
Same change for Wi-Fi timeout.

GCI search can be disabled completely by setting
CONFIG_LOCATION_REQUEST_DEFAULT_CELLULAR_CELL_COUNT=0

Jira: NCSDK-21877

Signed-off-by: Tommi Rantanen <tommi.rantanen@nordicsemi.no>
  • Loading branch information
trantanen authored and tokangas committed Jun 28, 2023
1 parent 43b92e6 commit 0ae3662
Show file tree
Hide file tree
Showing 12 changed files with 184 additions and 49 deletions.
4 changes: 4 additions & 0 deletions doc/nrf/libraries/modem/location.rst
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,9 @@ The supported location methods are as follows:
* Cellular positioning

* Uses :ref:`lte_lc_readme` for getting a list of nearby cellular base stations.
* Neighbor cell measurement is performed with :c:enum:`LTE_LC_NEIGHBOR_SEARCH_TYPE_EXTENDED_COMPLETE` search type.
A GCI search with :c:enum:`LTE_LC_NEIGHBOR_SEARCH_TYPE_GCI_EXTENDED_LIGHT` search type is performed if the previous search did not find enough cells.
For more details on GCI search, see :c:member:`location_cellular_config.cell_count`.
* The ``cloud location`` method handles sending cell information to the selected location service and getting the calculated location back to the device.

* Wi-Fi positioning
Expand Down Expand Up @@ -182,6 +185,7 @@ when :c:func:`location_config_defaults_set` function is called:
* :kconfig:option:`CONFIG_LOCATION_REQUEST_DEFAULT_GNSS_VISIBILITY_DETECTION`
* :kconfig:option:`CONFIG_LOCATION_REQUEST_DEFAULT_GNSS_PRIORITY_MODE`
* :kconfig:option:`CONFIG_LOCATION_REQUEST_DEFAULT_CELLULAR_TIMEOUT`
* :kconfig:option:`CONFIG_LOCATION_REQUEST_DEFAULT_CELLULAR_CELL_COUNT`
* :kconfig:option:`CONFIG_LOCATION_REQUEST_DEFAULT_WIFI_TIMEOUT`

Usage
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -360,6 +360,10 @@ Modem libraries

* Updated the :c:func:`nrf_modem_lib_shutdown` function to allow the modem to be in flight mode (``CFUN=4``) when shutting down the modem.

* :ref:`lib_location` library:

* Neighbor cell search is modified to use GCI search depending on :c:member:`location_cellular_config.cell_count` value.

* :ref:`pdn_readme` library:

* Updated the library to allow a ``PDP_type``-only configuration in the :c:func:`pdn_ctx_configure` function.
Expand Down
29 changes: 29 additions & 0 deletions include/modem/location.h
Original file line number Diff line number Diff line change
Expand Up @@ -339,6 +339,9 @@ struct location_cellular_config {
* @ref location_config_defaults_set function is called and can be changed
* at build time with CONFIG_LOCATION_REQUEST_DEFAULT_CELLULAR_TIMEOUT configuration.
*
* Timeout only applies to neighbor cell search, not the cloud communication.
* Timeout for the entire location request specified in @ref location_config structure
* is still valid.
* When CONFIG_LOCATION_SERVICE_EXTERNAL is enabled, this timeout stops when
* event LOCATION_EVT_CLOUD_LOCATION_EXT_REQUEST is sent. However, timeout specified in
* @ref location_config structure is still valid.
Expand All @@ -354,6 +357,28 @@ struct location_cellular_config {
* This parameter is ignored when CONFIG_LOCATION_SERVICE_EXTERNAL is enabled.
*/
enum location_service service;

/**
* @brief Number of cells to be requested for cellular positioning.
*
* @details Default value is 4. It is applied when
* @ref location_config_defaults_set function is called and can be changed
* at build time with CONFIG_LOCATION_REQUEST_DEFAULT_CELLULAR_CELL_COUNT configuration.
*
* Maximum value is 15.
*
* Zero indicates that only normal neighbor cell search is performed but no GCI search.
*
* If there are less than requested number of neighbor cells (including current cell),
* GCI (surrounding) cells are requested also.
*
* Note that even if there are a lot of cells available, the number of cells
* used for positioning may be lower than the requested number of cells due to
* the behavior of the search algorithm. Also, the number of cells used for
* positioning may be higher than the requested number of cells if there are
* more neighbor cells (including current cell).
*/
uint8_t cell_count;
};

/** Wi-Fi positioning configuration. */
Expand All @@ -366,6 +391,10 @@ struct location_wifi_config {
* @ref location_config_defaults_set function is called and can be changed
* at build time with CONFIG_LOCATION_REQUEST_DEFAULT_WIFI_TIMEOUT configuration.
*
* Timeout only applies to Wi-Fi scan, not the cloud communication.
* Timeout for the entire location request specified in @ref location_config structure
* is still valid.
*
* When CONFIG_LOCATION_SERVICE_EXTERNAL is enabled, this timeout stops when
* event LOCATION_EVT_CLOUD_LOCATION_EXT_REQUEST is sent. However, timeout specified in
* @ref location_config structure is still valid.
Expand Down
8 changes: 8 additions & 0 deletions lib/location/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -279,6 +279,14 @@ config LOCATION_REQUEST_DEFAULT_CELLULAR_TIMEOUT
Default value used in location_config_defaults_set() function for timeout
member within location_cellular_config structure.

config LOCATION_REQUEST_DEFAULT_CELLULAR_CELL_COUNT
int "Number of requested cellular neighbors"
default 4
range 0 15
help
Default value used in location_config_defaults_set() function for cell_count
member within location_cellular_config structure.

endif # LOCATION_METHOD_CELLULAR

if LOCATION_METHOD_WIFI
Expand Down
1 change: 1 addition & 0 deletions lib/location/location.c
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,7 @@ static void location_config_method_defaults_set(
#if defined(CONFIG_LOCATION_METHOD_CELLULAR)
method->cellular.timeout = CONFIG_LOCATION_REQUEST_DEFAULT_CELLULAR_TIMEOUT;
method->cellular.service = LOCATION_SERVICE_ANY;
method->cellular.cell_count = CONFIG_LOCATION_REQUEST_DEFAULT_CELLULAR_CELL_COUNT;
#endif
} else if (method_type == LOCATION_METHOD_WIFI) {
#if defined(CONFIG_LOCATION_METHOD_WIFI)
Expand Down
3 changes: 3 additions & 0 deletions lib/location/location_core.c
Original file line number Diff line number Diff line change
Expand Up @@ -335,6 +335,7 @@ void location_core_config_log(const struct location_config *config)
LOG_DBG(" Service: %s (%d)",
location_core_service_str(config->methods[i].cellular.service),
config->methods[i].cellular.service);
LOG_DBG(" Cell count: %d", config->methods[i].cellular.cell_count);
#if defined(CONFIG_LOCATION_METHOD_WIFI)
} else if (type == LOCATION_METHOD_WIFI) {
LOG_DBG(" Timeout: %dms", config->methods[i].wifi.timeout);
Expand Down Expand Up @@ -365,6 +366,8 @@ static int location_core_location_get_pos(void)

location_core_current_config_set(&loc_req_info.config);
/* Location request starts from the first method */
loc_req_info.timeout_uptime = (loc_req_info.config.timeout != SYS_FOREVER_MS) ?
k_uptime_get() + loc_req_info.config.timeout : SYS_FOREVER_MS;
loc_req_info.execute_fallback = true;
loc_req_info.current_method_index = 0;
requested_method = loc_req_info.methods[loc_req_info.current_method_index];
Expand Down
6 changes: 6 additions & 0 deletions lib/location/location_core.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,12 @@ struct location_request_info {

/** Whether to perform fallback for current location request processing. */
bool execute_fallback;

/**
* Device uptime when location request timer expires.
* This is used in cloud location method to calculate timeout for the cloud operation.
*/
int64_t timeout_uptime;
};

struct location_method_api {
Expand Down
32 changes: 10 additions & 22 deletions lib/location/method_cloud_location.c
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ struct method_cloud_location_start_work_args {
struct k_work work_item;
const struct location_wifi_config *wifi_config;
const struct location_cellular_config *cell_config;
int64_t starting_uptime_ms;
int64_t locreq_timeout_uptime;
};

static struct method_cloud_location_start_work_args method_cloud_location_start_work;
Expand All @@ -44,19 +44,13 @@ static void method_cloud_location_positioning_work_fn(struct k_work *work)
const struct location_cellular_config *cell_config = work_data->cell_config;
struct wifi_scan_info *scan_wifi_info = NULL;
struct lte_lc_cells_info *scan_cellular_info = NULL;
int64_t scan_start_time;
int32_t used_timeout_ms;
int err = 0;
#if defined(CONFIG_LOCATION_METHOD_WIFI)
struct k_sem wifi_scan_ready;

k_sem_init(&wifi_scan_ready, 0, 1);
#endif
#if defined(CONFIG_LOCATION_METHOD_CELLULAR)
struct k_sem ncellmeas_ready;

k_sem_init(&ncellmeas_ready, 0, 1);
#endif

if (wifi_config != NULL && cell_config != NULL) {
used_timeout_ms = MIN(cell_config->timeout, wifi_config->timeout);
Expand All @@ -68,7 +62,6 @@ static void method_cloud_location_positioning_work_fn(struct k_work *work)
}

location_core_timer_start(used_timeout_ms);
scan_start_time = k_uptime_get();

#if defined(CONFIG_LOCATION_METHOD_WIFI)
if (wifi_config != NULL) {
Expand All @@ -78,8 +71,7 @@ static void method_cloud_location_positioning_work_fn(struct k_work *work)

#if defined(CONFIG_LOCATION_METHOD_CELLULAR)
if (cell_config != NULL) {
err = scan_cellular_start(&ncellmeas_ready);
k_sem_take(&ncellmeas_ready, K_FOREVER);
err = scan_cellular_start(cell_config->cell_count);
scan_cellular_info = scan_cellular_results_get();
}
#endif
Expand Down Expand Up @@ -118,12 +110,11 @@ static void method_cloud_location_positioning_work_fn(struct k_work *work)
#else
struct location_data location;
struct location_data location_result = { 0 };
int64_t scan_time;
struct cloud_service_pos_req params = {
.cell_data = scan_cellular_info,
.wifi_data = scan_wifi_info,
.service = (cell_config != NULL) ? cell_config->service : wifi_config->service,
.timeout_ms = used_timeout_ms
.timeout_ms = SYS_FOREVER_MS
};

if (!location_utils_is_default_pdn_active()) {
Expand All @@ -138,19 +129,16 @@ static void method_cloud_location_positioning_work_fn(struct k_work *work)
/* Scannings done at this point of time. Store current time to response. */
location_utils_systime_to_location_datetime(&location_result.datetime);

/* Calculate timeout for cloud request */
if (used_timeout_ms != SYS_FOREVER_MS) {
/* +1 to round the time up */
scan_time = (k_uptime_get() - scan_start_time) + 1;

/* Check if timeout has already elapsed */
if (scan_time >= used_timeout_ms) {
/* Timeout for cloud request is the remaining time from the location request timeout.
* Notice that it's not from the method timeout, which only applies to the scan procedure.
*/
if (work_data->locreq_timeout_uptime != SYS_FOREVER_MS) {
params.timeout_ms = work_data->locreq_timeout_uptime - k_uptime_get();
if (params.timeout_ms < 0) {
LOG_WRN("Timeout occurred during scannings");
err = -ETIMEDOUT;
goto end;
}
/* Take time used for neighbour cell measurements into account */
params.timeout_ms = used_timeout_ms - scan_time;
}

/* Request location from the cloud */
Expand Down Expand Up @@ -216,7 +204,7 @@ int method_cloud_location_get(const struct location_request_info *request)
method_cloud_location_start_work.wifi_config = request->wifi;
}

method_cloud_location_start_work.starting_uptime_ms = k_uptime_get();
method_cloud_location_start_work.locreq_timeout_uptime = request->timeout_uptime;
k_work_submit_to_queue(
location_core_work_queue_get(),
&method_cloud_location_start_work.work_item);
Expand Down
97 changes: 80 additions & 17 deletions lib/location/scan_cellular.c
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,17 @@
LOG_MODULE_DECLARE(location, CONFIG_LOCATION_LOG_LEVEL);

static struct lte_lc_ncell neighbor_cells[CONFIG_LTE_NEIGHBOR_CELLS_MAX];
static struct lte_lc_cell gci_cells[CONFIG_LTE_NEIGHBOR_CELLS_MAX];
static struct lte_lc_cells_info scan_cellular_info = {
.neighbor_cells = neighbor_cells
.neighbor_cells = neighbor_cells,
.gci_cells = gci_cells
};
static struct k_sem *scan_cellular_ready;

static bool running;
/* Indicates when individual ncellmeas operation is completed. This is internal to this file. */
static struct k_sem scan_cellular_sem_ncellmeas_evt;
/* Requested number of cells to be searched. */
static int8_t scan_cellular_cell_count;

struct lte_lc_cells_info *scan_cellular_results_get(void)
{
Expand All @@ -36,10 +43,11 @@ void scan_cellular_lte_ind_handler(const struct lte_lc_evt *const evt)
{
switch (evt->type) {
case LTE_LC_EVT_NEIGHBOR_CELL_MEAS: {
if (scan_cellular_ready == NULL) {
if (!running) {
return;
}
LOG_DBG("Cell measurements results received");
LOG_DBG("Cell measurements results received: ncells_count=%d, gci_cells_count=%d",
evt->cells_info.ncells_count, evt->cells_info.gci_cells_count);

/* Copy current cell information */
memcpy(&scan_cellular_info.current_cell,
Expand All @@ -54,30 +62,46 @@ void scan_cellular_lte_ind_handler(const struct lte_lc_evt *const evt)

scan_cellular_info.ncells_count = evt->cells_info.ncells_count;
} else {
scan_cellular_info.ncells_count = 0;
LOG_DBG("No neighbor cell information from modem.");
LOG_DBG("No neighbor cell information from modem");
}

k_sem_give(scan_cellular_ready);
scan_cellular_ready = NULL;
/* Copy surrounding cell information if present */
if (evt->cells_info.gci_cells_count > 0 && evt->cells_info.gci_cells) {
memcpy(scan_cellular_info.gci_cells,
evt->cells_info.gci_cells,
sizeof(struct lte_lc_cell) * evt->cells_info.gci_cells_count);

scan_cellular_info.gci_cells_count = evt->cells_info.gci_cells_count;
} else {
LOG_DBG("No surrounding cell information from modem");

}

k_sem_give(&scan_cellular_sem_ncellmeas_evt);
} break;
default:
break;
}
}

int scan_cellular_start(struct k_sem *ncellmeas_ready)
int scan_cellular_start(uint8_t cell_count)
{
struct location_utils_modem_params_info modem_params = { 0 };
struct lte_lc_ncellmeas_params ncellmeas_params = {
.search_type = LTE_LC_NEIGHBOR_SEARCH_TYPE_EXTENDED_COMPLETE,
.gci_count = 0
};
int err;

scan_cellular_ready = ncellmeas_ready;
running = true;
scan_cellular_cell_count = cell_count;
scan_cellular_info.current_cell.id = LTE_LC_CELL_EUTRAN_ID_INVALID;
scan_cellular_info.ncells_count = 0;
scan_cellular_info.gci_cells_count = 0;

LOG_DBG("Triggering cell measurements");

/* Starting measurements with lte_lc default parameters */
err = lte_lc_neighbor_cell_measurement(NULL);
err = lte_lc_neighbor_cell_measurement(&ncellmeas_params);
if (err) {
LOG_WRN("Failed to initiate neighbor cell measurements: %d, "
"next: fallback to get modem parameters",
Expand All @@ -99,23 +123,60 @@ int scan_cellular_start(struct k_sem *ncellmeas_ready)
scan_cellular_info.current_cell.id = modem_params.cell_id;
scan_cellular_info.current_cell.phys_cell_id = modem_params.phys_cell_id;
}
k_sem_give(scan_cellular_ready);
scan_cellular_ready = NULL;
goto end;
}
err = k_sem_take(&scan_cellular_sem_ncellmeas_evt, K_FOREVER);
if (err) {
/* Semaphore was reset so stop search procedure */
err = 0;
goto end;
}

/* Calculate the number of GCI cells to be requested.
* We should subtract 1 for current cell as ncell_count won't include it.
* GCI count requested from modem includes also current cell so we should add 1.
* So these two cancel each other.
*/
scan_cellular_cell_count = scan_cellular_cell_count - scan_cellular_info.ncells_count;

if (scan_cellular_cell_count > 0) {

ncellmeas_params.search_type = LTE_LC_NEIGHBOR_SEARCH_TYPE_GCI_EXTENDED_LIGHT;
ncellmeas_params.gci_count = scan_cellular_cell_count;

/* GCI count given to modem cannot be one */
if (ncellmeas_params.gci_count == 1) {
ncellmeas_params.gci_count++;
}

err = lte_lc_neighbor_cell_measurement(&ncellmeas_params);
if (err) {
LOG_WRN("Failed to initiate GCI cell measurements: %d", err);
/* Clearing 'err' because normal neighbor search has succeeded
* so those are still valid and positioning can procede with that data
*/
err = 0;
goto end;
}
k_sem_take(&scan_cellular_sem_ncellmeas_evt, K_FOREVER);
}

end:
running = false;
return err;
}

int scan_cellular_cancel(void)
{
if (scan_cellular_ready != NULL) {
if (running) {
/* Cancel/stopping might trigger a NCELLMEAS notification */
(void)lte_lc_neighbor_cell_measurement_cancel();
k_sem_reset(scan_cellular_ready);
k_sem_reset(&scan_cellular_sem_ncellmeas_evt);
} else {
return -EPERM;
}

scan_cellular_ready = NULL;
running = false;

return 0;
}
Expand All @@ -124,5 +185,7 @@ int scan_cellular_init(void)
{
lte_lc_register_handler(scan_cellular_lte_ind_handler);

k_sem_init(&scan_cellular_sem_ncellmeas_evt, 0, 1);

return 0;
}
2 changes: 1 addition & 1 deletion lib/location/scan_cellular.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
#include <modem/lte_lc.h>

int scan_cellular_init(void);
int scan_cellular_start(struct k_sem *ncellmeas_ready);
int scan_cellular_start(uint8_t cell_count);
struct lte_lc_cells_info *scan_cellular_results_get(void);
int scan_cellular_cancel(void);

Expand Down
Loading

0 comments on commit 0ae3662

Please sign in to comment.