diff --git a/doc/nrf/libraries/modem/location.rst b/doc/nrf/libraries/modem/location.rst index 413682aa6ec3..bae506253fb3 100644 --- a/doc/nrf/libraries/modem/location.rst +++ b/doc/nrf/libraries/modem/location.rst @@ -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. diff --git a/doc/nrf/releases_and_maturity/releases/release-notes-changelog.rst b/doc/nrf/releases_and_maturity/releases/release-notes-changelog.rst index e9288f289924..f72a01af91b3 100644 --- a/doc/nrf/releases_and_maturity/releases/release-notes-changelog.rst +++ b/doc/nrf/releases_and_maturity/releases/release-notes-changelog.rst @@ -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: diff --git a/include/modem/location.h b/include/modem/location.h index 1ac23aa96deb..643aaa6f7bd5 100644 --- a/include/modem/location.h +++ b/include/modem/location.h @@ -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. */ @@ -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 { /** @@ -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. */ diff --git a/lib/location/location.c b/lib/location/location.c index 0b41f3eae0e2..93c1e28adb22 100644 --- a/lib/location/location.c +++ b/lib/location/location.c @@ -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) @@ -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; } } diff --git a/lib/location/location_core.c b/lib/location/location_core.c index 764ccac94c5b..6fcb6f2b2604 100644 --- a/lib/location/location_core.c +++ b/lib/location/location_core.c @@ -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, @@ -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) { @@ -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); @@ -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; } @@ -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]; @@ -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); @@ -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); diff --git a/lib/location/location_core.h b/lib/location/location_core.h index b5cae3fd40e3..cd62d8aa9ee5 100644 --- a/lib/location/location_core.h +++ b/lib/location/location_core.h @@ -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; diff --git a/lib/location/method_cloud_location.c b/lib/location/method_cloud_location.c index 20349d4e3893..8ef8c57c31a2 100644 --- a/lib/location/method_cloud_location.c +++ b/lib/location/method_cloud_location.c @@ -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; } diff --git a/samples/cellular/modem_shell/src/location/location_shell.c b/samples/cellular/modem_shell/src/location/location_shell.c index 06385978a87f..a48d8454b645 100644 --- a/samples/cellular/modem_shell/src/location/location_shell.c +++ b/samples/cellular/modem_shell/src/location/location_shell.c @@ -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 diff --git a/tests/lib/location/src/location_test.c b/tests/lib/location/src/location_test.c index 119a11e2a4e5..7b08a8861c27 100644 --- a/tests/lib/location/src/location_test.c +++ b/tests/lib/location/src/location_test.c @@ -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 ***********************/ @@ -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) {