Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions drivers/gnss/Kconfig.emul
Original file line number Diff line number Diff line change
Expand Up @@ -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.
180 changes: 95 additions & 85 deletions drivers/gnss/gnss_emul.c
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,12 @@ 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;
struct gnss_data data;
bool active;

#ifdef CONFIG_GNSS_SATELLITES
struct gnss_satellite satellites[GNSS_EMUL_SUPPORTED_SYSTEMS_COUNT];
Expand Down Expand Up @@ -73,24 +75,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)
{
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;
Expand All @@ -110,7 +94,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)
Expand Down Expand Up @@ -140,7 +124,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;

Expand All @@ -161,8 +145,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;

Expand All @@ -182,50 +165,14 @@ 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;

*systems = data->enabled_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;
Expand Down Expand Up @@ -346,35 +293,26 @@ 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;
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(&timestamp, &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;
Expand All @@ -384,6 +322,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 boot_realtime_ms)
{
struct gnss_emul_data *data = dev->data;

data->data.nav_data = *nav;
data->data.info = *info;
data->boot_realtime_ms = boot_realtime_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;
Expand All @@ -395,6 +367,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)
{
Expand Down Expand Up @@ -444,13 +418,19 @@ 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 {
gnss_emul_set_fix(dev);
gnss_emul_set_utc(dev);
gnss_emul_set_nav_data(dev);
}
#endif /* CONFIG_GNSS_EMUL_MANUAL_UPDATE */

gnss_publish_data(dev, &data->data);

Expand All @@ -463,27 +443,57 @@ 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;

data->dev = dev;
k_sem_init(&data->lock, 1, 1);
k_work_init_delayable(&data->data_dwork, gnss_emul_work_handler);
data->active = true;
gnss_emul_update_fix_timestamp(dev, true);
gnss_emul_schedule_work(dev);
}

static int gnss_emul_init(const struct device *dev)
static void gnss_emul_suspend(const struct device *dev)
{
gnss_emul_init_data(dev);
struct gnss_emul_data *data = dev->data;

if (pm_device_is_powered(dev)) {
gnss_emul_update_fix_timestamp(dev, true);
gnss_emul_schedule_work(dev);
} else {
pm_device_init_off(dev);
data->active = false;
gnss_emul_cancel_work(dev);
gnss_emul_clear_data(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;
}

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)
Expand Down
2 changes: 2 additions & 0 deletions dts/bindings/gnss/zephyr,gnss-emul.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,5 @@
description: Zephyr emulated GNSS device

compatible: "zephyr,gnss-emul"

include: base.yaml
63 changes: 63 additions & 0 deletions include/zephyr/drivers/gnss/gnss_emul.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
/*
* 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 <stdint.h>

#include <zephyr/device.h>
#include <zephyr/drivers/gnss.h>

/**
* @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 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 boot_realtime_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_ */
12 changes: 12 additions & 0 deletions tests/drivers/gnss/gnss_emul/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -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
)
Loading