Skip to content

Commit

Permalink
lib: location: Add new LOCATION_METHOD_WIFI_CELLULAR
Browse files Browse the repository at this point in the history
Combined method for Wi-Fi and cellular added as a method type
LOCATION_METHOD_WIFI_CELLULAR. This cannot be added to the method list
given to location_request() but will appear in the location event
when the methods are combined.

Jira: NCSDK-25618

Signed-off-by: Tommi Rantanen <[email protected]>
  • Loading branch information
trantanen authored and rlubos committed Feb 22, 2024
1 parent f4d1420 commit fc94a9f
Show file tree
Hide file tree
Showing 9 changed files with 75 additions and 115 deletions.
8 changes: 8 additions & 0 deletions doc/nrf/libraries/modem/location.rst
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,14 @@ The supported location methods are as follows:

The ``cloud location`` method handles the location methods (cellular and Wi-Fi positioning)
that scan for technology-specific information and sends it over to the cloud service for location resolution.
If the following conditions are met, Wi-Fi and cellular scan results are combined into a single cloud request:

* Methods are one after the other in the location request method list.
* Location request mode is :c:enum:`LOCATION_REQ_MODE_FALLBACK`.
* Requested cloud service for Wi-Fi and cellular is the same.

A special :c:enum:`LOCATION_METHOD_WIFI_CELLULAR` method can appear within the :c:struct:`location_event_data` structure,
but it cannot be added into the location configuration passed to the :c:func:`location_request` function.

The default priority order of location methods is GNSS positioning, Wi-Fi positioning and Cellular positioning.
If any of these methods are disabled, the method is simply omitted from the list.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -798,6 +798,8 @@ Modem libraries
These are for metrics collection purposes and sent only if the :kconfig:option:`CONFIG_LOCATION_DATA_DETAILS` Kconfig option is set.
* Support for multiple event handlers.
* Additional location data details into the :c:struct:`location_data_details` structure hierarchy.
* The :c:enumerator:`LOCATION_METHOD_WIFI_CELLULAR` method that cannot be added into the location configuration passed to the :c:func:`location_request()` function,
but may occur within the :c:struct:`location_event_data` structure.

* Updated:

Expand Down
25 changes: 16 additions & 9 deletions include/modem/location.h
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,19 @@ enum location_method {
LOCATION_METHOD_GNSS,
/** Wi-Fi positioning. */
LOCATION_METHOD_WIFI,
/**
* Wi-Fi and cellular positioning combined into one cloud request.
*
* Cannot be used in the method list passed to location_request(),
* but this method can appear in the location event (@ref location_event_data.id)
* to indicate that Wi-Fi and cellular positioning methods have been combined
* into a single cloud location method.
*
* If the combined method is desired, set Wi-Fi and cellular methods next to each other
* in the method list passed to location_request(). This also happens by default if
* NULL configuration is passed to location_request().
*/
LOCATION_METHOD_WIFI_CELLULAR,
};

