gnss: rtk: Add basic integration
Incorporate the basic RTK API as well as a basic client integration (serial) as a way to receive and publish the RTK data. Signed-off-by: Luis Ubieda <luisf@croxel.com>
This commit is contained in:
parent
a5d2bdc6ed
commit
a000acf85b
@ -258,3 +258,7 @@ endif()
|
||||
if(CONFIG_GNSS_SATELLITES)
|
||||
zephyr_iterable_section(NAME gnss_satellites_callback KVMA RAM_REGION GROUP RODATA_REGION)
|
||||
endif()
|
||||
|
||||
if(CONFIG_GNSS_RTK)
|
||||
zephyr_iterable_section(NAME gnss_rtk_data_callback KVMA RAM_REGION GROUP RODATA_REGION)
|
||||
endif()
|
||||
|
||||
31
include/zephyr/gnss/rtk/decoder.h
Normal file
31
include/zephyr/gnss/rtk/decoder.h
Normal file
@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Croxel Inc.
|
||||
* Copyright (c) 2025 CogniPilot Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#ifndef ZEPHYR_INCLUDE_GNSS_RTK_DECODER_H_
|
||||
#define ZEPHYR_INCLUDE_GNSS_RTK_DECODER_H_
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
/**
|
||||
* @brief Get an RTK frame from buffer
|
||||
*
|
||||
* Used by RTK clients to extract frames from a data-buffer.
|
||||
*
|
||||
* @param[in] buf Buffer holding encoded data.
|
||||
* @param[in] buf_len Buffer length.
|
||||
* @param[out] data Pointer to the decoded frame.
|
||||
* @param[out] data_len Length of the decoded frame
|
||||
*
|
||||
* @return Zero if successful.
|
||||
* @return -ENOENT if no frames have been decoded successfully.
|
||||
* @return Other negative error code if decoding failed.
|
||||
*/
|
||||
int gnss_rtk_decoder_frame_get(uint8_t *buf, size_t buf_len,
|
||||
uint8_t **data, size_t *data_len);
|
||||
|
||||
#endif /* ZEPHYR_INCLUDE_GNSS_RTK_DECODER_H_ */
|
||||
47
include/zephyr/gnss/rtk/rtk.h
Normal file
47
include/zephyr/gnss/rtk/rtk.h
Normal file
@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright (c) 2023 Trackunit Corporation
|
||||
* Copyright (c) 2025 Croxel Inc.
|
||||
* Copyright (c) 2025 CogniPilot Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#ifndef ZEPHYR_INCLUDE_GNSS_RTK_RTK_H_
|
||||
#define ZEPHYR_INCLUDE_GNSS_RTK_RTK_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
struct gnss_rtk_data {
|
||||
const uint8_t *data;
|
||||
size_t len;
|
||||
};
|
||||
|
||||
typedef void (*gnss_rtk_data_callback_t)(const struct device *dev,
|
||||
const struct gnss_rtk_data *data);
|
||||
|
||||
struct gnss_rtk_data_callback {
|
||||
const struct device *dev;
|
||||
gnss_rtk_data_callback_t callback;
|
||||
};
|
||||
|
||||
#if CONFIG_GNSS_RTK
|
||||
#define GNSS_RTK_DATA_CALLBACK_DEFINE(_dev, _callback) \
|
||||
static const STRUCT_SECTION_ITERABLE(gnss_rtk_data_callback, \
|
||||
_gnss_rtk_data_callback__##_callback) = { \
|
||||
.dev = _dev, \
|
||||
.callback = _callback, \
|
||||
}
|
||||
#else
|
||||
#define GNSS_RTK_DATA_CALLBACK_DEFINE(_dev, _callback)
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* ZEPHYR_INCLUDE_GNSS_RTK_RTK_H_ */
|
||||
18
include/zephyr/gnss/rtk/rtk_publish.h
Normal file
18
include/zephyr/gnss/rtk/rtk_publish.h
Normal file
@ -0,0 +1,18 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Croxel Inc.
|
||||
* Copyright (c) 2025 CogniPilot Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#ifndef ZEPHYR_INCLUDE_GNSS_RTK_PUBLISH_H_
|
||||
#define ZEPHYR_INCLUDE_GNSS_RTK_PUBLISH_H_
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <zephyr/gnss/rtk/rtk.h>
|
||||
|
||||
/* Internal function used by RTK clients to publish data-correction. */
|
||||
void gnss_rtk_publish_data(const struct gnss_rtk_data *data);
|
||||
|
||||
#endif /* ZEPHYR_INCLUDE_GNSS_RTK_PUBLISH_H_ */
|
||||
@ -68,3 +68,7 @@
|
||||
#if defined(CONFIG_GNSS_SATELLITES)
|
||||
ITERABLE_SECTION_ROM(gnss_satellites_callback, Z_LINK_ITERABLE_SUBALIGN)
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_GNSS_RTK)
|
||||
ITERABLE_SECTION_ROM(gnss_rtk_data_callback, Z_LINK_ITERABLE_SUBALIGN)
|
||||
#endif
|
||||
|
||||
@ -16,6 +16,7 @@ add_subdirectory(canbus)
|
||||
add_subdirectory(debug)
|
||||
add_subdirectory(fb)
|
||||
add_subdirectory(fs)
|
||||
add_subdirectory(gnss)
|
||||
add_subdirectory(ipc)
|
||||
add_subdirectory(logging)
|
||||
add_subdirectory(mem_mgmt)
|
||||
|
||||
@ -20,6 +20,7 @@ source "subsys/dsp/Kconfig"
|
||||
source "subsys/emul/Kconfig"
|
||||
source "subsys/fb/Kconfig"
|
||||
source "subsys/fs/Kconfig"
|
||||
source "subsys/gnss/Kconfig"
|
||||
source "subsys/input/Kconfig"
|
||||
source "subsys/ipc/Kconfig"
|
||||
source "subsys/jwt/Kconfig"
|
||||
|
||||
5
subsys/gnss/CMakeLists.txt
Normal file
5
subsys/gnss/CMakeLists.txt
Normal file
@ -0,0 +1,5 @@
|
||||
# Copyright (c) 2025 Croxel Inc.
|
||||
# Copyright (c) 2025 CogniPilot Foundation
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
add_subdirectory_ifdef(CONFIG_GNSS_RTK rtk)
|
||||
5
subsys/gnss/Kconfig
Normal file
5
subsys/gnss/Kconfig
Normal file
@ -0,0 +1,5 @@
|
||||
# Copyright (c) 2025 Croxel Inc.
|
||||
# Copyright (c) 2025 CogniPilot Foundation
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
rsource "rtk/Kconfig"
|
||||
9
subsys/gnss/rtk/CMakeLists.txt
Normal file
9
subsys/gnss/rtk/CMakeLists.txt
Normal file
@ -0,0 +1,9 @@
|
||||
# Copyright (c) 2025 Croxel Inc.
|
||||
# Copyright (c) 2025 CogniPilot Foundation
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
zephyr_library()
|
||||
zephyr_library_sources(rtk.c)
|
||||
|
||||
add_subdirectory(protocol)
|
||||
add_subdirectory_ifdef(CONFIG_GNSS_RTK_SERIAL serial)
|
||||
20
subsys/gnss/rtk/Kconfig
Normal file
20
subsys/gnss/rtk/Kconfig
Normal file
@ -0,0 +1,20 @@
|
||||
# Copyright (c) 2025 Croxel Inc.
|
||||
# Copyright (c) 2025 CogniPilot Foundation
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
menuconfig GNSS_RTK
|
||||
bool "GNSS RTK client"
|
||||
select EXPERIMENTAL
|
||||
help
|
||||
Enable GNSS RTK data-correction clients
|
||||
|
||||
if GNSS_RTK
|
||||
|
||||
rsource "protocol/Kconfig"
|
||||
rsource "serial/Kconfig"
|
||||
|
||||
module = GNSS_RTK
|
||||
module-str = GNSS RTK
|
||||
source "subsys/logging/Kconfig.template.log_config"
|
||||
|
||||
endif
|
||||
5
subsys/gnss/rtk/protocol/CMakeLists.txt
Normal file
5
subsys/gnss/rtk/protocol/CMakeLists.txt
Normal file
@ -0,0 +1,5 @@
|
||||
# Copyright (c) 2025 Croxel Inc.
|
||||
# Copyright (c) 2025 CogniPilot Foundation
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
zephyr_library_sources_ifdef(CONFIG_GNSS_RTK_PROTOCOL_RTCM3 rtcm3.c)
|
||||
17
subsys/gnss/rtk/protocol/Kconfig
Normal file
17
subsys/gnss/rtk/protocol/Kconfig
Normal file
@ -0,0 +1,17 @@
|
||||
# Copyright (c) 2025 Croxel Inc.
|
||||
# Copyright (c) 2025 CogniPilot Foundation
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
choice GNSS_RTK_PROTOCOL
|
||||
prompt "GNSS RTK Protocol selection"
|
||||
default GNSS_RTK_PROTOCOL_RTCM3
|
||||
help
|
||||
Select the GNSS RTK Protocol to use in data-correction
|
||||
|
||||
config GNSS_RTK_PROTOCOL_RTCM3
|
||||
bool "RTCM3 Protocol"
|
||||
select CRC
|
||||
help
|
||||
Select RTCM3 protocol as GNSS RTK corrections
|
||||
|
||||
endchoice
|
||||
58
subsys/gnss/rtk/protocol/rtcm3.c
Normal file
58
subsys/gnss/rtk/protocol/rtcm3.c
Normal file
@ -0,0 +1,58 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Croxel Inc.
|
||||
* Copyright (c) 2025 CogniPilot Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <zephyr/sys/byteorder.h>
|
||||
#include <zephyr/sys/util.h>
|
||||
#include <zephyr/gnss/rtk/decoder.h>
|
||||
#include <zephyr/sys/crc.h>
|
||||
|
||||
#define RTCM3_FRAME_SYNC_SZ 1
|
||||
#define RTCM3_FRAME_HDR_SZ 2
|
||||
#define RTCM3_FRAME_CHECKSUM_SZ 3
|
||||
#define RTCM3_FRAME_OVERHEAD (RTCM3_FRAME_SYNC_SZ + RTCM3_FRAME_HDR_SZ + \
|
||||
RTCM3_FRAME_CHECKSUM_SZ)
|
||||
|
||||
#define RTCM3_SYNC_BYTE 0xD3
|
||||
|
||||
#define RTCM3_FRAME_PAYLOAD_SZ(hdr) (sys_be16_to_cpu(hdr) & BIT_MASK(10))
|
||||
#define RTCM3_FRAME_SZ(payload_len) ((payload_len) + RTCM3_FRAME_OVERHEAD)
|
||||
|
||||
struct rtcm3_frame {
|
||||
uint8_t sync_frame;
|
||||
uint16_t hdr;
|
||||
uint8_t payload[];
|
||||
} __packed;
|
||||
|
||||
int gnss_rtk_decoder_frame_get(uint8_t *buf, size_t buf_len,
|
||||
uint8_t **data, size_t *data_len)
|
||||
{
|
||||
for (size_t i = 0 ; (i + RTCM3_FRAME_OVERHEAD - 1) < buf_len ; i++) {
|
||||
if (buf[i] != RTCM3_SYNC_BYTE) {
|
||||
continue;
|
||||
}
|
||||
|
||||
struct rtcm3_frame *frame = (struct rtcm3_frame *)&buf[i];
|
||||
uint16_t payload_len = RTCM3_FRAME_PAYLOAD_SZ(frame->hdr);
|
||||
uint16_t remaining_bytes = buf_len - i;
|
||||
|
||||
if (payload_len == 0 ||
|
||||
RTCM3_FRAME_SZ(payload_len) > remaining_bytes) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (crc24q_rtcm3((const uint8_t *)frame,
|
||||
RTCM3_FRAME_SZ(payload_len)) == 0) {
|
||||
*data = (uint8_t *)frame;
|
||||
*data_len = RTCM3_FRAME_SZ(payload_len);
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return -ENOENT;
|
||||
}
|
||||
24
subsys/gnss/rtk/rtk.c
Normal file
24
subsys/gnss/rtk/rtk.c
Normal file
@ -0,0 +1,24 @@
|
||||
/*
|
||||
* Copyright (c) 2023 Trackunit Corporation
|
||||
* Copyright (c) 2025 Croxel Inc.
|
||||
* Copyright (c) 2025 CogniPilot Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <zephyr/kernel.h>
|
||||
#include <zephyr/sys/iterable_sections.h>
|
||||
#include <zephyr/gnss/rtk/rtk.h>
|
||||
|
||||
void gnss_rtk_publish_data(const struct gnss_rtk_data *data)
|
||||
{
|
||||
static K_SEM_DEFINE(publish_lock, 1, 1);
|
||||
|
||||
(void)k_sem_take(&publish_lock, K_FOREVER);
|
||||
|
||||
STRUCT_SECTION_FOREACH(gnss_rtk_data_callback, callback) {
|
||||
callback->callback(callback->dev, data);
|
||||
}
|
||||
|
||||
k_sem_give(&publish_lock);
|
||||
}
|
||||
5
subsys/gnss/rtk/serial/CMakeLists.txt
Normal file
5
subsys/gnss/rtk/serial/CMakeLists.txt
Normal file
@ -0,0 +1,5 @@
|
||||
# Copyright (c) 2025 Croxel Inc.
|
||||
# Copyright (c) 2025 CogniPilot Foundation
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
zephyr_library_sources(serial.c)
|
||||
10
subsys/gnss/rtk/serial/Kconfig
Normal file
10
subsys/gnss/rtk/serial/Kconfig
Normal file
@ -0,0 +1,10 @@
|
||||
# Copyright (c) 2025 Croxel Inc.
|
||||
# Copyright (c) 2025 CogniPilot Foundation
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
config GNSS_RTK_SERIAL
|
||||
bool "Serial GNSS RTK client"
|
||||
select SERIAL
|
||||
select UART_INTERRUPT_DRIVEN
|
||||
help
|
||||
Use RTK Serial client to obtain data-correction.
|
||||
101
subsys/gnss/rtk/serial/serial.c
Normal file
101
subsys/gnss/rtk/serial/serial.c
Normal file
@ -0,0 +1,101 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Croxel Inc.
|
||||
* Copyright (c) 2025 CogniPilot Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <zephyr/kernel.h>
|
||||
#include <zephyr/init.h>
|
||||
#include <zephyr/sys/check.h>
|
||||
#include <zephyr/sys/ring_buffer.h>
|
||||
#include <zephyr/drivers/uart.h>
|
||||
#include <zephyr/gnss/rtk/decoder.h>
|
||||
#include <zephyr/gnss/rtk/rtk_publish.h>
|
||||
#include <zephyr/gnss/rtk/rtk.h>
|
||||
|
||||
#include <zephyr/logging/log.h>
|
||||
LOG_MODULE_REGISTER(rtk_serial, CONFIG_GNSS_RTK_LOG_LEVEL);
|
||||
|
||||
static const struct device *rtk_serial_dev = DEVICE_DT_GET(DT_CHOSEN(zephyr_rtk_serial));
|
||||
static struct ring_buf process_ringbuf;
|
||||
static uint8_t process_buf[2048];
|
||||
|
||||
static void gnss_rtk_process_work_handler(struct k_work *work)
|
||||
{
|
||||
static uint8_t work_buf[2048];
|
||||
uint32_t len = ring_buf_get(&process_ringbuf, work_buf, sizeof(work_buf));
|
||||
uint32_t offset = 0;
|
||||
|
||||
ARG_UNUSED(work);
|
||||
|
||||
do {
|
||||
uint8_t *frame;
|
||||
size_t frame_len;
|
||||
int err;
|
||||
|
||||
err = gnss_rtk_decoder_frame_get(work_buf + offset, len - offset,
|
||||
&frame, &frame_len);
|
||||
if (err != 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
LOG_HEXDUMP_DBG(frame, frame_len, "Frame received");
|
||||
|
||||
/* Publish results */
|
||||
struct gnss_rtk_data rtk_data = {
|
||||
.data = frame,
|
||||
.len = frame_len,
|
||||
};
|
||||
|
||||
gnss_rtk_publish_data(&rtk_data);
|
||||
offset += frame_len;
|
||||
|
||||
} while (len > offset);
|
||||
}
|
||||
|
||||
static K_WORK_DELAYABLE_DEFINE(gnss_rtk_process_work, gnss_rtk_process_work_handler);
|
||||
|
||||
static void rtk_uart_isr_callback(const struct device *dev, void *user_data)
|
||||
{
|
||||
ARG_UNUSED(user_data);
|
||||
|
||||
(void)uart_irq_update(dev);
|
||||
|
||||
if (uart_irq_rx_ready(dev)) {
|
||||
int ret;
|
||||
|
||||
do {
|
||||
char c;
|
||||
|
||||
ret = uart_fifo_read(dev, &c, 1);
|
||||
if (ret > 0) {
|
||||
ret = ring_buf_put(&process_ringbuf, &c, 1);
|
||||
}
|
||||
} while (ret > 0);
|
||||
|
||||
/** Since messages come through in a burst at a period
|
||||
* (e.g: 1 Hz), wait until all messages are received before
|
||||
* processing.
|
||||
*/
|
||||
(void)k_work_reschedule(&gnss_rtk_process_work, K_MSEC(10));
|
||||
}
|
||||
}
|
||||
|
||||
static int rtk_serial_client_init(void)
|
||||
{
|
||||
int err;
|
||||
|
||||
ring_buf_init(&process_ringbuf, ARRAY_SIZE(process_buf), process_buf);
|
||||
|
||||
err = uart_irq_callback_user_data_set(rtk_serial_dev, rtk_uart_isr_callback, NULL);
|
||||
if (err < 0) {
|
||||
return err;
|
||||
}
|
||||
|
||||
uart_irq_rx_enable(rtk_serial_dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
SYS_INIT(rtk_serial_client_init, APPLICATION, CONFIG_APPLICATION_INIT_PRIORITY);
|
||||
Loading…
Reference in New Issue
Block a user