From 065b9750c59fa2d325e3a92d479811194d0d2238 Mon Sep 17 00:00:00 2001 From: Jordan Yates Date: Fri, 17 Oct 2025 11:14:30 +1000 Subject: [PATCH 1/6] gnss: gnss_emul: manual data update mode Add an option for the GNSS fix state reported by the emulated GNSS modem to be manually configured by test code, instead of being hardcoded to always achieve a fix of hardcoded parameters after 5 seconds. Signed-off-by: Jordan Yates --- drivers/gnss/Kconfig.emul | 9 ++++ drivers/gnss/gnss_emul.c | 63 +++++++++++++++++-------- include/zephyr/drivers/gnss/gnss_emul.h | 33 +++++++++++++ 3 files changed, 85 insertions(+), 20 deletions(-) create mode 100644 include/zephyr/drivers/gnss/gnss_emul.h diff --git a/drivers/gnss/Kconfig.emul b/drivers/gnss/Kconfig.emul index b6667f36864bd..cb3cfdd3e6647 100644 --- a/drivers/gnss/Kconfig.emul +++ b/drivers/gnss/Kconfig.emul @@ -9,3 +9,12 @@ config GNSS_EMUL select TIMEOUT_64BIT help Enable emulated GNSS driver. + +config GNSS_EMUL_MANUAL_UPDATE + bool "Internal state manually updated through gnss_emul_set_data" + depends on GNSS_EMUL + help + The internal state of the GNSS emulator (location, time, etc) + must be updated through gnss_emul_set_data, instead of automatically + transitioning to hardcoded states at hardcoded times. Once the current + time is set, the published time automatically increments. diff --git a/drivers/gnss/gnss_emul.c b/drivers/gnss/gnss_emul.c index ac1761d84c855..b61cb60dd7feb 100644 --- a/drivers/gnss/gnss_emul.c +++ b/drivers/gnss/gnss_emul.c @@ -73,15 +73,6 @@ static void gnss_emul_update_fix_timestamp(const struct device *dev, bool resumi } } -static bool gnss_emul_fix_is_acquired(const struct device *dev) -{ - struct gnss_emul_data *data = dev->data; - int64_t time_since_resume; - - time_since_resume = data->fix_timestamp_ms - data->resume_timestamp_ms; - return time_since_resume >= GNSS_EMUL_FIX_ACQUIRE_TIME_MS; -} - #ifdef CONFIG_PM_DEVICE static void gnss_emul_clear_fix_timestamp(const struct device *dev) { @@ -346,23 +337,13 @@ static DEVICE_API(gnss, api) = { .get_supported_systems = gnss_emul_api_get_supported_systems, }; -static void gnss_emul_clear_data(const struct device *dev) +void gnss_emul_clear_data(const struct device *dev) { struct gnss_emul_data *data = dev->data; memset(&data->data, 0, sizeof(data->data)); } -static void gnss_emul_set_fix(const struct device *dev) -{ - struct gnss_emul_data *data = dev->data; - - data->data.info.satellites_cnt = 8; - data->data.info.hdop = 100; - data->data.info.fix_status = GNSS_FIX_STATUS_GNSS_FIX; - data->data.info.fix_quality = GNSS_FIX_QUALITY_GNSS_SPS; -} - static void gnss_emul_set_utc(const struct device *dev) { struct gnss_emul_data *data = dev->data; @@ -384,6 +365,40 @@ static void gnss_emul_set_utc(const struct device *dev) data->data.utc.century_year = datetime.tm_year % 100; } +#ifdef CONFIG_GNSS_EMUL_MANUAL_UPDATE + +void gnss_emul_set_data(const struct device *dev, const struct navigation_data *nav, + const struct gnss_info *info, int64_t timestamp_ms) +{ + struct gnss_emul_data *data = dev->data; + + data->data.nav_data = *nav; + data->data.info = *info; + data->fix_timestamp_ms = timestamp_ms; + gnss_emul_set_utc(dev); +} + +#else + +static bool gnss_emul_fix_is_acquired(const struct device *dev) +{ + struct gnss_emul_data *data = dev->data; + int64_t time_since_resume; + + time_since_resume = data->fix_timestamp_ms - data->resume_timestamp_ms; + return time_since_resume >= GNSS_EMUL_FIX_ACQUIRE_TIME_MS; +} + +static void gnss_emul_set_fix(const struct device *dev) +{ + struct gnss_emul_data *data = dev->data; + + data->data.info.satellites_cnt = 8; + data->data.info.hdop = 100; + data->data.info.fix_status = GNSS_FIX_STATUS_GNSS_FIX; + data->data.info.fix_quality = GNSS_FIX_QUALITY_GNSS_SPS; +} + static void gnss_emul_set_nav_data(const struct device *dev) { struct gnss_emul_data *data = dev->data; @@ -395,6 +410,8 @@ static void gnss_emul_set_nav_data(const struct device *dev) data->data.nav_data.altitude = 20000; } +#endif /* CONFIG_GNSS_EMUL_MANUAL_UPDATE */ + #ifdef CONFIG_GNSS_SATELLITES static void gnss_emul_clear_satellites(const struct device *dev) { @@ -444,6 +461,11 @@ static void gnss_emul_work_handler(struct k_work *work) struct gnss_emul_data *data = CONTAINER_OF(dwork, struct gnss_emul_data, data_dwork); const struct device *dev = data->dev; +#ifdef CONFIG_GNSS_EMUL_MANUAL_UPDATE + /* Tick the timestamp */ + gnss_emul_set_utc(dev); +#else + /* Automatically update internal state if not done manually */ if (!gnss_emul_fix_is_acquired(dev)) { gnss_emul_clear_data(dev); } else { @@ -451,6 +473,7 @@ static void gnss_emul_work_handler(struct k_work *work) gnss_emul_set_utc(dev); gnss_emul_set_nav_data(dev); } +#endif /* CONFIG_GNSS_EMUL_MANUAL_UPDATE */ gnss_publish_data(dev, &data->data); diff --git a/include/zephyr/drivers/gnss/gnss_emul.h b/include/zephyr/drivers/gnss/gnss_emul.h new file mode 100644 index 0000000000000..023642c933ae3 --- /dev/null +++ b/include/zephyr/drivers/gnss/gnss_emul.h @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2025 Embeint Pty Ltd + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef ZEPHYR_DRIVERS_GNSS_GNSS_EMUL_H_ +#define ZEPHYR_DRIVERS_GNSS_GNSS_EMUL_H_ + +#include + +#include +#include + +/** + * @brief Clear all internal GNSS data of the emulator + * + * @param dev GNSS emulator device + */ +void gnss_emul_clear_data(const struct device *dev); + +/** + * @brief Set the internal GNSS data of the emulator + * + * @param dev GNSS emulator device + * @param nav Updated navigation state + * @param info Updated GNSS fix information + * @param timestamp_ms Timestamp associated with the GNSS fix + */ +void gnss_emul_set_data(const struct device *dev, const struct navigation_data *nav, + const struct gnss_info *info, int64_t timestamp_ms); + +#endif /* ZEPHYR_DRIVERS_GNSS_GNSS_EMUL_H_ */ From 2467679d64fde554789a974426cda329f21d6dfc Mon Sep 17 00:00:00 2001 From: Jordan Yates Date: Fri, 17 Oct 2025 11:19:33 +1000 Subject: [PATCH 2/6] gnss: gnss_emul: init with `pm_device_driver_init` Remove the custom initialisation logic that attempts to duplicate `pm_device_driver_init`. Signed-off-by: Jordan Yates --- drivers/gnss/gnss_emul.c | 93 ++++++++++--------------- dts/bindings/gnss/zephyr,gnss-emul.yaml | 2 + 2 files changed, 37 insertions(+), 58 deletions(-) diff --git a/drivers/gnss/gnss_emul.c b/drivers/gnss/gnss_emul.c index b61cb60dd7feb..e898e8e90ddac 100644 --- a/drivers/gnss/gnss_emul.c +++ b/drivers/gnss/gnss_emul.c @@ -73,15 +73,6 @@ static void gnss_emul_update_fix_timestamp(const struct device *dev, bool resumi } } -#ifdef CONFIG_PM_DEVICE -static void gnss_emul_clear_fix_timestamp(const struct device *dev) -{ - struct gnss_emul_data *data = dev->data; - - data->fix_timestamp_ms = 0; -} -#endif - static void gnss_emul_schedule_work(const struct device *dev) { struct gnss_emul_data *data = dev->data; @@ -181,42 +172,6 @@ static int gnss_emul_get_enabled_systems(const struct device *dev, gnss_systems_ return 0; } -#ifdef CONFIG_PM_DEVICE -static void gnss_emul_resume(const struct device *dev) -{ - gnss_emul_update_fix_timestamp(dev, true); -} - -static void gnss_emul_suspend(const struct device *dev) -{ - gnss_emul_clear_fix_timestamp(dev); -} - -static int gnss_emul_pm_action(const struct device *dev, enum pm_device_action action) -{ - int ret = 0; - - gnss_emul_lock(dev); - - switch (action) { - case PM_DEVICE_ACTION_SUSPEND: - gnss_emul_suspend(dev); - break; - - case PM_DEVICE_ACTION_RESUME: - gnss_emul_resume(dev); - break; - - default: - ret = -ENOTSUP; - break; - } - - gnss_emul_unlock(dev); - return ret; -} -#endif - static int gnss_emul_api_set_fix_rate(const struct device *dev, uint32_t fix_interval_ms) { int ret = -ENODEV; @@ -486,27 +441,49 @@ static void gnss_emul_work_handler(struct k_work *work) gnss_emul_schedule_work(dev); } -static void gnss_emul_init_data(const struct device *dev) +static void gnss_emul_resume(const struct device *dev) { - struct gnss_emul_data *data = dev->data; + gnss_emul_update_fix_timestamp(dev, true); +} - data->dev = dev; - k_sem_init(&data->lock, 1, 1); - k_work_init_delayable(&data->data_dwork, gnss_emul_work_handler); +static void gnss_emul_suspend(const struct device *dev) +{ + gnss_emul_clear_data(dev); } -static int gnss_emul_init(const struct device *dev) +static int gnss_emul_pm_action(const struct device *dev, enum pm_device_action action) { - gnss_emul_init_data(dev); + int ret = 0; - if (pm_device_is_powered(dev)) { - gnss_emul_update_fix_timestamp(dev, true); - gnss_emul_schedule_work(dev); - } else { - pm_device_init_off(dev); + gnss_emul_lock(dev); + + switch (action) { + case PM_DEVICE_ACTION_SUSPEND: + gnss_emul_suspend(dev); + break; + + case PM_DEVICE_ACTION_RESUME: + gnss_emul_resume(dev); + break; + + default: + ret = -ENOTSUP; + break; } - return pm_device_runtime_enable(dev); + gnss_emul_unlock(dev); + return ret; +} + +static int gnss_emul_init(const struct device *dev) +{ + struct gnss_emul_data *data = dev->data; + + data->dev = dev; + k_sem_init(&data->lock, 1, 1); + k_work_init_delayable(&data->data_dwork, gnss_emul_work_handler); + + return pm_device_driver_init(dev, gnss_emul_pm_action); } #define GNSS_EMUL_NAME(inst, name) _CONCAT(name, inst) diff --git a/dts/bindings/gnss/zephyr,gnss-emul.yaml b/dts/bindings/gnss/zephyr,gnss-emul.yaml index eb7d9eec8acc3..10c4659dc0785 100644 --- a/dts/bindings/gnss/zephyr,gnss-emul.yaml +++ b/dts/bindings/gnss/zephyr,gnss-emul.yaml @@ -4,3 +4,5 @@ description: Zephyr emulated GNSS device compatible: "zephyr,gnss-emul" + +include: base.yaml From 6c5034f047bb1723b2980da10dd3e631182f64f6 Mon Sep 17 00:00:00 2001 From: Jordan Yates Date: Fri, 17 Oct 2025 14:14:24 +1000 Subject: [PATCH 3/6] gnss: gnss_emul: halt callbacks when not ACTIVE Ensure that callbacks do not continue to fire after `ACTION_SUSPEND`. Signed-off-by: Jordan Yates --- drivers/gnss/gnss_emul.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/drivers/gnss/gnss_emul.c b/drivers/gnss/gnss_emul.c index e898e8e90ddac..d7d66b5c0ed4e 100644 --- a/drivers/gnss/gnss_emul.c +++ b/drivers/gnss/gnss_emul.c @@ -39,6 +39,7 @@ struct gnss_emul_data { enum gnss_navigation_mode nav_mode; gnss_systems_t enabled_systems; struct gnss_data data; + bool active; #ifdef CONFIG_GNSS_SATELLITES struct gnss_satellite satellites[GNSS_EMUL_SUPPORTED_SYSTEMS_COUNT]; @@ -92,7 +93,7 @@ static bool gnss_emul_is_resumed(const struct device *dev) { struct gnss_emul_data *data = dev->data; - return data->fix_timestamp_ms > 0; + return data->active; } static void gnss_emul_lock(const struct device *dev) @@ -443,11 +444,19 @@ static void gnss_emul_work_handler(struct k_work *work) static void gnss_emul_resume(const struct device *dev) { + struct gnss_emul_data *data = dev->data; + + data->active = true; gnss_emul_update_fix_timestamp(dev, true); + gnss_emul_schedule_work(dev); } static void gnss_emul_suspend(const struct device *dev) { + struct gnss_emul_data *data = dev->data; + + data->active = false; + gnss_emul_cancel_work(dev); gnss_emul_clear_data(dev); } From cd0274a905cf6facde57e0d6f832f3846f0ce0ed Mon Sep 17 00:00:00 2001 From: Jordan Yates Date: Fri, 17 Oct 2025 14:19:54 +1000 Subject: [PATCH 4/6] gnss: gnss_emul: allow direct config queries Provide an escape hatch from the GNSS API requirement that a device be active to run the configuration `get` functions. This is useful in the context of an emulator device to query how other software modules have configured the GNSS. Signed-off-by: Jordan Yates --- drivers/gnss/gnss_emul.c | 7 +++--- include/zephyr/drivers/gnss/gnss_emul.h | 30 +++++++++++++++++++++++++ 2 files changed, 33 insertions(+), 4 deletions(-) diff --git a/drivers/gnss/gnss_emul.c b/drivers/gnss/gnss_emul.c index d7d66b5c0ed4e..7c0310d8c4492 100644 --- a/drivers/gnss/gnss_emul.c +++ b/drivers/gnss/gnss_emul.c @@ -123,7 +123,7 @@ static int gnss_emul_set_fix_rate(const struct device *dev, uint32_t fix_interva return 0; } -static int gnss_emul_get_fix_rate(const struct device *dev, uint32_t *fix_interval_ms) +int gnss_emul_get_fix_rate(const struct device *dev, uint32_t *fix_interval_ms) { struct gnss_emul_data *data = dev->data; @@ -144,8 +144,7 @@ static int gnss_emul_set_navigation_mode(const struct device *dev, return 0; } -static int gnss_emul_get_navigation_mode(const struct device *dev, - enum gnss_navigation_mode *mode) +int gnss_emul_get_navigation_mode(const struct device *dev, enum gnss_navigation_mode *mode) { struct gnss_emul_data *data = dev->data; @@ -165,7 +164,7 @@ static int gnss_emul_set_enabled_systems(const struct device *dev, gnss_systems_ return 0; } -static int gnss_emul_get_enabled_systems(const struct device *dev, gnss_systems_t *systems) +int gnss_emul_get_enabled_systems(const struct device *dev, gnss_systems_t *systems) { struct gnss_emul_data *data = dev->data; diff --git a/include/zephyr/drivers/gnss/gnss_emul.h b/include/zephyr/drivers/gnss/gnss_emul.h index 023642c933ae3..dc5802ee40c70 100644 --- a/include/zephyr/drivers/gnss/gnss_emul.h +++ b/include/zephyr/drivers/gnss/gnss_emul.h @@ -30,4 +30,34 @@ void gnss_emul_clear_data(const struct device *dev); void gnss_emul_set_data(const struct device *dev, const struct navigation_data *nav, const struct gnss_info *info, int64_t timestamp_ms); +/** + * @brief Retrieve the last configured fix rate, regardless of PM state + * + * @param dev GNSS emulator device + * @param fix_interval_ms Output fix interval + * + * @retval 0 On success + */ +int gnss_emul_get_fix_rate(const struct device *dev, uint32_t *fix_interval_ms); + +/** + * @brief Retrieve the last configured navigation mode, regardless of PM state + * + * @param dev GNSS emulator device + * @param mode Output navigation mode + * + * @retval 0 On success + */ +int gnss_emul_get_navigation_mode(const struct device *dev, enum gnss_navigation_mode *mode); + +/** + * @brief Retrieve the last configured systems, regardless of PM state + * + * @param dev GNSS emulator device + * @param systems Output GNSS systems + * + * @retval 0 On success + */ +int gnss_emul_get_enabled_systems(const struct device *dev, gnss_systems_t *systems); + #endif /* ZEPHYR_DRIVERS_GNSS_GNSS_EMUL_H_ */ From b7fea81405fd0ab986f4e975ddc02594e10d9b7e Mon Sep 17 00:00:00 2001 From: Jordan Yates Date: Fri, 17 Oct 2025 15:29:15 +1000 Subject: [PATCH 5/6] gnss: gnss_emul: decouple realtime and gnss time Allow the emulator to be set to an arbitrary UTC time, instead of being locked to reporting the current system uptime as UTC. Signed-off-by: Jordan Yates --- drivers/gnss/gnss_emul.c | 12 +++++++----- include/zephyr/drivers/gnss/gnss_emul.h | 4 ++-- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/drivers/gnss/gnss_emul.c b/drivers/gnss/gnss_emul.c index 7c0310d8c4492..5c706c9ce105f 100644 --- a/drivers/gnss/gnss_emul.c +++ b/drivers/gnss/gnss_emul.c @@ -35,6 +35,7 @@ struct gnss_emul_data { struct k_sem lock; int64_t resume_timestamp_ms; int64_t fix_timestamp_ms; + int64_t boot_realtime_ms; uint32_t fix_interval_ms; enum gnss_navigation_mode nav_mode; gnss_systems_t enabled_systems; @@ -302,15 +303,16 @@ void gnss_emul_clear_data(const struct device *dev) static void gnss_emul_set_utc(const struct device *dev) { struct gnss_emul_data *data = dev->data; + int64_t timestamp_realtime; time_t timestamp; struct tm datetime; uint16_t millisecond; - timestamp = (time_t)(data->fix_timestamp_ms / 1000); + timestamp_realtime = data->boot_realtime_ms + data->fix_timestamp_ms; + timestamp = (time_t)(timestamp_realtime / 1000); gmtime_r(×tamp, &datetime); - millisecond = (uint16_t)(data->fix_timestamp_ms % 1000) - + (uint16_t)(datetime.tm_sec * 1000); + millisecond = (uint16_t)(timestamp_realtime % 1000) + (uint16_t)(datetime.tm_sec * 1000); data->data.utc.hour = datetime.tm_hour; data->data.utc.millisecond = millisecond; @@ -323,13 +325,13 @@ static void gnss_emul_set_utc(const struct device *dev) #ifdef CONFIG_GNSS_EMUL_MANUAL_UPDATE void gnss_emul_set_data(const struct device *dev, const struct navigation_data *nav, - const struct gnss_info *info, int64_t timestamp_ms) + const struct gnss_info *info, int64_t boot_realtime_ms) { struct gnss_emul_data *data = dev->data; data->data.nav_data = *nav; data->data.info = *info; - data->fix_timestamp_ms = timestamp_ms; + data->boot_realtime_ms = boot_realtime_ms; gnss_emul_set_utc(dev); } diff --git a/include/zephyr/drivers/gnss/gnss_emul.h b/include/zephyr/drivers/gnss/gnss_emul.h index dc5802ee40c70..4416ce26cfed6 100644 --- a/include/zephyr/drivers/gnss/gnss_emul.h +++ b/include/zephyr/drivers/gnss/gnss_emul.h @@ -25,10 +25,10 @@ void gnss_emul_clear_data(const struct device *dev); * @param dev GNSS emulator device * @param nav Updated navigation state * @param info Updated GNSS fix information - * @param timestamp_ms Timestamp associated with the GNSS fix + * @param boot_realtime_ms Unix timestamp associated with system boot */ void gnss_emul_set_data(const struct device *dev, const struct navigation_data *nav, - const struct gnss_info *info, int64_t timestamp_ms); + const struct gnss_info *info, int64_t boot_realtime_ms); /** * @brief Retrieve the last configured fix rate, regardless of PM state From 8f1fba7fccf4f3fab646b84ea2f05db10087af22 Mon Sep 17 00:00:00 2001 From: Jordan Yates Date: Fri, 17 Oct 2025 15:34:58 +1000 Subject: [PATCH 6/6] tests: drivers: gnss: gnss_emul: test driver Test the behaviour of the emulated GNSS driver. Signed-off-by: Jordan Yates --- tests/drivers/gnss/gnss_emul/CMakeLists.txt | 12 ++ tests/drivers/gnss/gnss_emul/app.overlay | 17 +++ tests/drivers/gnss/gnss_emul/prj.conf | 12 ++ tests/drivers/gnss/gnss_emul/src/main.c | 161 ++++++++++++++++++++ tests/drivers/gnss/gnss_emul/testcase.yaml | 11 ++ 5 files changed, 213 insertions(+) create mode 100644 tests/drivers/gnss/gnss_emul/CMakeLists.txt create mode 100644 tests/drivers/gnss/gnss_emul/app.overlay create mode 100644 tests/drivers/gnss/gnss_emul/prj.conf create mode 100644 tests/drivers/gnss/gnss_emul/src/main.c create mode 100644 tests/drivers/gnss/gnss_emul/testcase.yaml diff --git a/tests/drivers/gnss/gnss_emul/CMakeLists.txt b/tests/drivers/gnss/gnss_emul/CMakeLists.txt new file mode 100644 index 0000000000000..03db665f34ab0 --- /dev/null +++ b/tests/drivers/gnss/gnss_emul/CMakeLists.txt @@ -0,0 +1,12 @@ +# Copyright (c) 2025 Embeint Pty Ltd +# SPDX-License-Identifier: Apache-2.0 + +cmake_minimum_required(VERSION 3.20.0) + +find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) + +project(gnss_emul) + +target_sources(app PRIVATE + src/main.c +) diff --git a/tests/drivers/gnss/gnss_emul/app.overlay b/tests/drivers/gnss/gnss_emul/app.overlay new file mode 100644 index 0000000000000..1746d8ccb9486 --- /dev/null +++ b/tests/drivers/gnss/gnss_emul/app.overlay @@ -0,0 +1,17 @@ +/* + * Copyright 2025 Embeint Pty Ltd + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/ { + aliases { + gnss = &gnss; + }; + + gnss: gnss { + compatible = "zephyr,gnss-emul"; + status = "okay"; + zephyr,pm-device-runtime-auto; + }; +}; diff --git a/tests/drivers/gnss/gnss_emul/prj.conf b/tests/drivers/gnss/gnss_emul/prj.conf new file mode 100644 index 0000000000000..dde94f372248d --- /dev/null +++ b/tests/drivers/gnss/gnss_emul/prj.conf @@ -0,0 +1,12 @@ +# Copyright (c) 2025 Embeint Pty Ltd +# SPDX-License-Identifier: Apache-2.0 + +CONFIG_ZTEST=y +CONFIG_ZTEST_STACK_SIZE=4096 + +CONFIG_PM_DEVICE=y +CONFIG_PM_DEVICE_RUNTIME=y + +CONFIG_GNSS=y +CONFIG_GNSS_EMUL=y +CONFIG_GNSS_EMUL_MANUAL_UPDATE=y diff --git a/tests/drivers/gnss/gnss_emul/src/main.c b/tests/drivers/gnss/gnss_emul/src/main.c new file mode 100644 index 0000000000000..a76f2c4337880 --- /dev/null +++ b/tests/drivers/gnss/gnss_emul/src/main.c @@ -0,0 +1,161 @@ +/* + * Copyright 2025 Embeint Pty Ltd + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +#include +#include +#include +#include +#include +#include + +static void gnss_data_callback(const struct device *dev, const struct gnss_data *data); + +GNSS_DATA_CALLBACK_DEFINE(DEVICE_DT_GET(DT_ALIAS(gnss)), gnss_data_callback); +static K_SEM_DEFINE(gnss_data_published, 0, 1); +static struct gnss_data gnss_published_data; + +static void expected_pm_state(const struct device *dev, enum pm_device_state expected) +{ + enum pm_device_state state; + + zassert_equal(0, pm_device_state_get(dev, &state)); + zassert_equal(expected, state); +} + +static void gnss_data_callback(const struct device *dev, const struct gnss_data *data) +{ + gnss_published_data = *data; + k_sem_give(&gnss_data_published); +} + +static void print_time(const struct gnss_time *utc) +{ + printk("TIME: %02d/%02d/%02d %02d:%02d:%02d.%03d\n", utc->century_year, utc->month, + utc->month_day, utc->hour, utc->minute, utc->millisecond / 1000, + utc->millisecond % 1000); +} + +ZTEST(gnss_emul, test_config_functions) +{ + const struct device *dev = DEVICE_DT_GET(DT_ALIAS(gnss)); + enum gnss_navigation_mode mode; + gnss_systems_t systems; + uint32_t fix_rate; + + /* Booted into suspend mode */ + expected_pm_state(dev, PM_DEVICE_STATE_SUSPENDED); + + /* Configuration get API functions fail when suspended */ + zassert_equal(-ENODEV, gnss_get_enabled_systems(dev, &systems)); + zassert_equal(-ENODEV, gnss_get_navigation_mode(dev, &mode)); + zassert_equal(-ENODEV, gnss_get_fix_rate(dev, &fix_rate)); + + /* Configuration can be queried when enabled */ + zassert_equal(0, pm_device_runtime_get(dev)); + zassert_equal(0, gnss_set_enabled_systems(dev, GNSS_SYSTEM_GPS | GNSS_SYSTEM_GALILEO)); + zassert_equal(0, gnss_set_navigation_mode(dev, GNSS_NAVIGATION_MODE_HIGH_DYNAMICS)); + zassert_equal(0, gnss_set_fix_rate(dev, 1500)); + + zassert_equal(0, gnss_get_enabled_systems(dev, &systems)); + zassert_equal(0, gnss_get_navigation_mode(dev, &mode)); + zassert_equal(0, gnss_get_fix_rate(dev, &fix_rate)); + zassert_equal(GNSS_SYSTEM_GPS | GNSS_SYSTEM_GALILEO, systems); + zassert_equal(GNSS_NAVIGATION_MODE_HIGH_DYNAMICS, mode); + zassert_equal(1500, fix_rate); + + zassert_equal(0, pm_device_runtime_put(dev)); + + /* Fails again when suspended */ + zassert_equal(-ENODEV, gnss_get_enabled_systems(dev, &systems)); + zassert_equal(-ENODEV, gnss_get_navigation_mode(dev, &mode)); + zassert_equal(-ENODEV, gnss_get_fix_rate(dev, &fix_rate)); + + /* But escape hatches work */ + systems = 0; + mode = 0; + fix_rate = 0; + zassert_equal(0, gnss_emul_get_enabled_systems(dev, &systems)); + zassert_equal(0, gnss_emul_get_navigation_mode(dev, &mode)); + zassert_equal(0, gnss_emul_get_fix_rate(dev, &fix_rate)); + zassert_equal(GNSS_SYSTEM_GPS | GNSS_SYSTEM_GALILEO, systems); + zassert_equal(GNSS_NAVIGATION_MODE_HIGH_DYNAMICS, mode); + zassert_equal(1500, fix_rate); +} + +ZTEST(gnss_emul, test_callback_behaviour) +{ + const struct device *dev = DEVICE_DT_GET(DT_ALIAS(gnss)); + const struct navigation_data nav = { + .latitude = 150000000000, + .longitude = -15199000000, + .altitude = 123456, + }; + const struct gnss_info info = { + .satellites_cnt = 7, + .hdop = 1999, + .geoid_separation = 1000, + .fix_status = GNSS_FIX_STATUS_GNSS_FIX, + .fix_quality = GNSS_FIX_QUALITY_GNSS_SPS, + }; + const struct gnss_time *utc; + uint32_t timestamp; + + /* Booted into suspend mode */ + expected_pm_state(dev, PM_DEVICE_STATE_SUSPENDED); + + /* No data published while suspended */ + zassert_equal(-EAGAIN, k_sem_take(&gnss_data_published, K_SECONDS(5))); + + /* Power up and configure for 1Hz */ + zassert_equal(0, pm_device_runtime_get(dev)); + zassert_equal(0, gnss_set_fix_rate(dev, 1000)); + timestamp = k_uptime_get_32(); + + /* Monitor data for a while */ + for (int i = 0; i < 10; i++) { + zassert_equal(0, k_sem_take(&gnss_data_published, K_MSEC(1100))); + zassert_equal(0, gnss_published_data.nav_data.latitude); + zassert_equal(0, gnss_published_data.nav_data.longitude); + zassert_equal(0, gnss_published_data.nav_data.altitude); + zassert_equal(0, gnss_published_data.info.satellites_cnt); + print_time(&gnss_published_data.utc); + } + + /* Set a location, approximately 14th July 2017, 02:40:xx am */ + gnss_emul_set_data(dev, &nav, &info, 1500000000000LL); + for (int i = 0; i < 3; i++) { + utc = &gnss_published_data.utc; + /* Published data should match that configured */ + zassert_equal(0, k_sem_take(&gnss_data_published, K_MSEC(1100))); + zassert_mem_equal(&gnss_published_data.nav_data, &nav, sizeof(nav)); + zassert_mem_equal(&gnss_published_data.info, &info, sizeof(info)); + zassert_equal(17, utc->century_year); + zassert_equal(7, utc->month); + zassert_equal(14, utc->month_day); + zassert_equal(2, utc->hour); + zassert_equal(40, utc->minute); + print_time(&gnss_published_data.utc); + } + + /* Reset back to no location */ + gnss_emul_clear_data(dev); + for (int i = 0; i < 5; i++) { + zassert_equal(0, k_sem_take(&gnss_data_published, K_MSEC(1100))); + zassert_equal(0, gnss_published_data.nav_data.latitude); + zassert_equal(0, gnss_published_data.nav_data.longitude); + zassert_equal(0, gnss_published_data.nav_data.altitude); + zassert_equal(0, gnss_published_data.info.satellites_cnt); + print_time(&gnss_published_data.utc); + } + + /* Once again no callbacks once suspended */ + zassert_equal(0, pm_device_runtime_put(dev)); + zassert_equal(-EAGAIN, k_sem_take(&gnss_data_published, K_SECONDS(5))); +} + +ZTEST_SUITE(gnss_emul, NULL, NULL, NULL, NULL, NULL); diff --git a/tests/drivers/gnss/gnss_emul/testcase.yaml b/tests/drivers/gnss/gnss_emul/testcase.yaml new file mode 100644 index 0000000000000..f3a5780fd06c3 --- /dev/null +++ b/tests/drivers/gnss/gnss_emul/testcase.yaml @@ -0,0 +1,11 @@ +# Copyright (c) 2025 Embeint Pty Ltd +# SPDX-License-Identifier: Apache-2.0 + +tests: + drivers.gnss.gnss_emul: + platform_allow: + - native_sim + integration_platforms: + - native_sim + tags: + - gnss