/** Location acquisition mode. */
Expand Down Expand Up @@ -204,8 +217,8 @@ struct location_data_details_wifi {
*
* Only one of the child structures is filled most of the time depending on the method
* found in @ref location_event_data.method member of the parent structure.
* There are cases when cellular and Wi-Fi data are combined into a single cloud request
* in which case data details for both methods are filled.
* For a combined method @ref LOCATION_METHOD_WIFI_CELLULAR both @ref cellular and
* @ref wifi are filled.
*/
struct location_data_details {
/**
Expand Down Expand Up @@ -285,13 +298,7 @@ struct location_data_cloud {
struct location_event_data {
/** Event ID. */
enum location_event_id id;
/**
* Used location method.
*
* When cellular and Wi-Fi positioning are used and they are combined into a single
* cloud request by the library, the method is not known so there is some uncertainty
* on the reported location method.
*/
/** Used location method. */
enum location_method method;

/** Event specific data. */
Expand Down
11 changes: 4 additions & 7 deletions lib/location/location.c
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ static bool initialized;
static const char LOCATION_METHOD_CELLULAR_STR[] = "Cellular";
static const char LOCATION_METHOD_GNSS_STR[] = "GNSS";
static const char LOCATION_METHOD_WIFI_STR[] = "Wi-Fi";
static const char LOCATION_METHOD_INTERNAL_WIFI_CELLULAR_STR[] = "Wi-Fi + Cellular";
static const char LOCATION_METHOD_WIFI_CELLULAR_STR[] = "Wi-Fi + Cellular";
static const char LOCATION_METHOD_UNKNOWN_STR[] = "Unknown";

int location_handler_register(location_event_handler_t handler)
Expand Down Expand Up @@ -299,13 +299,10 @@ const char *location_method_str(enum location_method method)
case LOCATION_METHOD_WIFI:
return LOCATION_METHOD_WIFI_STR;

case LOCATION_METHOD_WIFI_CELLULAR:
return LOCATION_METHOD_WIFI_CELLULAR_STR;

default:
/* Wi-Fi + Cellular method cannot be checked in switch-case because
* it's not defined in the enum
*/
if (method == LOCATION_METHOD_INTERNAL_WIFI_CELLULAR) {
return LOCATION_METHOD_INTERNAL_WIFI_CELLULAR_STR;
}
return LOCATION_METHOD_UNKNOWN_STR;
}
}
Expand Down
112 changes: 24 additions & 88 deletions lib/location/location_core.c
Original file line number Diff line number Diff line change
Expand Up @@ -122,8 +122,8 @@ static const struct location_method_api method_wifi_api = {
* into a single cloud request. This method cannot be chosen in the library API.
*/
static const struct location_method_api method_cloud_location_api = {
.method = LOCATION_METHOD_INTERNAL_WIFI_CELLULAR,
.method_string = "Wi-Fi + Cellular (internal)",
.method = LOCATION_METHOD_WIFI_CELLULAR,
.method_string = "Wi-Fi + Cellular",
.init = method_cloud_location_init,
.location_get = method_cloud_location_get,
.cancel = method_cloud_location_cancel,
Expand Down Expand Up @@ -281,6 +281,10 @@ int location_core_validate_params(const struct location_config *config)
}

for (int i = 0; i < config->methods_count; i++) {
if (config->methods[i].method == LOCATION_METHOD_WIFI_CELLULAR) {
LOG_ERR("LOCATION_METHOD_WIFI_CELLULAR cannot be given in location config");
return -EINVAL;
}
/* Check if the method is valid */
method_api = location_method_api_get(config->methods[i].method);
if (method_api == NULL) {
Expand Down Expand Up @@ -376,10 +380,7 @@ static int location_core_location_get_pos(void)
if (IS_ENABLED(CONFIG_LOCATION_DATA_DETAILS)) {
struct location_event_data request_started = {
.id = LOCATION_EVT_STARTED,
/* Use the first method from the config to avoid putting internal combined
* Wi-Fi and cellular method into the event.
*/
.method = loc_req_info.config.methods[0].method
.method = requested_method
};

location_utils_event_dispatch(&request_started);
Expand Down Expand Up @@ -450,7 +451,7 @@ static void location_request_info_create(const struct location_config *config)
if (!combined) {
LOG_INF("Wi-Fi and cellular methods combined");
loc_req_info.methods[loc_req_info.methods_count] =
LOCATION_METHOD_INTERNAL_WIFI_CELLULAR;
LOCATION_METHOD_WIFI_CELLULAR;
loc_req_info.methods_count++;
combined = true;
}
Expand Down Expand Up @@ -624,80 +625,6 @@ static void location_core_event_details_get(struct location_event_data *event)
#endif
}

static void location_core_event_cb_fallback(
enum location_method current_method,
enum location_method next_method,
enum location_event_id current_event,
struct location_data_details *details)
{
#if defined(CONFIG_LOCATION_DATA_DETAILS)
if (current_method == LOCATION_METHOD_INTERNAL_WIFI_CELLULAR) {
/* When fallback happens, current method is always the 1st one whenever
* combined Wi-Fi and cellular method is used.
* For example, method list is Wi-Fi, Cellular, GNSS, we use Wi-Fi.
* If the Wi-Fi and cellular are not the first two methods in some order,
* we won't be in fallback from the combined Wi-Fi and cellular method.
* Note: If CONFIG_LOCATION_METHODS_LIST_SIZE is increased, this won't work anymore.
*/
current_method = loc_req_info.config.methods[0].method;
}
if (next_method == LOCATION_METHOD_INTERNAL_WIFI_CELLULAR) {
/* When fallback happens, next method is always the 2nd one whenever
* combined Wi-Fi and cellular method is used.
* For example, method list is GNSS, Cellular, Wi-Fi, we use Cellular.
* If the Wi-Fi and cellular are not the 2nd and 3rd methods in some order,
* we won't be in fallback into the combined Wi-Fi and cellular method.
* Note: If CONFIG_LOCATION_METHODS_LIST_SIZE is increased, this won't work anymore.
*/
next_method = loc_req_info.config.methods[1].method;
}

struct location_event_data fallback = {
.id = LOCATION_EVT_FALLBACK,
.method = current_method,
.fallback = {
.next_method = next_method,
.cause = current_event,
.details = *details,
}
};

location_utils_event_dispatch(&fallback);
#endif
}

enum location_method location_core_event_method_resolve(enum location_method method)
{
#if defined(CONFIG_LOCATION_METHOD_CELLULAR) && defined(CONFIG_LOCATION_METHOD_WIFI)
/* Other than combined Wi-Fi + cellular method is used as is */
if (method != LOCATION_METHOD_INTERNAL_WIFI_CELLULAR) {
return method;
}

/* If Wi-Fi and cellular were requested and location accuracy is available */
if (loc_req_info.wifi != NULL && loc_req_info.cellular != NULL &&
loc_req_info.current_event_data.location.accuracy > 0) {

/* If accuracy is higher than Wi-Fi threshold, use cellular, otherwise Wi-Fi */
if (loc_req_info.current_event_data.location.accuracy >
METHOD_WIFI_ACCURACY_THRESHOLD) {
return LOCATION_METHOD_CELLULAR;
} else {
return LOCATION_METHOD_WIFI;
}
}

/* If Wi-Fi was requested, use Wi-Fi, otherwise cellular */
if (loc_req_info.wifi != NULL) {
return LOCATION_METHOD_WIFI;
} else {
return LOCATION_METHOD_CELLULAR;
}
#else
return method;
#endif
}

static void location_core_event_cb_fn(struct k_work *work)
{
char latitude_str[12];
Expand All @@ -707,8 +634,7 @@ static void location_core_event_cb_fn(struct k_work *work)
int err;

k_work_cancel_delayable(&location_core_method_timeout_work);
loc_req_info.current_event_data.method =
location_core_event_method_resolve(loc_req_info.current_method);
loc_req_info.current_event_data.method = loc_req_info.current_method;

/* Update the event structure with the details of the current method */
location_core_event_details_get(&loc_req_info.current_event_data);
Expand Down Expand Up @@ -790,13 +716,23 @@ static void location_core_event_cb_fn(struct k_work *work)
* also for failure events
*/
location_utils_event_dispatch(&loc_req_info.current_event_data);
#if defined(CONFIG_LOCATION_DATA_DETAILS)
} else {
/* Details had been set into the error information */
location_core_event_cb_fallback(
loc_req_info.current_method,
requested_method,
loc_req_info.current_event_data.id,
&loc_req_info.current_event_data.error.details);
struct location_data_details *details =
&loc_req_info.current_event_data.error.details;

struct location_event_data fallback = {
.id = LOCATION_EVT_FALLBACK,
.method = loc_req_info.current_method,
.fallback = {
.next_method = requested_method,
.cause = loc_req_info.current_event_data.id,
.details = *details,
}
};
location_utils_event_dispatch(&fallback);
#endif
}

location_core_current_event_data_init(requested_method);
Expand Down
2 changes: 0 additions & 2 deletions lib/location/location_core.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,6 @@
#ifndef LOCATION_CORE_H
#define LOCATION_CORE_H

#define LOCATION_METHOD_INTERNAL_WIFI_CELLULAR 100

/** Information required to carry out a location request. */
struct location_request_info {
const struct location_wifi_config *wifi;
Expand Down
4 changes: 2 additions & 2 deletions lib/location/method_cloud_location.c
Original file line number Diff line number Diff line change
Expand Up @@ -179,11 +179,11 @@ int method_cloud_location_get(const struct location_request_info *request)
method_cloud_location_start_work.wifi_config = NULL;
method_cloud_location_start_work.cell_config = NULL;
if (request->current_method == LOCATION_METHOD_CELLULAR ||
request->current_method == LOCATION_METHOD_INTERNAL_WIFI_CELLULAR) {
request->current_method == LOCATION_METHOD_WIFI_CELLULAR) {
method_cloud_location_start_work.cell_config = request->cellular;
}
if (request->current_method == LOCATION_METHOD_WIFI ||
request->current_method == LOCATION_METHOD_INTERNAL_WIFI_CELLULAR) {
request->current_method == LOCATION_METHOD_WIFI_CELLULAR) {
method_cloud_location_start_work.wifi_config = request->wifi;
}

Expand Down
4 changes: 2 additions & 2 deletions samples/cellular/modem_shell/src/location/location_shell.c
Original file line number Diff line number Diff line change
Expand Up @@ -236,13 +236,13 @@ static void location_print_data_details(
}
#endif
#if defined(CONFIG_LOCATION_METHOD_CELLULAR)
if (method == LOCATION_METHOD_CELLULAR) {
if (method == LOCATION_METHOD_CELLULAR || method == LOCATION_METHOD_WIFI_CELLULAR) {
mosh_print(" neighbor cells: %d", details->cellular.ncells_count);
mosh_print(" GCI cells: %d", details->cellular.gci_cells_count);
}
#endif
#if defined(CONFIG_LOCATION_METHOD_WIFI)
if (method == LOCATION_METHOD_WIFI) {
if (method == LOCATION_METHOD_WIFI || method == LOCATION_METHOD_WIFI_CELLULAR) {
mosh_print(" Wi-Fi APs: %d", details->wifi.ap_count);
}
#endif
Expand Down
22 changes: 17 additions & 5 deletions tests/lib/location/src/location_test.c
Original file line number Diff line number Diff line change
Expand Up @@ -504,11 +504,6 @@ void test_location_method_str(void)

method = location_method_str(99);
TEST_ASSERT_EQUAL_STRING("Unknown", method);

/* 100 is a hidden internally used LOCATION_METHOD_INTERNAL_WIFI_CELLULAR constant */
method = location_method_str(100);
TEST_ASSERT_EQUAL_STRING("Wi-Fi + Cellular", method);

}

/********* GNSS POSITIONING TESTS ***********************/
Expand Down Expand Up @@ -1352,6 +1347,23 @@ void test_error_too_many_methods(void)
TEST_ASSERT_EQUAL(-EINVAL, err);
}

/* Test location request with LOCATION_METHOD_WIFI_CELLULAR in method list. */
void test_error_wifi_cellular_method(void)
{
int err;
struct location_config config = { 0 };
enum location_method methods[] = {
LOCATION_METHOD_GNSS,
LOCATION_METHOD_WIFI_CELLULAR,
LOCATION_METHOD_CELLULAR};

/* Check that location_config_defaults_set returns an error with too many methods */
location_config_defaults_set(&config, 3, methods);

err = location_request(&config);
TEST_ASSERT_EQUAL(-EINVAL, err);
}

/* Test cancelling location request when there is no pending location request. */
void test_error_cancel_no_operation(void)
{
Expand Down

0 comments on commit fc94a9f

Please sign in to comment.