1 /*
2 * Copyright (c) 2024 Trackunit Corporation
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #undef _POSIX_C_SOURCE
8 #define _POSIX_C_SOURCE 200809L /* Required for gmtime_r */
9
10 #include <zephyr/drivers/gnss.h>
11 #include <zephyr/drivers/gnss/gnss_publish.h>
12 #include <zephyr/kernel.h>
13 #include <zephyr/pm/device.h>
14 #include <zephyr/pm/device_runtime.h>
15
16 #include <string.h>
17 #include <time.h>
18
19 #include <zephyr/logging/log.h>
20 LOG_MODULE_REGISTER(gnss_emul, CONFIG_GNSS_LOG_LEVEL);
21
22 #define DT_DRV_COMPAT zephyr_gnss_emul
23
24 #define GNSS_EMUL_DEFAULT_FIX_INTERVAL_MS 1000
25 #define GNSS_EMUL_MIN_FIX_INTERVAL_MS 100
26 #define GNSS_EMUL_FIX_ACQUIRE_TIME_MS 5000
27 #define GNSS_EMUL_DEFAULT_NAV_MODE GNSS_NAVIGATION_MODE_BALANCED_DYNAMICS
28 #define GNSS_EMUL_SUPPORTED_SYSTEMS_MASK 0xFF
29 #define GNSS_EMUL_SUPPORTED_SYSTEMS_COUNT 8
30 #define GNSS_EMUL_DEFAULT_ENABLED_SYSTEMS_MASK GNSS_EMUL_SUPPORTED_SYSTEMS_MASK
31
32 struct gnss_emul_data {
33 const struct device *dev;
34 struct k_work_delayable data_dwork;
35 struct k_sem lock;
36 int64_t resume_timestamp_ms;
37 int64_t fix_timestamp_ms;
38 uint32_t fix_interval_ms;
39 enum gnss_navigation_mode nav_mode;
40 gnss_systems_t enabled_systems;
41 struct gnss_data data;
42
43 #ifdef CONFIG_GNSS_SATELLITES
44 struct gnss_satellite satellites[GNSS_EMUL_SUPPORTED_SYSTEMS_COUNT];
45 uint8_t satellites_len;
46 #endif
47 };
48
gnss_emul_lock_sem(const struct device * dev)49 static void gnss_emul_lock_sem(const struct device *dev)
50 {
51 struct gnss_emul_data *data = dev->data;
52
53 (void)k_sem_take(&data->lock, K_FOREVER);
54 }
55
gnss_emul_unlock_sem(const struct device * dev)56 static void gnss_emul_unlock_sem(const struct device *dev)
57 {
58 struct gnss_emul_data *data = dev->data;
59
60 k_sem_give(&data->lock);
61 }
62
gnss_emul_update_fix_timestamp(const struct device * dev,bool resuming)63 static void gnss_emul_update_fix_timestamp(const struct device *dev, bool resuming)
64 {
65 struct gnss_emul_data *data = dev->data;
66 int64_t uptime_ms;
67
68 uptime_ms = k_uptime_get();
69 data->fix_timestamp_ms = ((uptime_ms / data->fix_interval_ms) + 1) * data->fix_interval_ms;
70
71 if (resuming) {
72 data->resume_timestamp_ms = data->fix_timestamp_ms;
73 }
74 }
75
gnss_emul_fix_is_acquired(const struct device * dev)76 static bool gnss_emul_fix_is_acquired(const struct device *dev)
77 {
78 struct gnss_emul_data *data = dev->data;
79 int64_t time_since_resume;
80
81 time_since_resume = data->fix_timestamp_ms - data->resume_timestamp_ms;
82 return time_since_resume >= GNSS_EMUL_FIX_ACQUIRE_TIME_MS;
83 }
84
85 #ifdef CONFIG_PM_DEVICE
gnss_emul_clear_fix_timestamp(const struct device * dev)86 static void gnss_emul_clear_fix_timestamp(const struct device *dev)
87 {
88 struct gnss_emul_data *data = dev->data;
89
90 data->fix_timestamp_ms = 0;
91 }
92 #endif
93
gnss_emul_schedule_work(const struct device * dev)94 static void gnss_emul_schedule_work(const struct device *dev)
95 {
96 struct gnss_emul_data *data = dev->data;
97
98 k_work_schedule(&data->data_dwork, K_TIMEOUT_ABS_MS(data->fix_timestamp_ms));
99 }
100
gnss_emul_cancel_work(const struct device * dev)101 static bool gnss_emul_cancel_work(const struct device *dev)
102 {
103 struct gnss_emul_data *data = dev->data;
104 struct k_work_sync sync;
105
106 return k_work_cancel_delayable_sync(&data->data_dwork, &sync);
107 }
108
gnss_emul_is_resumed(const struct device * dev)109 static bool gnss_emul_is_resumed(const struct device *dev)
110 {
111 struct gnss_emul_data *data = dev->data;
112
113 return data->fix_timestamp_ms > 0;
114 }
115
gnss_emul_lock(const struct device * dev)116 static void gnss_emul_lock(const struct device *dev)
117 {
118 gnss_emul_lock_sem(dev);
119 gnss_emul_cancel_work(dev);
120 }
121
gnss_emul_unlock(const struct device * dev)122 static void gnss_emul_unlock(const struct device *dev)
123 {
124 if (gnss_emul_is_resumed(dev)) {
125 gnss_emul_schedule_work(dev);
126 }
127
128 gnss_emul_unlock_sem(dev);
129 }
130
gnss_emul_set_fix_rate(const struct device * dev,uint32_t fix_interval_ms)131 static int gnss_emul_set_fix_rate(const struct device *dev, uint32_t fix_interval_ms)
132 {
133 struct gnss_emul_data *data = dev->data;
134
135 if (fix_interval_ms < GNSS_EMUL_MIN_FIX_INTERVAL_MS) {
136 return -EINVAL;
137 }
138
139 data->fix_interval_ms = fix_interval_ms;
140 return 0;
141 }
142
gnss_emul_get_fix_rate(const struct device * dev,uint32_t * fix_interval_ms)143 static int gnss_emul_get_fix_rate(const struct device *dev, uint32_t *fix_interval_ms)
144 {
145 struct gnss_emul_data *data = dev->data;
146
147 *fix_interval_ms = data->fix_interval_ms;
148 return 0;
149 }
150
gnss_emul_set_navigation_mode(const struct device * dev,enum gnss_navigation_mode mode)151 static int gnss_emul_set_navigation_mode(const struct device *dev,
152 enum gnss_navigation_mode mode)
153 {
154 struct gnss_emul_data *data = dev->data;
155
156 if (mode > GNSS_NAVIGATION_MODE_HIGH_DYNAMICS) {
157 return -EINVAL;
158 }
159
160 data->nav_mode = mode;
161 return 0;
162 }
163
gnss_emul_get_navigation_mode(const struct device * dev,enum gnss_navigation_mode * mode)164 static int gnss_emul_get_navigation_mode(const struct device *dev,
165 enum gnss_navigation_mode *mode)
166 {
167 struct gnss_emul_data *data = dev->data;
168
169 *mode = data->nav_mode;
170 return 0;
171 }
172
gnss_emul_set_enabled_systems(const struct device * dev,gnss_systems_t systems)173 static int gnss_emul_set_enabled_systems(const struct device *dev, gnss_systems_t systems)
174 {
175 struct gnss_emul_data *data = dev->data;
176
177 if (systems > GNSS_EMUL_SUPPORTED_SYSTEMS_MASK) {
178 return -EINVAL;
179 }
180
181 data->enabled_systems = systems;
182 return 0;
183 }
184
gnss_emul_get_enabled_systems(const struct device * dev,gnss_systems_t * systems)185 static int gnss_emul_get_enabled_systems(const struct device *dev, gnss_systems_t *systems)
186 {
187 struct gnss_emul_data *data = dev->data;
188
189 *systems = data->enabled_systems;
190 return 0;
191 }
192
193 #ifdef CONFIG_PM_DEVICE
gnss_emul_resume(const struct device * dev)194 static void gnss_emul_resume(const struct device *dev)
195 {
196 gnss_emul_update_fix_timestamp(dev, true);
197 }
198
gnss_emul_suspend(const struct device * dev)199 static void gnss_emul_suspend(const struct device *dev)
200 {
201 gnss_emul_clear_fix_timestamp(dev);
202 }
203
gnss_emul_pm_action(const struct device * dev,enum pm_device_action action)204 static int gnss_emul_pm_action(const struct device *dev, enum pm_device_action action)
205 {
206 int ret = 0;
207
208 gnss_emul_lock(dev);
209
210 switch (action) {
211 case PM_DEVICE_ACTION_SUSPEND:
212 gnss_emul_suspend(dev);
213 break;
214
215 case PM_DEVICE_ACTION_RESUME:
216 gnss_emul_resume(dev);
217 break;
218
219 default:
220 ret = -ENOTSUP;
221 break;
222 }
223
224 gnss_emul_unlock(dev);
225 return ret;
226 }
227 #endif
228
gnss_emul_api_set_fix_rate(const struct device * dev,uint32_t fix_interval_ms)229 static int gnss_emul_api_set_fix_rate(const struct device *dev, uint32_t fix_interval_ms)
230 {
231 int ret = -ENODEV;
232
233 gnss_emul_lock(dev);
234
235 if (!gnss_emul_is_resumed(dev)) {
236 goto unlock_return;
237 }
238
239 ret = gnss_emul_set_fix_rate(dev, fix_interval_ms);
240
241 unlock_return:
242 gnss_emul_unlock(dev);
243 return ret;
244 }
245
gnss_emul_api_get_fix_rate(const struct device * dev,uint32_t * fix_interval_ms)246 static int gnss_emul_api_get_fix_rate(const struct device *dev, uint32_t *fix_interval_ms)
247 {
248 int ret = -ENODEV;
249
250 gnss_emul_lock(dev);
251
252 if (!gnss_emul_is_resumed(dev)) {
253 goto unlock_return;
254 }
255
256 ret = gnss_emul_get_fix_rate(dev, fix_interval_ms);
257
258 unlock_return:
259 gnss_emul_unlock(dev);
260 return ret;
261 }
262
gnss_emul_api_set_navigation_mode(const struct device * dev,enum gnss_navigation_mode mode)263 static int gnss_emul_api_set_navigation_mode(const struct device *dev,
264 enum gnss_navigation_mode mode)
265 {
266 int ret = -ENODEV;
267
268 gnss_emul_lock(dev);
269
270 if (!gnss_emul_is_resumed(dev)) {
271 goto unlock_return;
272 }
273
274 ret = gnss_emul_set_navigation_mode(dev, mode);
275
276 unlock_return:
277 gnss_emul_unlock(dev);
278 return ret;
279 }
280
gnss_emul_api_get_navigation_mode(const struct device * dev,enum gnss_navigation_mode * mode)281 static int gnss_emul_api_get_navigation_mode(const struct device *dev,
282 enum gnss_navigation_mode *mode)
283 {
284 int ret = -ENODEV;
285
286 gnss_emul_lock(dev);
287
288 if (!gnss_emul_is_resumed(dev)) {
289 goto unlock_return;
290 }
291
292 ret = gnss_emul_get_navigation_mode(dev, mode);
293
294 unlock_return:
295 gnss_emul_unlock(dev);
296 return ret;
297 }
298
gnss_emul_api_set_enabled_systems(const struct device * dev,gnss_systems_t systems)299 static int gnss_emul_api_set_enabled_systems(const struct device *dev, gnss_systems_t systems)
300 {
301 int ret = -ENODEV;
302
303 gnss_emul_lock(dev);
304
305 if (!gnss_emul_is_resumed(dev)) {
306 goto unlock_return;
307 }
308
309 ret = gnss_emul_set_enabled_systems(dev, systems);
310
311 unlock_return:
312 gnss_emul_unlock(dev);
313 return ret;
314 }
315
gnss_emul_api_get_enabled_systems(const struct device * dev,gnss_systems_t * systems)316 static int gnss_emul_api_get_enabled_systems(const struct device *dev, gnss_systems_t *systems)
317 {
318 int ret = -ENODEV;
319
320 gnss_emul_lock(dev);
321
322 if (!gnss_emul_is_resumed(dev)) {
323 goto unlock_return;
324 }
325
326 ret = gnss_emul_get_enabled_systems(dev, systems);
327
328 unlock_return:
329 gnss_emul_unlock(dev);
330 return ret;
331 }
332
gnss_emul_api_get_supported_systems(const struct device * dev,gnss_systems_t * systems)333 static int gnss_emul_api_get_supported_systems(const struct device *dev, gnss_systems_t *systems)
334 {
335 *systems = GNSS_EMUL_SUPPORTED_SYSTEMS_MASK;
336 return 0;
337 }
338
339 static DEVICE_API(gnss, api) = {
340 .set_fix_rate = gnss_emul_api_set_fix_rate,
341 .get_fix_rate = gnss_emul_api_get_fix_rate,
342 .set_navigation_mode = gnss_emul_api_set_navigation_mode,
343 .get_navigation_mode = gnss_emul_api_get_navigation_mode,
344 .set_enabled_systems = gnss_emul_api_set_enabled_systems,
345 .get_enabled_systems = gnss_emul_api_get_enabled_systems,
346 .get_supported_systems = gnss_emul_api_get_supported_systems,
347 };
348
gnss_emul_clear_data(const struct device * dev)349 static void gnss_emul_clear_data(const struct device *dev)
350 {
351 struct gnss_emul_data *data = dev->data;
352
353 memset(&data->data, 0, sizeof(data->data));
354 }
355
gnss_emul_set_fix(const struct device * dev)356 static void gnss_emul_set_fix(const struct device *dev)
357 {
358 struct gnss_emul_data *data = dev->data;
359
360 data->data.info.satellites_cnt = 8;
361 data->data.info.hdop = 100;
362 data->data.info.fix_status = GNSS_FIX_STATUS_GNSS_FIX;
363 data->data.info.fix_quality = GNSS_FIX_QUALITY_GNSS_SPS;
364 }
365
gnss_emul_set_utc(const struct device * dev)366 static void gnss_emul_set_utc(const struct device *dev)
367 {
368 struct gnss_emul_data *data = dev->data;
369 time_t timestamp;
370 struct tm datetime;
371 uint16_t millisecond;
372
373 timestamp = (time_t)(data->fix_timestamp_ms / 1000);
374 gmtime_r(×tamp, &datetime);
375
376 millisecond = (uint16_t)(data->fix_timestamp_ms % 1000)
377 + (uint16_t)(datetime.tm_sec * 1000);
378
379 data->data.utc.hour = datetime.tm_hour;
380 data->data.utc.millisecond = millisecond;
381 data->data.utc.minute = datetime.tm_min;
382 data->data.utc.month = datetime.tm_mon + 1;
383 data->data.utc.century_year = datetime.tm_year % 100;
384 }
385
gnss_emul_set_nav_data(const struct device * dev)386 static void gnss_emul_set_nav_data(const struct device *dev)
387 {
388 struct gnss_emul_data *data = dev->data;
389
390 data->data.nav_data.latitude = 10000000000;
391 data->data.nav_data.longitude = -10000000000;
392 data->data.nav_data.bearing = 3000;
393 data->data.nav_data.speed = 0;
394 data->data.nav_data.altitude = 20000;
395 }
396
397 #ifdef CONFIG_GNSS_SATELLITES
gnss_emul_clear_satellites(const struct device * dev)398 static void gnss_emul_clear_satellites(const struct device *dev)
399 {
400 struct gnss_emul_data *data = dev->data;
401
402 data->satellites_len = 0;
403 }
404
gnss_emul_system_enabled(const struct device * dev,uint8_t system_bit)405 static bool gnss_emul_system_enabled(const struct device *dev, uint8_t system_bit)
406 {
407 struct gnss_emul_data *data = dev->data;
408
409 return BIT(system_bit) & data->enabled_systems;
410 }
411
gnss_emul_add_satellite(const struct device * dev,uint8_t system_bit)412 static void gnss_emul_add_satellite(const struct device *dev, uint8_t system_bit)
413 {
414 struct gnss_emul_data *data = dev->data;
415
416 /* Unique values synthesized from GNSS system */
417 data->satellites[data->satellites_len].prn = system_bit;
418 data->satellites[data->satellites_len].snr = system_bit + 20;
419 data->satellites[data->satellites_len].elevation = system_bit + 40;
420 data->satellites[data->satellites_len].azimuth = system_bit + 60;
421 data->satellites[data->satellites_len].system = BIT(system_bit);
422 data->satellites[data->satellites_len].is_tracked = true;
423 data->satellites_len++;
424 }
425
gnss_emul_set_satellites(const struct device * dev)426 static void gnss_emul_set_satellites(const struct device *dev)
427 {
428 gnss_emul_clear_satellites(dev);
429
430 for (uint8_t i = 0; i < GNSS_EMUL_SUPPORTED_SYSTEMS_COUNT; i++) {
431 if (!gnss_emul_system_enabled(dev, i)) {
432 continue;
433 }
434
435 gnss_emul_add_satellite(dev, i);
436 }
437 }
438 #endif
439
gnss_emul_work_handler(struct k_work * work)440 static void gnss_emul_work_handler(struct k_work *work)
441 {
442 struct k_work_delayable *dwork = k_work_delayable_from_work(work);
443 struct gnss_emul_data *data = CONTAINER_OF(dwork, struct gnss_emul_data, data_dwork);
444 const struct device *dev = data->dev;
445
446 if (!gnss_emul_fix_is_acquired(dev)) {
447 gnss_emul_clear_data(dev);
448 } else {
449 gnss_emul_set_fix(dev);
450 gnss_emul_set_utc(dev);
451 gnss_emul_set_nav_data(dev);
452 }
453
454 gnss_publish_data(dev, &data->data);
455
456 #ifdef CONFIG_GNSS_SATELLITES
457 gnss_emul_set_satellites(dev);
458 gnss_publish_satellites(dev, data->satellites, data->satellites_len);
459 #endif
460
461 gnss_emul_update_fix_timestamp(dev, false);
462 gnss_emul_schedule_work(dev);
463 }
464
gnss_emul_init_data(const struct device * dev)465 static void gnss_emul_init_data(const struct device *dev)
466 {
467 struct gnss_emul_data *data = dev->data;
468
469 data->dev = dev;
470 k_sem_init(&data->lock, 1, 1);
471 k_work_init_delayable(&data->data_dwork, gnss_emul_work_handler);
472 }
473
gnss_emul_init(const struct device * dev)474 static int gnss_emul_init(const struct device *dev)
475 {
476 gnss_emul_init_data(dev);
477
478 if (pm_device_is_powered(dev)) {
479 gnss_emul_update_fix_timestamp(dev, true);
480 gnss_emul_schedule_work(dev);
481 } else {
482 pm_device_init_off(dev);
483 }
484
485 return pm_device_runtime_enable(dev);
486 }
487
488 #define GNSS_EMUL_NAME(inst, name) _CONCAT(name, inst)
489
490 #define GNSS_EMUL_DEVICE(inst) \
491 static struct gnss_emul_data GNSS_EMUL_NAME(inst, data) = { \
492 .fix_interval_ms = GNSS_EMUL_DEFAULT_FIX_INTERVAL_MS, \
493 .nav_mode = GNSS_EMUL_DEFAULT_NAV_MODE, \
494 .enabled_systems = GNSS_EMUL_DEFAULT_ENABLED_SYSTEMS_MASK, \
495 }; \
496 \
497 PM_DEVICE_DT_INST_DEFINE(inst, gnss_emul_pm_action); \
498 \
499 DEVICE_DT_INST_DEFINE( \
500 inst, \
501 gnss_emul_init, \
502 PM_DEVICE_DT_INST_GET(inst), \
503 &GNSS_EMUL_NAME(inst, data), \
504 NULL, \
505 POST_KERNEL, \
506 CONFIG_GNSS_INIT_PRIORITY, \
507 &api \
508 );
509
510 DT_INST_FOREACH_STATUS_OKAY(GNSS_EMUL_DEVICE)
511