├── .DS_Store ├── bmi270 ├── .gitignore ├── sample.yaml ├── app.overlay ├── CMakeLists.txt ├── prj.conf ├── README.rst └── src │ └── main.c ├── gnss ├── .gitignore ├── .DS_Store ├── overlay-supl.conf ├── CMakeLists.txt ├── overlay-pgps.conf ├── src │ ├── assistance.h │ ├── mcc_location_table.h │ ├── factory_almanac_v2.h │ ├── factory_almanac_v3.h │ ├── assistance_supl.c │ ├── assistance_minimal.c │ ├── assistance.c │ └── mcc_location_table.c ├── prj.conf ├── sample.yaml ├── Kconfig └── README.rst ├── bme_688 ├── .gitignore ├── boards │ ├── thingy91x_nrf9151_ns.overlay │ └── nrf9160dk_nrf9160_ns.overlay ├── Kconfig ├── CMakeLists.txt ├── prj.conf ├── sample.yaml ├── README.rst └── src │ └── main.c ├── rgb_led ├── .gitignore ├── .DS_Store ├── CMakeLists.txt ├── prj.conf ├── sample.yaml ├── boards │ └── ucans32k1sic.overlay ├── README.rst └── src │ └── main.c ├── .gitattributes ├── projects ├── geofencing │ ├── .gitignore │ ├── .DS_Store │ ├── overlay-supl.conf │ ├── CMakeLists.txt │ ├── prj.conf │ ├── overlay-pgps.conf │ ├── src │ │ ├── assistance.h │ │ ├── mcc_location_table.h │ │ ├── factory_almanac_v2.h │ │ ├── factory_almanac_v3.h │ │ ├── assistance_supl.c │ │ ├── main.c │ │ ├── assistance_minimal.c │ │ ├── assistance.c │ │ └── mcc_location_table.c │ ├── sample.yaml │ ├── Kconfig │ └── README.rst ├── .DS_Store └── temperature_sensing │ ├── .gitignore │ ├── .DS_Store │ ├── prj.conf │ ├── Kconfig │ ├── boards │ ├── nrf9160dk_nrf9160_ns.overlay │ └── thingy91x_nrf9151_ns.overlay │ ├── CMakeLists.txt │ ├── sample.yaml │ ├── README.rst │ └── src │ └── main.c ├── thingy91x_hello.nrfcloud.com_v2.0.1.zip └── connectivity-bridge-v2.0.1-thingy91x-nrf53-dfu.zip /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techiesms/FAB25-WORKSHOP/main/.DS_Store -------------------------------------------------------------------------------- /bmi270/.gitignore: -------------------------------------------------------------------------------- 1 | # editors 2 | *.swp 3 | *~ 4 | 5 | # build 6 | /build*/ 7 | -------------------------------------------------------------------------------- /gnss/.gitignore: -------------------------------------------------------------------------------- 1 | # editors 2 | *.swp 3 | *~ 4 | 5 | # build 6 | /build*/ 7 | -------------------------------------------------------------------------------- /bme_688/.gitignore: -------------------------------------------------------------------------------- 1 | # editors 2 | *.swp 3 | *~ 4 | 5 | # build 6 | /build*/ 7 | -------------------------------------------------------------------------------- /rgb_led/.gitignore: -------------------------------------------------------------------------------- 1 | # editors 2 | *.swp 3 | *~ 4 | 5 | # build 6 | /build*/ 7 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /gnss/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techiesms/FAB25-WORKSHOP/main/gnss/.DS_Store -------------------------------------------------------------------------------- /projects/geofencing/.gitignore: -------------------------------------------------------------------------------- 1 | # editors 2 | *.swp 3 | *~ 4 | 5 | # build 6 | /build*/ 7 | -------------------------------------------------------------------------------- /projects/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techiesms/FAB25-WORKSHOP/main/projects/.DS_Store -------------------------------------------------------------------------------- /rgb_led/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techiesms/FAB25-WORKSHOP/main/rgb_led/.DS_Store -------------------------------------------------------------------------------- /projects/temperature_sensing/.gitignore: -------------------------------------------------------------------------------- 1 | # editors 2 | *.swp 3 | *~ 4 | 5 | # build 6 | /build*/ 7 | -------------------------------------------------------------------------------- /projects/geofencing/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techiesms/FAB25-WORKSHOP/main/projects/geofencing/.DS_Store -------------------------------------------------------------------------------- /projects/temperature_sensing/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techiesms/FAB25-WORKSHOP/main/projects/temperature_sensing/.DS_Store -------------------------------------------------------------------------------- /projects/temperature_sensing/prj.conf: -------------------------------------------------------------------------------- 1 | CONFIG_SENSOR=y 2 | CONFIG_BME680=y 3 | 4 | CONFIG_GPIO=y 5 | CONFIG_LOG=y 6 | CONFIG_CONSOLE=y 7 | -------------------------------------------------------------------------------- /thingy91x_hello.nrfcloud.com_v2.0.1.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techiesms/FAB25-WORKSHOP/main/thingy91x_hello.nrfcloud.com_v2.0.1.zip -------------------------------------------------------------------------------- /connectivity-bridge-v2.0.1-thingy91x-nrf53-dfu.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techiesms/FAB25-WORKSHOP/main/connectivity-bridge-v2.0.1-thingy91x-nrf53-dfu.zip -------------------------------------------------------------------------------- /bmi270/sample.yaml: -------------------------------------------------------------------------------- 1 | sample: 2 | name: BMI270 Sensor sample 3 | tests: 4 | sample.sensor.bmi270: 5 | harness: sensor 6 | tags: 7 | - samples 8 | - sensor 9 | depends_on: arduino_i2c 10 | -------------------------------------------------------------------------------- /bme_688/boards/thingy91x_nrf9151_ns.overlay: -------------------------------------------------------------------------------- 1 | 2 | &i2c2 { 3 | status = "okay"; 4 | 5 | bme680@76 { 6 | compatible = "bosch,bme680"; 7 | reg = <0x76>; 8 | status = "okay"; 9 | }; 10 | }; 11 | -------------------------------------------------------------------------------- /rgb_led/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: Apache-2.0 2 | 3 | cmake_minimum_required(VERSION 3.20.0) 4 | find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) 5 | project(rgb_led) 6 | 7 | target_sources(app PRIVATE src/main.c) 8 | -------------------------------------------------------------------------------- /rgb_led/prj.conf: -------------------------------------------------------------------------------- 1 | # Enable logging and debug print 2 | CONFIG_PRINTK=y 3 | CONFIG_LOG=y 4 | 5 | # Enable PWM driver 6 | CONFIG_GPIO=y 7 | 8 | # Enable device power management support (safe check) 9 | CONFIG_PM_DEVICE=y 10 | CONFIG_PM_DEVICE_RUNTIME=y 11 | -------------------------------------------------------------------------------- /bmi270/app.overlay: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021 Bosch Sensortec GmbH 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | &spi3 { 8 | 9 | bmi270: bmi270@2 { 10 | compatible = "bosch,bmi270"; 11 | status = "okay"; 12 | }; 13 | }; 14 | 15 | -------------------------------------------------------------------------------- /bmi270/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (c) 2021 Bosch Sensortec GmbH 3 | # 4 | # SPDX-License-Identifier: Apache-2.0 5 | # 6 | 7 | cmake_minimum_required(VERSION 3.20.0) 8 | find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) 9 | project(bmi270) 10 | 11 | FILE(GLOB app_sources src/*.c) 12 | target_sources(app PRIVATE ${app_sources}) 13 | -------------------------------------------------------------------------------- /rgb_led/sample.yaml: -------------------------------------------------------------------------------- 1 | sample: 2 | name: RGB LED 3 | tests: 4 | sample.basic.rgb_led: 5 | filter: dt_alias_exists("red-pwm-led") and dt_alias_exists("green-pwm-led") 6 | and dt_alias_exists("blue-pwm-led") 7 | tags: 8 | - drivers 9 | - pwm 10 | depends_on: pwm 11 | harness: led 12 | integration_platforms: 13 | - nrf52840_mdk 14 | -------------------------------------------------------------------------------- /bme_688/Kconfig: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (c) 2023 Nordic Semiconductor 3 | # 4 | # SPDX-License-Identifier: LicenseRef-Nordic-5-Clause 5 | # 6 | 7 | menu "Zephyr" 8 | source "Kconfig.zephyr" 9 | endmenu 10 | 11 | config APP_TRIGGER 12 | bool "Print sensor values on trigger" 13 | default y 14 | 15 | module = APP 16 | module-str = APP 17 | source "subsys/logging/Kconfig.template.log_config" 18 | -------------------------------------------------------------------------------- /bme_688/boards/nrf9160dk_nrf9160_ns.overlay: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023 Nordic Semiconductor ASA 3 | * 4 | * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause 5 | */ 6 | &arduino_spi { 7 | status = "okay"; 8 | 9 | bme680@0 { 10 | compatible = "bosch,bme680"; 11 | reg = <0x0 >; 12 | spi-max-frequency = ; 13 | }; 14 | }; 15 | -------------------------------------------------------------------------------- /projects/temperature_sensing/Kconfig: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (c) 2023 Nordic Semiconductor 3 | # 4 | # SPDX-License-Identifier: LicenseRef-Nordic-5-Clause 5 | # 6 | 7 | menu "Zephyr" 8 | source "Kconfig.zephyr" 9 | endmenu 10 | 11 | config APP_TRIGGER 12 | bool "Print sensor values on trigger" 13 | default y 14 | 15 | module = APP 16 | module-str = APP 17 | source "subsys/logging/Kconfig.template.log_config" 18 | -------------------------------------------------------------------------------- /projects/temperature_sensing/boards/nrf9160dk_nrf9160_ns.overlay: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023 Nordic Semiconductor ASA 3 | * 4 | * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause 5 | */ 6 | &arduino_spi { 7 | status = "okay"; 8 | 9 | bme680@0 { 10 | compatible = "bosch,bme680"; 11 | reg = <0x0 >; 12 | spi-max-frequency = ; 13 | }; 14 | }; 15 | -------------------------------------------------------------------------------- /gnss/overlay-supl.conf: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (c) 2023 Nordic Semiconductor ASA 3 | # 4 | # SPDX-License-Identifier: LicenseRef-Nordic-5-Clause 5 | # 6 | 7 | # SUPL overlay configuration 8 | 9 | # Enable SUPL assistance for GNSS sample 10 | CONFIG_GNSS_SAMPLE_ASSISTANCE_SUPL=y 11 | 12 | # SUPL client library requires that the newlib C library is used 13 | CONFIG_NEWLIB_LIBC=y 14 | CONFIG_NEWLIB_LIBC_FLOAT_PRINTF=y 15 | -------------------------------------------------------------------------------- /bme_688/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (c) 2023 Nordic Semiconductor 3 | # 4 | # SPDX-License-Identifier: LicenseRef-Nordic-5-Clause 5 | # 6 | 7 | cmake_minimum_required(VERSION 3.20.0) 8 | 9 | find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) 10 | project(bme68x_iaq) 11 | 12 | FILE(GLOB app_sources src/*.c) 13 | # NORDIC SDK APP START 14 | target_sources(app PRIVATE ${app_sources}) 15 | # NORDIC SDK APP END 16 | -------------------------------------------------------------------------------- /projects/geofencing/overlay-supl.conf: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (c) 2023 Nordic Semiconductor ASA 3 | # 4 | # SPDX-License-Identifier: LicenseRef-Nordic-5-Clause 5 | # 6 | 7 | # SUPL overlay configuration 8 | 9 | # Enable SUPL assistance for GNSS sample 10 | CONFIG_GNSS_SAMPLE_ASSISTANCE_SUPL=y 11 | 12 | # SUPL client library requires that the newlib C library is used 13 | CONFIG_NEWLIB_LIBC=y 14 | CONFIG_NEWLIB_LIBC_FLOAT_PRINTF=y 15 | -------------------------------------------------------------------------------- /projects/temperature_sensing/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (c) 2023 Nordic Semiconductor 3 | # 4 | # SPDX-License-Identifier: LicenseRef-Nordic-5-Clause 5 | # 6 | 7 | cmake_minimum_required(VERSION 3.20.0) 8 | 9 | find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) 10 | project(bme68x_iaq_1) 11 | 12 | FILE(GLOB app_sources src/*.c) 13 | # NORDIC SDK APP START 14 | target_sources(app PRIVATE ${app_sources}) 15 | # NORDIC SDK APP END 16 | -------------------------------------------------------------------------------- /bmi270/prj.conf: -------------------------------------------------------------------------------- 1 | # Console and sensor framework 2 | CONFIG_STDOUT_CONSOLE=y 3 | CONFIG_SENSOR=y 4 | 5 | # SPI is needed for BMI270 on SPI3 6 | CONFIG_SPI=y 7 | 8 | # Disable I2C for BMI270 if it's not being used 9 | CONFIG_I2C=n 10 | 11 | # Enable BMI270 driver over SPI 12 | CONFIG_BMI270=y 13 | CONFIG_BMI270_TRIGGER_GLOBAL_THREAD=y 14 | # Optional alternative: 15 | # CONFIG_BMI270_TRIGGER_OWN_THREAD=y 16 | 17 | # Optional if using Zephyr device tree alias like accel0 18 | CONFIG_SENSOR_INIT_PRIORITY=90 19 | -------------------------------------------------------------------------------- /bme_688/prj.conf: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (c) 2023 Nordic Semiconductor 3 | # 4 | # SPDX-License-Identifier: LicenseRef-Nordic-5-Clause 5 | # 6 | 7 | CONFIG_SENSOR=y 8 | CONFIG_SENSOR_INFO=y 9 | 10 | CONFIG_BME680=y 11 | 12 | 13 | CONFIG_BME68X_IAQ=n 14 | 15 | # Settings - Used to store real-time device configuration to flash. 16 | CONFIG_SETTINGS=y 17 | CONFIG_SETTINGS_FCB=y 18 | CONFIG_FCB=y 19 | CONFIG_FLASH=y 20 | CONFIG_FLASH_PAGE_LAYOUT=y 21 | CONFIG_FLASH_MAP=y 22 | CONFIG_STREAM_FLASH=y 23 | CONFIG_MPU_ALLOW_FLASH_WRITE=y 24 | 25 | CONFIG_LOG=y 26 | CONFIG_CONSOLE=y 27 | 28 | CONFIG_ASSERT=y 29 | CONFIG_REBOOT=y 30 | CONFIG_FPU=y 31 | -------------------------------------------------------------------------------- /gnss/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (c) 2019 Nordic Semiconductor 3 | # 4 | # SPDX-License-Identifier: LicenseRef-Nordic-5-Clause 5 | # 6 | 7 | cmake_minimum_required(VERSION 3.20.0) 8 | 9 | find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) 10 | project(gnss_1) 11 | 12 | zephyr_library_sources(src/main.c) 13 | 14 | zephyr_library_sources_ifdef(CONFIG_GNSS_SAMPLE_ASSISTANCE_NRF_CLOUD src/assistance.c) 15 | zephyr_library_sources_ifdef(CONFIG_GNSS_SAMPLE_ASSISTANCE_SUPL src/assistance_supl.c) 16 | zephyr_library_sources_ifdef(CONFIG_GNSS_SAMPLE_ASSISTANCE_MINIMAL src/assistance_minimal.c) 17 | zephyr_library_sources_ifdef(CONFIG_GNSS_SAMPLE_ASSISTANCE_MINIMAL src/mcc_location_table.c) 18 | -------------------------------------------------------------------------------- /projects/geofencing/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (c) 2019 Nordic Semiconductor 3 | # 4 | # SPDX-License-Identifier: LicenseRef-Nordic-5-Clause 5 | # 6 | 7 | cmake_minimum_required(VERSION 3.20.0) 8 | 9 | find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) 10 | project(gnss) 11 | 12 | zephyr_library_sources(src/main.c) 13 | 14 | zephyr_library_sources_ifdef(CONFIG_GNSS_SAMPLE_ASSISTANCE_NRF_CLOUD src/assistance.c) 15 | zephyr_library_sources_ifdef(CONFIG_GNSS_SAMPLE_ASSISTANCE_SUPL src/assistance_supl.c) 16 | zephyr_library_sources_ifdef(CONFIG_GNSS_SAMPLE_ASSISTANCE_MINIMAL src/assistance_minimal.c) 17 | zephyr_library_sources_ifdef(CONFIG_GNSS_SAMPLE_ASSISTANCE_MINIMAL src/mcc_location_table.c) 18 | -------------------------------------------------------------------------------- /projects/geofencing/prj.conf: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (c) 2019 Nordic Semiconductor ASA 3 | # 4 | # SPDX-License-Identifier: LicenseRef-Nordic-5-Clause 5 | # 6 | 7 | # General 8 | CONFIG_FPU=y 9 | CONFIG_NRF_MODEM_LIB=y 10 | CONFIG_STDOUT_CONSOLE=y 11 | CONFIG_UART_INTERRUPT_DRIVEN=y 12 | CONFIG_PICOLIBC_IO_FLOAT=y 13 | 14 | # Logging 15 | CONFIG_LOG=y 16 | CONFIG_LOG_MODE_IMMEDIATE=y 17 | CONFIG_GNSS_SAMPLE_LOG_LEVEL_INF=y 18 | 19 | # LED Support 20 | CONFIG_GPIO=y 21 | 22 | # GNSS Configuration - No assistance 23 | CONFIG_GNSS_SAMPLE_ASSISTANCE_NONE=y 24 | 25 | # Minimal LTE Link Control (required for modem initialization) 26 | CONFIG_LTE_LINK_CONTROL=y 27 | 28 | # Memory and stack configuration 29 | CONFIG_HEAP_MEM_POOL_SIZE=2048 30 | CONFIG_MAIN_STACK_SIZE=4096 31 | CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE=1536 -------------------------------------------------------------------------------- /gnss/overlay-pgps.conf: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (c) 2021 Nordic Semiconductor ASA 3 | # 4 | # SPDX-License-Identifier: LicenseRef-Nordic-5-Clause 5 | # 6 | 7 | # P-GPS overlay configuration 8 | 9 | # Enable nRF Cloud assistance for GNSS sample 10 | CONFIG_GNSS_SAMPLE_ASSISTANCE_NRF_CLOUD=y 11 | 12 | # P-GPS 13 | CONFIG_NRF_CLOUD_PGPS=y 14 | CONFIG_NRF_CLOUD_PGPS_REPLACEMENT_THRESHOLD=4 15 | 16 | # Disable A-GNSS, comment this if you want to use both A-GNSS and P-GPS at the same time 17 | CONFIG_NRF_CLOUD_AGNSS=n 18 | 19 | # Storage for P-GPS 20 | CONFIG_STREAM_FLASH=y 21 | CONFIG_FLASH=y 22 | CONFIG_FLASH_PAGE_LAYOUT=y 23 | CONFIG_FLASH_MAP=y 24 | CONFIG_FCB=y 25 | CONFIG_SETTINGS=y 26 | CONFIG_SETTINGS_FCB=y 27 | CONFIG_MPU_ALLOW_FLASH_WRITE=y 28 | 29 | # P-GPS needs more heap 30 | CONFIG_HEAP_MEM_POOL_SIZE=8192 31 | -------------------------------------------------------------------------------- /projects/geofencing/overlay-pgps.conf: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (c) 2021 Nordic Semiconductor ASA 3 | # 4 | # SPDX-License-Identifier: LicenseRef-Nordic-5-Clause 5 | # 6 | 7 | # P-GPS overlay configuration 8 | 9 | # Enable nRF Cloud assistance for GNSS sample 10 | CONFIG_GNSS_SAMPLE_ASSISTANCE_NRF_CLOUD=y 11 | 12 | # P-GPS 13 | CONFIG_NRF_CLOUD_PGPS=y 14 | CONFIG_NRF_CLOUD_PGPS_REPLACEMENT_THRESHOLD=4 15 | 16 | # Disable A-GNSS, comment this if you want to use both A-GNSS and P-GPS at the same time 17 | CONFIG_NRF_CLOUD_AGNSS=n 18 | 19 | # Storage for P-GPS 20 | CONFIG_STREAM_FLASH=y 21 | CONFIG_FLASH=y 22 | CONFIG_FLASH_PAGE_LAYOUT=y 23 | CONFIG_FLASH_MAP=y 24 | CONFIG_FCB=y 25 | CONFIG_SETTINGS=y 26 | CONFIG_SETTINGS_FCB=y 27 | CONFIG_MPU_ALLOW_FLASH_WRITE=y 28 | 29 | # P-GPS needs more heap 30 | CONFIG_HEAP_MEM_POOL_SIZE=8192 31 | -------------------------------------------------------------------------------- /projects/temperature_sensing/boards/thingy91x_nrf9151_ns.overlay: -------------------------------------------------------------------------------- 1 | / { 2 | leds { 3 | compatible = "gpio-leds"; 4 | 5 | red_led: led_1 { 6 | gpios = <&gpio0 29 GPIO_ACTIVE_LOW>; 7 | label = "RGB red channel"; 8 | }; 9 | 10 | green_led: led_2 { 11 | gpios = <&gpio0 30 GPIO_ACTIVE_LOW>; 12 | label = "RGB green channel"; 13 | }; 14 | 15 | blue_led: led_3 { 16 | gpios = <&gpio0 31 GPIO_ACTIVE_LOW>; 17 | label = "RGB blue channel"; 18 | }; 19 | }; 20 | 21 | aliases { 22 | led0 = &red_led; 23 | led1 = &green_led; 24 | led2 = &blue_led; 25 | }; 26 | }; 27 | &i2c2 { 28 | status = "okay"; 29 | 30 | bme680@76 { 31 | compatible = "bosch,bme680"; 32 | reg = <0x76>; 33 | status = "okay"; 34 | }; 35 | }; 36 | 37 | &gpio0 { 38 | status = "okay"; 39 | }; 40 | -------------------------------------------------------------------------------- /gnss/src/assistance.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2019 Nordic Semiconductor ASA 3 | * 4 | * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause 5 | */ 6 | 7 | #ifndef ASSISTANCE_H_ 8 | #define ASSISTANCE_H_ 9 | 10 | #ifdef __cplusplus 11 | extern "C" { 12 | #endif 13 | 14 | /** 15 | * @brief Initializes the assistance module. 16 | * 17 | * @param[in] assistance_work_q Work queue that can be used by the assistance module. 18 | * 19 | * @retval 0 on success. 20 | * @retval -1 in case of an error. 21 | */ 22 | int assistance_init(struct k_work_q *assistance_work_q); 23 | 24 | /** 25 | * @brief Handles an assistance data request. 26 | * 27 | * @details Fetches and injects assistance data to the GNSS. 28 | * 29 | * @param[in] agnss_request A-GNSS data requested by GNSS. 30 | * 31 | * @retval 0 on success. 32 | * @retval <0 in case of an error. 33 | */ 34 | int assistance_request(struct nrf_modem_gnss_agnss_data_frame *agnss_request); 35 | 36 | /** 37 | * @brief Returns assistance module state. 38 | * 39 | * @retval true if assistance module is downloading data. 40 | * @retval false if assistance module is idle. 41 | */ 42 | bool assistance_is_active(void); 43 | 44 | #ifdef __cplusplus 45 | } 46 | #endif 47 | 48 | #endif /* ASSISTANCE_H_ */ 49 | -------------------------------------------------------------------------------- /projects/geofencing/src/assistance.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2019 Nordic Semiconductor ASA 3 | * 4 | * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause 5 | */ 6 | 7 | #ifndef ASSISTANCE_H_ 8 | #define ASSISTANCE_H_ 9 | 10 | #ifdef __cplusplus 11 | extern "C" { 12 | #endif 13 | 14 | /** 15 | * @brief Initializes the assistance module. 16 | * 17 | * @param[in] assistance_work_q Work queue that can be used by the assistance module. 18 | * 19 | * @retval 0 on success. 20 | * @retval -1 in case of an error. 21 | */ 22 | int assistance_init(struct k_work_q *assistance_work_q); 23 | 24 | /** 25 | * @brief Handles an assistance data request. 26 | * 27 | * @details Fetches and injects assistance data to the GNSS. 28 | * 29 | * @param[in] agnss_request A-GNSS data requested by GNSS. 30 | * 31 | * @retval 0 on success. 32 | * @retval <0 in case of an error. 33 | */ 34 | int assistance_request(struct nrf_modem_gnss_agnss_data_frame *agnss_request); 35 | 36 | /** 37 | * @brief Returns assistance module state. 38 | * 39 | * @retval true if assistance module is downloading data. 40 | * @retval false if assistance module is idle. 41 | */ 42 | bool assistance_is_active(void); 43 | 44 | #ifdef __cplusplus 45 | } 46 | #endif 47 | 48 | #endif /* ASSISTANCE_H_ */ 49 | -------------------------------------------------------------------------------- /gnss/prj.conf: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (c) 2019 Nordic Semiconductor ASA 3 | # 4 | # SPDX-License-Identifier: LicenseRef-Nordic-5-Clause 5 | # 6 | 7 | # General 8 | CONFIG_FPU=y 9 | CONFIG_NRF_MODEM_LIB=y 10 | CONFIG_STDOUT_CONSOLE=y 11 | CONFIG_UART_INTERRUPT_DRIVEN=y 12 | CONFIG_PICOLIBC_IO_FLOAT=y 13 | CONFIG_LOG=y 14 | CONFIG_LOG_MODE_IMMEDIATE=y 15 | 16 | # GNSS sample 17 | # Enable to use nRF Cloud A-GNSS 18 | CONFIG_GNSS_SAMPLE_ASSISTANCE_NRF_CLOUD=n 19 | 20 | # LTE Link Control 21 | CONFIG_LTE_LINK_CONTROL=y 22 | # Request eDRX from the network 23 | CONFIG_LTE_LC_EDRX_MODULE=y 24 | CONFIG_LTE_EDRX_REQ=y 25 | # PSM requested periodic TAU 8 hours 26 | CONFIG_LTE_LC_PSM_MODULE=y 27 | CONFIG_LTE_PSM_REQ_RPTAU="00101000" 28 | # PSM requested active time 6 seconds 29 | CONFIG_LTE_PSM_REQ_RAT="00000011" 30 | 31 | # AT Host library - Used to send AT commands directy from an UART terminal and to allow 32 | # integration with nRF Connect for Desktop LTE Link monitor application. 33 | CONFIG_AT_HOST_LIBRARY=y 34 | 35 | # Networking 36 | CONFIG_NETWORKING=y 37 | CONFIG_NET_SOCKETS_OFFLOAD=y 38 | CONFIG_NET_SOCKETS=y 39 | CONFIG_POSIX_API=y 40 | # Disable native network stack to save some memory 41 | CONFIG_NET_NATIVE=n 42 | 43 | # Memory and stack configuration 44 | CONFIG_HEAP_MEM_POOL_SIZE=2048 45 | CONFIG_MAIN_STACK_SIZE=4096 46 | CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE=1536 47 | -------------------------------------------------------------------------------- /bme_688/sample.yaml: -------------------------------------------------------------------------------- 1 | sample: 2 | name: BME68X Sensor Sample 3 | tests: 4 | sample.sensor.bme68x.i2c: 5 | sysbuild: true 6 | depends_on: i2c 7 | harness: sensor 8 | integration_platforms: 9 | - thingy91/nrf9160 10 | - thingy91/nrf9160/ns 11 | - thingy53/nrf5340/cpuapp 12 | - thingy53/nrf5340/cpuapp/ns 13 | platform_allow: >- 14 | thingy91/nrf9160 15 | thingy91/nrf9160/ns 16 | thingy53/nrf5340/cpuapp 17 | thingy53/nrf5340/cpuapp/ns 18 | tags: sensors sysbuild ci_samples_sensor 19 | sample.sensor.bme68x.polling: 20 | sysbuild: true 21 | depends_on: i2c 22 | integration_platforms: 23 | - thingy91/nrf9160 24 | - thingy91/nrf9160/ns 25 | - thingy53/nrf5340/cpuapp 26 | - thingy53/nrf5340/cpuapp/ns 27 | extra_args: CONFIG_APP_TRIGGER=n 28 | platform_allow: >- 29 | thingy91/nrf9160 30 | thingy91/nrf9160/ns 31 | thingy53/nrf5340/cpuapp 32 | thingy53/nrf5340/cpuapp/ns 33 | tags: sensors sysbuild ci_samples_sensor 34 | harness: console 35 | harness_config: 36 | type: multi_line 37 | ordered: true 38 | regex: 39 | - "(\\d+\\.\\d+); press: (\\d+\\.\\d+); humidity: (\\d+\\.\\d+); iaq: (\\d+)" 40 | sample.sensor.bme68x.spi: 41 | sysbuild: true 42 | depends_on: spi 43 | harness: sensor 44 | platform_allow: >- 45 | nrf9160dk/nrf9160/ns 46 | tags: sensors sysbuild ci_samples_sensor 47 | -------------------------------------------------------------------------------- /rgb_led/boards/ucans32k1sic.overlay: -------------------------------------------------------------------------------- 1 | / { 2 | leds { 3 | compatible = "gpio-leds"; 4 | 5 | red_led: led_1 { 6 | gpios = <&gpio0 29 GPIO_ACTIVE_LOW>; 7 | label = "RGB red channel"; 8 | }; 9 | 10 | green_led: led_2 { 11 | gpios = <&gpio0 31 GPIO_ACTIVE_LOW>; 12 | label = "RGB green channel"; 13 | }; 14 | 15 | blue_led: led_3 { 16 | gpios = <&gpio0 30 GPIO_ACTIVE_LOW>; 17 | label = "RGB blue channel"; 18 | }; 19 | }; 20 | 21 | zephyr,user { 22 | nrf5340-reset-gpios = <&gpio0 20 GPIO_ACTIVE_LOW>; 23 | }; 24 | 25 | aliases { 26 | sw0 = &button0; 27 | led0 = &red_led; 28 | led1 = &green_led; 29 | led2 = &blue_led; 30 | 31 | // Remove PWM-related aliases if not needed anymore 32 | // pwm-led0 = &pwm_led0; 33 | // pwm-led1 = &pwm_led1; 34 | // pwm-led2 = &pwm_led2; 35 | // rgb-pwm = &pwm0; 36 | 37 | mcuboot-button0 = &button0; 38 | spi-flash0 = &flash_ext; 39 | }; 40 | }; 41 | 42 | &adc { 43 | status = "okay"; 44 | }; 45 | 46 | &gpiote { 47 | status = "okay"; 48 | }; 49 | 50 | &gpio0 { 51 | status = "okay"; 52 | sense-edge-mask = <0xffffffff>; 53 | 54 | exp_board_enable: exp_board_enable { 55 | gpio-hog; 56 | output-low; 57 | gpios = <3 GPIO_ACTIVE_HIGH>; 58 | }; 59 | }; 60 | 61 | // Comment out PWM block if not used anymore 62 | // &pwm0 { 63 | // status = "okay"; 64 | // pinctrl-0 = <&pwm0_default>; 65 | // pinctrl-1 = <&pwm0_sleep>; 66 | // pinctrl-names = "default", "sleep"; 67 | // }; 68 | -------------------------------------------------------------------------------- /projects/temperature_sensing/sample.yaml: -------------------------------------------------------------------------------- 1 | sample: 2 | name: BME68X Sensor Sample 3 | tests: 4 | sample.sensor.bme68x.i2c: 5 | sysbuild: true 6 | depends_on: i2c 7 | harness: sensor 8 | integration_platforms: 9 | - thingy91/nrf9160 10 | - thingy91/nrf9160/ns 11 | - thingy53/nrf5340/cpuapp 12 | - thingy53/nrf5340/cpuapp/ns 13 | platform_allow: >- 14 | thingy91/nrf9160 15 | thingy91/nrf9160/ns 16 | thingy53/nrf5340/cpuapp 17 | thingy53/nrf5340/cpuapp/ns 18 | tags: sensors sysbuild ci_samples_sensor 19 | sample.sensor.bme68x.polling: 20 | sysbuild: true 21 | depends_on: i2c 22 | integration_platforms: 23 | - thingy91/nrf9160 24 | - thingy91/nrf9160/ns 25 | - thingy53/nrf5340/cpuapp 26 | - thingy53/nrf5340/cpuapp/ns 27 | extra_args: CONFIG_APP_TRIGGER=n 28 | platform_allow: >- 29 | thingy91/nrf9160 30 | thingy91/nrf9160/ns 31 | thingy53/nrf5340/cpuapp 32 | thingy53/nrf5340/cpuapp/ns 33 | tags: sensors sysbuild ci_samples_sensor 34 | harness: console 35 | harness_config: 36 | type: multi_line 37 | ordered: true 38 | regex: 39 | - "(\\d+\\.\\d+); press: (\\d+\\.\\d+); humidity: (\\d+\\.\\d+); iaq: (\\d+)" 40 | sample.sensor.bme68x.spi: 41 | sysbuild: true 42 | depends_on: spi 43 | harness: sensor 44 | platform_allow: >- 45 | nrf9160dk/nrf9160/ns 46 | tags: sensors sysbuild ci_samples_sensor 47 | -------------------------------------------------------------------------------- /gnss/src/mcc_location_table.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021 Nordic Semiconductor ASA 3 | * 4 | * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause 5 | */ 6 | 7 | #ifndef MCC_LOCATION_TABLE_H_ 8 | #define MCC_LOCATION_TABLE_H_ 9 | 10 | struct __attribute__ ((__packed__)) mcc_table { 11 | uint8_t confidence; /* percentage, 0-100 */ 12 | uint8_t unc_semiminor; /* scaled, see GNSS interface for details */ 13 | uint8_t unc_semimajor; /* scaled, see GNSS interface for details */ 14 | uint8_t orientation; /* orientation angle between the major axis and north */ 15 | float lat; 16 | float lon; 17 | uint16_t mcc; 18 | }; 19 | 20 | /** 21 | * @brief Finds location information for a given Mobile Country Code (MCC). 22 | * 23 | * @param mcc[in] MCC to look for. 24 | * 25 | * @return Location information for the matching MCC, NULL if no entry was found. 26 | */ 27 | const struct mcc_table *mcc_lookup(uint16_t mcc); 28 | 29 | /** 30 | * @brief Converts a latitude in degrees to the integer representation used by GNSS. 31 | * 32 | * @param lat[in] Latitude in degrees. 33 | * 34 | * @return Latitude in scaled integer representation. 35 | */ 36 | int32_t lat_convert(float lat); 37 | 38 | /** 39 | * @brief Converts a longitude in degrees to the integer representation used by GNSS. 40 | * 41 | * @param lon[in] Longitude in degrees. 42 | * 43 | * @return Longitude in scaled integer representation. 44 | */ 45 | int32_t lon_convert(float lon); 46 | 47 | #endif /* MCC_LOCATION_TABLE_H_ */ 48 | -------------------------------------------------------------------------------- /projects/geofencing/src/mcc_location_table.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021 Nordic Semiconductor ASA 3 | * 4 | * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause 5 | */ 6 | 7 | #ifndef MCC_LOCATION_TABLE_H_ 8 | #define MCC_LOCATION_TABLE_H_ 9 | 10 | struct __attribute__ ((__packed__)) mcc_table { 11 | uint8_t confidence; /* percentage, 0-100 */ 12 | uint8_t unc_semiminor; /* scaled, see GNSS interface for details */ 13 | uint8_t unc_semimajor; /* scaled, see GNSS interface for details */ 14 | uint8_t orientation; /* orientation angle between the major axis and north */ 15 | float lat; 16 | float lon; 17 | uint16_t mcc; 18 | }; 19 | 20 | /** 21 | * @brief Finds location information for a given Mobile Country Code (MCC). 22 | * 23 | * @param mcc[in] MCC to look for. 24 | * 25 | * @return Location information for the matching MCC, NULL if no entry was found. 26 | */ 27 | const struct mcc_table *mcc_lookup(uint16_t mcc); 28 | 29 | /** 30 | * @brief Converts a latitude in degrees to the integer representation used by GNSS. 31 | * 32 | * @param lat[in] Latitude in degrees. 33 | * 34 | * @return Latitude in scaled integer representation. 35 | */ 36 | int32_t lat_convert(float lat); 37 | 38 | /** 39 | * @brief Converts a longitude in degrees to the integer representation used by GNSS. 40 | * 41 | * @param lon[in] Longitude in degrees. 42 | * 43 | * @return Longitude in scaled integer representation. 44 | */ 45 | int32_t lon_convert(float lon); 46 | 47 | #endif /* MCC_LOCATION_TABLE_H_ */ 48 | -------------------------------------------------------------------------------- /bme_688/README.rst: -------------------------------------------------------------------------------- 1 | .. _bme68x: 2 | 3 | BME68X: Gas Sensor 4 | ################## 5 | 6 | .. contents:: 7 | :local: 8 | :depth: 2 9 | 10 | This sample application sets up the BME68X gas sensor with the Bosch Sensor Environmental Cluster (BSEC) library. 11 | 12 | Requirements 13 | ************ 14 | 15 | To use the BME68X IAQ driver, you must manually download the BSEC library. 16 | See the :ref:`bme68x_iaq` documentation for more details. 17 | 18 | The sample supports the following devices: 19 | 20 | .. table-from-sample-yaml:: 21 | 22 | Building and running 23 | ******************** 24 | 25 | This project outputs sensor data to the console. 26 | It requires a BME68X sensor. 27 | 28 | .. |sample path| replace:: :file:`samples/sensor/bme68x_iaq` 29 | 30 | .. include:: /includes/build_and_run.txt 31 | 32 | Testing 33 | ======= 34 | 35 | After programming the sample to your development kit, test it by performing the following steps: 36 | 37 | 1. |connect_terminal| 38 | #. Reset the kit. 39 | #. Observe that output similar to the following is logged on UART: 40 | 41 | .. parsed-literal:: 42 | :class: highlight 43 | 44 | *** Booting Zephyr OS build v3.2.99-ncs1-1531-gaf18f6b63608 *** 45 | [00:00:01.285,339] app: App started 46 | [00:00:07.287,658] app: temp: 28.240385; press: 100043.570312; humidity: 19.981348; iaq: 50 47 | [00:00:10.301,391] app: temp: 28.220613; press: 100039.585937; humidity: 19.983814; iaq: 50 48 | [00:00:13.315,124] app: temp: 28.188013; press: 100040.007812; humidity: 20.015941; iaq: 50 49 | 50 | .. note:: 51 | BSEC takes about 24 hours to calibrate the indoor air quality (IAQ) output. 52 | 53 | References 54 | ********** 55 | 56 | `BME680`_ 57 | -------------------------------------------------------------------------------- /projects/temperature_sensing/README.rst: -------------------------------------------------------------------------------- 1 | .. _bme68x: 2 | 3 | BME68X: Gas Sensor 4 | ################## 5 | 6 | .. contents:: 7 | :local: 8 | :depth: 2 9 | 10 | This sample application sets up the BME68X gas sensor with the Bosch Sensor Environmental Cluster (BSEC) library. 11 | 12 | Requirements 13 | ************ 14 | 15 | To use the BME68X IAQ driver, you must manually download the BSEC library. 16 | See the :ref:`bme68x_iaq` documentation for more details. 17 | 18 | The sample supports the following devices: 19 | 20 | .. table-from-sample-yaml:: 21 | 22 | Building and running 23 | ******************** 24 | 25 | This project outputs sensor data to the console. 26 | It requires a BME68X sensor. 27 | 28 | .. |sample path| replace:: :file:`samples/sensor/bme68x_iaq` 29 | 30 | .. include:: /includes/build_and_run.txt 31 | 32 | Testing 33 | ======= 34 | 35 | After programming the sample to your development kit, test it by performing the following steps: 36 | 37 | 1. |connect_terminal| 38 | #. Reset the kit. 39 | #. Observe that output similar to the following is logged on UART: 40 | 41 | .. parsed-literal:: 42 | :class: highlight 43 | 44 | *** Booting Zephyr OS build v3.2.99-ncs1-1531-gaf18f6b63608 *** 45 | [00:00:01.285,339] app: App started 46 | [00:00:07.287,658] app: temp: 28.240385; press: 100043.570312; humidity: 19.981348; iaq: 50 47 | [00:00:10.301,391] app: temp: 28.220613; press: 100039.585937; humidity: 19.983814; iaq: 50 48 | [00:00:13.315,124] app: temp: 28.188013; press: 100040.007812; humidity: 20.015941; iaq: 50 49 | 50 | .. note:: 51 | BSEC takes about 24 hours to calibrate the indoor air quality (IAQ) output. 52 | 53 | References 54 | ********** 55 | 56 | `BME680`_ 57 | -------------------------------------------------------------------------------- /bmi270/README.rst: -------------------------------------------------------------------------------- 1 | .. zephyr:code-sample:: bmi270 2 | :name: BMI270 6-axis IMU sensor 3 | :relevant-api: sensor_interface 4 | 5 | Configure and read accelerometer and gyroscope data from a BMI270 sensor. 6 | 7 | Description 8 | *********** 9 | 10 | This sample application configures the accelerometer and gyroscope to 11 | measure data at 100Hz. The result is written to the console. 12 | 13 | References 14 | ********** 15 | 16 | - BMI270: https://www.bosch-sensortec.com/products/motion-sensors/imus/bmi270.html 17 | 18 | Wiring 19 | ******* 20 | 21 | This sample uses the BMI270 sensor controlled using the I2C interface. 22 | Connect Supply: **VDD**, **VDDIO**, **GND** and Interface: **SDA**, **SCL**. 23 | The supply voltage can be in the 1.8V to 3.6V range. 24 | Depending on the baseboard used, the **SDA** and **SCL** lines require Pull-Up 25 | resistors. 26 | 27 | Building and Running 28 | ******************** 29 | 30 | This project outputs sensor data to the console. It requires a BMI270 31 | sensor. It should work with any platform featuring a I2C peripheral interface. 32 | It does not work on QEMU. 33 | In this example below the :ref:`nrf52840dk_nrf52840` board is used. 34 | 35 | 36 | .. zephyr-app-commands:: 37 | :zephyr-app: samples/sensor/bmi270 38 | :board: nrf52840dk/nrf52840 39 | :goals: build flash 40 | 41 | Sample Output 42 | ============= 43 | 44 | .. code-block:: console 45 | 46 | Device 0x200014cc name is BMI270 47 | AX: 0.268150; AY: 0.076614; AZ: 9.730035; GX: 0.001065; GY: -0.005326; GZ: -0.004261; 48 | AX: 0.229843; AY: 0.076614; AZ: 9.806650; GX: 0.000532; GY: -0.005592; GZ: -0.002929; 49 | AX: 0.229843; AY: 0.076614; AZ: 9.806650; GX: 0.000266; GY: -0.006125; GZ: -0.002663; 50 | AX: 0.306457; AY: 0.038307; AZ: 9.768342; GX: 0.001331; GY: -0.005326; GZ: -0.004793; 51 | 52 | 53 | -------------------------------------------------------------------------------- /projects/temperature_sensing/src/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | LOG_MODULE_REGISTER(app); 8 | 9 | static const struct gpio_dt_spec red_led = GPIO_DT_SPEC_GET(DT_ALIAS(led0), gpios); 10 | static const struct gpio_dt_spec green_led = GPIO_DT_SPEC_GET(DT_ALIAS(led1), gpios); 11 | static const struct gpio_dt_spec blue_led = GPIO_DT_SPEC_GET(DT_ALIAS(led2), gpios); 12 | 13 | void set_color(int temp) { 14 | // Turn all LEDs off first 15 | gpio_pin_set_dt(&red_led, 1); 16 | gpio_pin_set_dt(&green_led, 1); 17 | gpio_pin_set_dt(&blue_led, 1); 18 | 19 | // Choose color based on temperature 20 | if (temp >= 30) { 21 | gpio_pin_set_dt(&blue_led, 0); // Hot = Blue 22 | } else if (temp >= 20) { 23 | gpio_pin_set_dt(&green_led, 0); // Normal = Green 24 | } else { 25 | gpio_pin_set_dt(&red_led, 0); // Cold = Red 26 | } 27 | } 28 | 29 | int main(void) { 30 | const struct device *const dev = DEVICE_DT_GET_ANY(bosch_bme680); 31 | 32 | if (!device_is_ready(dev)) { 33 | LOG_ERR("BME680 sensor not ready"); 34 | return 0; 35 | } 36 | 37 | if (!device_is_ready(red_led.port) || 38 | !device_is_ready(green_led.port) || 39 | !device_is_ready(blue_led.port)) { 40 | LOG_ERR("LEDs not ready"); 41 | return 0; 42 | } 43 | 44 | gpio_pin_configure_dt(&red_led, GPIO_OUTPUT_INACTIVE); 45 | gpio_pin_configure_dt(&green_led, GPIO_OUTPUT_INACTIVE); 46 | gpio_pin_configure_dt(&blue_led, GPIO_OUTPUT_INACTIVE); 47 | 48 | while (1) { 49 | struct sensor_value temp; 50 | 51 | if (sensor_sample_fetch(dev) == 0 && 52 | sensor_channel_get(dev, SENSOR_CHAN_AMBIENT_TEMP, &temp) == 0) { 53 | 54 | LOG_INF("Temperature: %d.%06d °C", temp.val1, temp.val2); 55 | set_color(temp.val1); 56 | } else { 57 | LOG_ERR("Sensor read error"); 58 | } 59 | 60 | k_sleep(K_SECONDS(2)); 61 | } 62 | 63 | return 0; 64 | } 65 | -------------------------------------------------------------------------------- /rgb_led/README.rst: -------------------------------------------------------------------------------- 1 | .. zephyr:code-sample:: rgb-led 2 | :name: PWM RGB LED 3 | :relevant-api: pwm_interface 4 | 5 | Drive an RGB LED using the PWM API. 6 | 7 | Overview 8 | ******** 9 | 10 | This is a sample app which drives an RGB LED using the :ref:`PWM API `. 11 | 12 | There are three single-color component LEDs in an RGB LED. Each component LED 13 | is driven by a PWM port where the pulse width is changed from zero to the period 14 | indicated in Devicetree. Such period should be fast enough to be above the 15 | flicker fusion threshold (the minimum flicker rate where the LED is perceived as 16 | being steady). The sample causes each LED component to step from dark to max 17 | brightness. Three **for** loops (one for each component LED) generate a gradual 18 | range of color changes from the RGB LED, and the sample repeats forever. 19 | 20 | Requirements 21 | ************ 22 | 23 | The board must have red, green, and blue LEDs connected via PWM output channels. 24 | 25 | The LED PWM channels must be configured using the following :ref:`devicetree 26 | ` aliases, usually in the :ref:`BOARD.dts file 27 | `: 28 | 29 | - ``red-pwm-led`` 30 | - ``green-pwm-led`` 31 | - ``blue-pwm-led`` 32 | 33 | You will see at least one of these errors if you try to build this sample for 34 | an unsupported board: 35 | 36 | .. code-block:: none 37 | 38 | Unsupported board: red-pwm-led devicetree alias is not defined 39 | Unsupported board: green-pwm-led devicetree alias is not defined 40 | Unsupported board: blue-pwm-led devicetree alias is not defined 41 | 42 | See :zephyr_file:`boards/nxp/hexiwear/hexiwear_mk64f12.dts` for an example 43 | :file:`BOARD.dts` file which supports this sample. 44 | 45 | Wiring 46 | ****** 47 | 48 | No additional wiring is necessary if the required devicetree aliases refer to 49 | hardware that is already connected to LEDs on the board. 50 | 51 | Otherwise, LEDs should be connected to the appropriate PWM channels. 52 | 53 | Building and Running 54 | ******************** 55 | 56 | For example, to build and flash this board for :ref:`hexiwear`: 57 | 58 | .. zephyr-app-commands:: 59 | :zephyr-app: samples/basic/rgb_led 60 | :board: hexiwear/mk64f12 61 | :goals: build flash 62 | :compact: 63 | 64 | Change ``hexiwear/mk64f12`` appropriately for other supported boards. 65 | -------------------------------------------------------------------------------- /rgb_led/src/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | // GPIO spec for RGB LEDs 7 | static const struct gpio_dt_spec red_led = GPIO_DT_SPEC_GET(DT_ALIAS(led0), gpios); 8 | static const struct gpio_dt_spec blue_led = GPIO_DT_SPEC_GET(DT_ALIAS(led1), gpios); 9 | static const struct gpio_dt_spec green_led = GPIO_DT_SPEC_GET(DT_ALIAS(led2), gpios); 10 | 11 | 12 | // Button spec 13 | static const struct gpio_dt_spec button = GPIO_DT_SPEC_GET(DT_ALIAS(sw0), gpios); 14 | 15 | enum color_state { 16 | RED, 17 | GREEN, 18 | BLUE 19 | }; 20 | 21 | void set_color(enum color_state color) { 22 | // Turn all LEDs OFF (active-low logic) 23 | gpio_pin_set_dt(&red_led, 1); 24 | gpio_pin_set_dt(&green_led, 1); 25 | gpio_pin_set_dt(&blue_led, 1); 26 | 27 | switch (color) { 28 | case RED: 29 | gpio_pin_set_dt(&red_led, 0); 30 | printf("Button pressed. New color: RED\n"); 31 | break; 32 | case GREEN: 33 | gpio_pin_set_dt(&green_led, 0); 34 | printf("Button pressed. New color: GREEN\n"); 35 | break; 36 | case BLUE: 37 | gpio_pin_set_dt(&blue_led, 0); 38 | printf("Button pressed. New color: BLUE\n"); 39 | break; 40 | } 41 | } 42 | 43 | int main(void) 44 | { 45 | if (!device_is_ready(red_led.port) || 46 | !device_is_ready(green_led.port) || 47 | !device_is_ready(blue_led.port) || 48 | !device_is_ready(button.port)) { 49 | printk("Error: Device not ready\n"); 50 | return 0; 51 | } 52 | 53 | gpio_pin_configure_dt(&red_led, GPIO_OUTPUT_INACTIVE); 54 | gpio_pin_configure_dt(&green_led, GPIO_OUTPUT_INACTIVE); 55 | gpio_pin_configure_dt(&blue_led, GPIO_OUTPUT_INACTIVE); 56 | gpio_pin_configure_dt(&button, GPIO_INPUT); 57 | 58 | enum color_state current_color = RED; 59 | set_color(current_color); 60 | 61 | while (1) { 62 | int val = gpio_pin_get_dt(&button); 63 | if (val == 0) { // Button pressed (active low) 64 | k_msleep(50); // debounce 65 | int val = gpio_pin_get_dt(&button); 66 | if (val == 0) { // Button pressed (active low) 67 | // Manually cycle to next color 68 | if (current_color == RED) { 69 | current_color = GREEN; 70 | } else if (current_color == GREEN) { 71 | current_color = BLUE; 72 | } else { // BLUE 73 | current_color = RED; 74 | } 75 | 76 | set_color(current_color); 77 | 78 | // Wait for button release 79 | while (gpio_pin_get_dt(&button) == 0) { 80 | k_msleep(50); 81 | } 82 | } 83 | } 84 | 85 | k_msleep(50); 86 | } 87 | } -------------------------------------------------------------------------------- /bme_688/src/main.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023 Nordic Semiconductor ASA. 3 | * 4 | * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause 5 | */ 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #include 15 | LOG_MODULE_REGISTER(app, CONFIG_APP_LOG_LEVEL); 16 | 17 | // #if defined(CONFIG_APP_TRIGGER) 18 | // const struct sensor_trigger trig = { 19 | // .chan = SENSOR_CHAN_ALL, 20 | // .type = SENSOR_TRIG_TIMER, 21 | // }; 22 | 23 | // static void trigger_handler(const struct device *dev, const struct sensor_trigger *trig) 24 | // { 25 | // struct sensor_value temp, press, humidity, iaq, co2, voc; 26 | 27 | // sensor_sample_fetch(dev); 28 | // sensor_channel_get(dev, SENSOR_CHAN_AMBIENT_TEMP, &temp); 29 | // sensor_channel_get(dev, SENSOR_CHAN_PRESS, &press); 30 | // sensor_channel_get(dev, SENSOR_CHAN_HUMIDITY, &humidity); 31 | // sensor_channel_get(dev, SENSOR_CHAN_CO2, &co2); 32 | // sensor_channel_get(dev, SENSOR_CHAN_VOC, &voc); 33 | // sensor_channel_get(dev, SENSOR_CHAN_IAQ, &iaq); 34 | 35 | // LOG_INF("temp: %d.%06d; press: %d.%06d; humidity: %d.%06d; iaq: %d; CO2: %d.%06d; VOC: " 36 | // "%d.%06d", 37 | // temp.val1, temp.val2, press.val1, press.val2, humidity.val1, humidity.val2, 38 | // iaq.val1, co2.val1, co2.val2, voc.val1, voc.val2); 39 | // }; 40 | // #endif /* defined(CONFIG_APP_TRIGGER) */ 41 | 42 | int main(void) 43 | { 44 | const struct device *const dev = DEVICE_DT_GET_ANY(bosch_bme680); 45 | 46 | LOG_INF("App started"); 47 | 48 | k_sleep(K_SECONDS(5)); 49 | 50 | if (dev == NULL) { 51 | LOG_ERR("no device found"); 52 | return 0; 53 | } 54 | if (!device_is_ready(dev)) { 55 | LOG_ERR("device is not ready"); 56 | return 0; 57 | } 58 | 59 | // #if defined(CONFIG_APP_TRIGGER) 60 | // int ret = sensor_trigger_set(dev, &trig, trigger_handler); 61 | 62 | // if (ret) { 63 | // LOG_ERR("couldn't set trigger"); 64 | // return 0; 65 | // } 66 | 67 | while (1) { 68 | struct sensor_value temp, press, humidity, iaq, co2, voc; 69 | 70 | sensor_sample_fetch(dev); 71 | sensor_channel_get(dev, SENSOR_CHAN_AMBIENT_TEMP, &temp); 72 | sensor_channel_get(dev, SENSOR_CHAN_PRESS, &press); 73 | sensor_channel_get(dev, SENSOR_CHAN_HUMIDITY, &humidity); 74 | sensor_channel_get(dev, SENSOR_CHAN_IAQ, &iaq); 75 | sensor_channel_get(dev, SENSOR_CHAN_CO2, &co2); 76 | sensor_channel_get(dev, SENSOR_CHAN_VOC, &voc); 77 | 78 | LOG_INF("temp: %d.%06d; press: %d.%06d; humidity: %d.%06d; iaq: %d; CO2: %d.%06d; " 79 | "VOC: %d.%06d", 80 | temp.val1, temp.val2, press.val1, press.val2, humidity.val1, humidity.val2, 81 | iaq.val1, co2.val1, co2.val2, voc.val1, voc.val2); 82 | k_msleep(1000); 83 | } 84 | /* defined(CONFIG_APP_TRIGGER) */ 85 | 86 | return 0; 87 | } 88 | -------------------------------------------------------------------------------- /bmi270/src/main.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021 Bosch Sensortec GmbH 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | int main(void) 13 | { 14 | const struct device *const dev = DEVICE_DT_GET_ONE(bosch_bmi270); 15 | struct sensor_value acc[3], gyr[3]; 16 | struct sensor_value full_scale, sampling_freq, oversampling; 17 | 18 | if (!device_is_ready(dev)) { 19 | printf("Device %s is not ready\n", dev->name); 20 | return 0; 21 | } 22 | 23 | printf("Device %p name is %s\n", dev, dev->name); 24 | 25 | /* Setting scale in G, due to loss of precision if the SI unit m/s^2 26 | * is used 27 | */ 28 | full_scale.val1 = 2; /* G */ 29 | full_scale.val2 = 0; 30 | sampling_freq.val1 = 100; /* Hz. Performance mode */ 31 | sampling_freq.val2 = 0; 32 | oversampling.val1 = 1; /* Normal mode */ 33 | oversampling.val2 = 0; 34 | 35 | sensor_attr_set(dev, SENSOR_CHAN_ACCEL_XYZ, SENSOR_ATTR_FULL_SCALE, 36 | &full_scale); 37 | sensor_attr_set(dev, SENSOR_CHAN_ACCEL_XYZ, SENSOR_ATTR_OVERSAMPLING, 38 | &oversampling); 39 | /* Set sampling frequency last as this also sets the appropriate 40 | * power mode. If already sampling, change to 0.0Hz before changing 41 | * other attributes 42 | */ 43 | sensor_attr_set(dev, SENSOR_CHAN_ACCEL_XYZ, 44 | SENSOR_ATTR_SAMPLING_FREQUENCY, 45 | &sampling_freq); 46 | 47 | 48 | /* Setting scale in degrees/s to match the sensor scale */ 49 | full_scale.val1 = 500; /* dps */ 50 | full_scale.val2 = 0; 51 | sampling_freq.val1 = 100; /* Hz. Performance mode */ 52 | sampling_freq.val2 = 0; 53 | oversampling.val1 = 1; /* Normal mode */ 54 | oversampling.val2 = 0; 55 | 56 | sensor_attr_set(dev, SENSOR_CHAN_GYRO_XYZ, SENSOR_ATTR_FULL_SCALE, 57 | &full_scale); 58 | sensor_attr_set(dev, SENSOR_CHAN_GYRO_XYZ, SENSOR_ATTR_OVERSAMPLING, 59 | &oversampling); 60 | /* Set sampling frequency last as this also sets the appropriate 61 | * power mode. If already sampling, change sampling frequency to 62 | * 0.0Hz before changing other attributes 63 | */ 64 | sensor_attr_set(dev, SENSOR_CHAN_GYRO_XYZ, 65 | SENSOR_ATTR_SAMPLING_FREQUENCY, 66 | &sampling_freq); 67 | 68 | while (1) { 69 | /* 10ms period, 100Hz Sampling frequency */ 70 | k_msleep(10); 71 | 72 | sensor_sample_fetch(dev); 73 | 74 | sensor_channel_get(dev, SENSOR_CHAN_ACCEL_XYZ, acc); 75 | sensor_channel_get(dev, SENSOR_CHAN_GYRO_XYZ, gyr); 76 | 77 | printf("AX: %d.%06d; AY: %d.%06d; AZ: %d.%06d; " 78 | "GX: %d.%06d; GY: %d.%06d; GZ: %d.%06d;\n", 79 | acc[0].val1, acc[0].val2, 80 | acc[1].val1, acc[1].val2, 81 | acc[2].val1, acc[2].val2, 82 | gyr[0].val1, gyr[0].val2, 83 | gyr[1].val1, gyr[1].val2, 84 | gyr[2].val1, gyr[2].val2); 85 | } 86 | return 0; 87 | } 88 | -------------------------------------------------------------------------------- /gnss/src/factory_almanac_v2.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021 Nordic Semiconductor ASA 3 | * 4 | * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause 5 | */ 6 | 7 | #ifndef FACTORY_ALMANAC_V2_H_ 8 | #define FACTORY_ALMANAC_V2_H_ 9 | 10 | /* Factory almanac generated on 2024-12-06 18:30:12. 11 | * 12 | * Note, that the almanac gets more inaccurate with time and it should be updated periodically. 13 | */ 14 | 15 | #define FACTORY_ALMANAC_DATA_V2 \ 16 | "f0ea020031280900000000000000000000000000000000000000000000000000" \ 17 | "00000000000000000000310e870f28099c0f50fd00170da1009209c0fff3d9d3" \ 18 | "ff173dc6ffc9fe020031582e0f2809601d55fd008e0ca1005c6deeff18fd2e00" \ 19 | "82d338008a02020031381a0f2809980f47fd000d0da1003a461a00594386ff8c" \ 20 | "0ab4ff05020200313e2e0f2809cd144afd00520da1008d3becff78573600d40c" \ 21 | "ceff33ff000031871b0f28096f1e63fd00a50da1001139c4ff0632e1ffc4c472" \ 22 | "00e2fff8ff31d19a0f2809e0044ffd00430ca1005d80430097d2aafff0875000" \ 23 | "efff000031d4520f28092b0535fd004f0da1008a4797ff2ebe0f00f8eec0ffc7" \ 24 | "01030031e8190f28098f0b3ffd00f90ba100e4c9170058b35200a440d7fff601" \ 25 | "0400314c520f2809221d55fd001c0da100fc52eeff4b50a0ff14a5100013fffc" \ 26 | "ff31850e0f28093a0f4ffd004e0ca100bc64c5ff125c9fff710e9cffebfc0000" \ 27 | "3105490f28095c0c48fd00d40da10090fa7000f0833b00cbb63200b8fdffff31" \ 28 | "fe470f2809a1134ffd00160da1009daa1e00cfb02700db9db4ffd30200003191" \ 29 | "290f2809780139fd00240da100390b6f00f4578bff5f712c0047020200313184" \ 30 | "0f2809bcfc29fd00610da1002b601200d7573800cc9897fff90001003189750f" \ 31 | "2809f50b46fd00da0ca10054b27100ae022300057fe9ff8eff0300315c6e0f28" \ 32 | "096d0f43fd004b0ca10026439bffb3b3ccff6905bfff2902fcff3162270f2809" \ 33 | "951456fd006d0da100a846c4fffc4d88ff2ed36d004bfd000031b5510f2809e5" \ 34 | "0e41fd00580ca10056109dff40a66d00ff180b0058020100318e1f0f28093609" \ 35 | "3cfd00d00ca100c1d8e6ffd5709dfff5967e0082010000319fd20f2809db0b50" \ 36 | "fd00ac0ca10064b2bfffe15decff4074b9ff6400000031be6f0f28098a0b46fd" \ 37 | "00000da100b7d47100e9ecd3ff4043d4ffa7ffffff3195280f2809f11951fd00" \ 38 | "a30ca100de1aedff48648aff555c40008e0102003118860f280949fa40fd0017" \ 39 | "0da1000f683f007a472a0042d373000efe010031d8630f28098d043ffd00220d" \ 40 | "a100135d6d0084e92c00e7cc2a000b02000031574f0f2809fbf72efd00710da1" \ 41 | "00a9ab6a006a1a1800f3680b00fefffcff313d6a0f28090f0b3dfd000c0da100" \ 42 | "266d98ff09ac200083e3c3ffdcff000031de030f2809140c59fd00cc0da10095" \ 43 | "3e4200741a3a00260d1e00f1fdfdff3182170f28090d1145fd00790da10000e5" \ 44 | "9bff826e6c00bb58bdffb7fd010031e23a0f280934fb42fd00b20da100516843" \ 45 | "008ac39bffbc424900eafe020031d3570f28096e0754fd00640ca1006d694400" \ 46 | "8ee01f005bac240019ff000031a5420f2809b50d44fd00a90da100676a180073" \ 47 | "6ca9ffdfabdcffb2fd0200000000000000000000000000000000000000000000" \ 48 | "0000000000" 49 | #define FACTORY_ALMANAC_CHECKSUM_V2 \ 50 | "2264cef19c4c46673702013eaa4a98f012e8f9e83a60c1c468baeae74f45e82e" 51 | 52 | #endif /* FACTORY_ALMANAC_V2_H_ */ 53 | -------------------------------------------------------------------------------- /projects/geofencing/src/factory_almanac_v2.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021 Nordic Semiconductor ASA 3 | * 4 | * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause 5 | */ 6 | 7 | #ifndef FACTORY_ALMANAC_V2_H_ 8 | #define FACTORY_ALMANAC_V2_H_ 9 | 10 | /* Factory almanac generated on 2024-12-06 18:30:12. 11 | * 12 | * Note, that the almanac gets more inaccurate with time and it should be updated periodically. 13 | */ 14 | 15 | #define FACTORY_ALMANAC_DATA_V2 \ 16 | "f0ea020031280900000000000000000000000000000000000000000000000000" \ 17 | "00000000000000000000310e870f28099c0f50fd00170da1009209c0fff3d9d3" \ 18 | "ff173dc6ffc9fe020031582e0f2809601d55fd008e0ca1005c6deeff18fd2e00" \ 19 | "82d338008a02020031381a0f2809980f47fd000d0da1003a461a00594386ff8c" \ 20 | "0ab4ff05020200313e2e0f2809cd144afd00520da1008d3becff78573600d40c" \ 21 | "ceff33ff000031871b0f28096f1e63fd00a50da1001139c4ff0632e1ffc4c472" \ 22 | "00e2fff8ff31d19a0f2809e0044ffd00430ca1005d80430097d2aafff0875000" \ 23 | "efff000031d4520f28092b0535fd004f0da1008a4797ff2ebe0f00f8eec0ffc7" \ 24 | "01030031e8190f28098f0b3ffd00f90ba100e4c9170058b35200a440d7fff601" \ 25 | "0400314c520f2809221d55fd001c0da100fc52eeff4b50a0ff14a5100013fffc" \ 26 | "ff31850e0f28093a0f4ffd004e0ca100bc64c5ff125c9fff710e9cffebfc0000" \ 27 | "3105490f28095c0c48fd00d40da10090fa7000f0833b00cbb63200b8fdffff31" \ 28 | "fe470f2809a1134ffd00160da1009daa1e00cfb02700db9db4ffd30200003191" \ 29 | "290f2809780139fd00240da100390b6f00f4578bff5f712c0047020200313184" \ 30 | "0f2809bcfc29fd00610da1002b601200d7573800cc9897fff90001003189750f" \ 31 | "2809f50b46fd00da0ca10054b27100ae022300057fe9ff8eff0300315c6e0f28" \ 32 | "096d0f43fd004b0ca10026439bffb3b3ccff6905bfff2902fcff3162270f2809" \ 33 | "951456fd006d0da100a846c4fffc4d88ff2ed36d004bfd000031b5510f2809e5" \ 34 | "0e41fd00580ca10056109dff40a66d00ff180b0058020100318e1f0f28093609" \ 35 | "3cfd00d00ca100c1d8e6ffd5709dfff5967e0082010000319fd20f2809db0b50" \ 36 | "fd00ac0ca10064b2bfffe15decff4074b9ff6400000031be6f0f28098a0b46fd" \ 37 | "00000da100b7d47100e9ecd3ff4043d4ffa7ffffff3195280f2809f11951fd00" \ 38 | "a30ca100de1aedff48648aff555c40008e0102003118860f280949fa40fd0017" \ 39 | "0da1000f683f007a472a0042d373000efe010031d8630f28098d043ffd00220d" \ 40 | "a100135d6d0084e92c00e7cc2a000b02000031574f0f2809fbf72efd00710da1" \ 41 | "00a9ab6a006a1a1800f3680b00fefffcff313d6a0f28090f0b3dfd000c0da100" \ 42 | "266d98ff09ac200083e3c3ffdcff000031de030f2809140c59fd00cc0da10095" \ 43 | "3e4200741a3a00260d1e00f1fdfdff3182170f28090d1145fd00790da10000e5" \ 44 | "9bff826e6c00bb58bdffb7fd010031e23a0f280934fb42fd00b20da100516843" \ 45 | "008ac39bffbc424900eafe020031d3570f28096e0754fd00640ca1006d694400" \ 46 | "8ee01f005bac240019ff000031a5420f2809b50d44fd00a90da100676a180073" \ 47 | "6ca9ffdfabdcffb2fd0200000000000000000000000000000000000000000000" \ 48 | "0000000000" 49 | #define FACTORY_ALMANAC_CHECKSUM_V2 \ 50 | "2264cef19c4c46673702013eaa4a98f012e8f9e83a60c1c468baeae74f45e82e" 51 | 52 | #endif /* FACTORY_ALMANAC_V2_H_ */ 53 | -------------------------------------------------------------------------------- /gnss/src/factory_almanac_v3.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023 Nordic Semiconductor ASA 3 | * 4 | * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause 5 | */ 6 | 7 | #ifndef FACTORY_ALMANAC_V3_H_ 8 | #define FACTORY_ALMANAC_V3_H_ 9 | 10 | /* Factory almanac generated on 2024-12-09 07:50:32. 11 | * 12 | * Note, that the almanac gets more inaccurate with time and it should be updated periodically. 13 | */ 14 | 15 | #define FACTORY_ALMANAC_DATA_V3 \ 16 | "f0ea030031280900000000000000000000000000000000000000000000000000" \ 17 | "00000000000000000000312287392809970f45fd00120da100fefabfffc4e6d3" \ 18 | "ffde88c4ffcbfe0200315f2e392809681d6afd00940ca100465feeffcef02e00" \ 19 | "184237008c02020031331a3928099a0f45fd00fe0ca100a2371a00885786ffdf" \ 20 | "4fb2ff0602020031392e392809d5145efd005d0da100352decff084e3600d669" \ 21 | "ccff33ff0000319c1b3928096b1e5bfd00a40da100022bc4ffd936e1ffbd0d71" \ 22 | "00dcfff8ff31469a392809db044afd00b50da100dc7143008cd8aaff44ac4e00" \ 23 | "efff000031e452392809270527fd00520da100663897ff31cc0f001935bfffc8" \ 24 | "01020031da19392809910b3efd00ea0ba10026bb1700edad520015b4d5fff901" \ 25 | "04003154523928092a1d69fd00240da100e944eeff685aa0ff9af20e0010fffc" \ 26 | "ff318c0e392809370f48fd004d0ca1003756c5ffdb8f9fff12429affebfc0000" \ 27 | "310e493928095e0c52fd00e20da10011ec7000eb8a3b00b9f93000b7fdffff31" \ 28 | "0448392809a2134afd00060da1002d9c1e00bfa92700bbfdb2ffd302000031a3" \ 29 | "29392809790145fd00350da1006dfc6e00eb558bff5eca2a0049020200312f84" \ 30 | "392809c1fc2afd00590da100e6501200b858380007eb95fffa00010031977539" \ 31 | "2809f70b50fd00ec0ca100cea371006d082300aad5e7ff90ff030031496e3928" \ 32 | "09690f33fd004c0ca10057349bff8ebbccff0f65bdff2602fcff316327392809" \ 33 | "92144dfd00690da1004e38c4ff806288ffc7106c004bfd000031c751392809e1" \ 34 | "0e31fd00570ca10085019dff17aa6d00ad7b090058020100318b1f3928093d09" \ 35 | "4ffd00dc0ca10014cae6ff29989dffeecc7c008201000031b1d2392809d60b43" \ 36 | "fd00a50ca100b1a3bfffa467ecff39cbb7ff6400000031a36f3928098c0b52fd" \ 37 | "00110da10032c6710091f2d3ff2c97d2ffa6ffffff319828392809f91966fd00" \ 38 | "ac0ca100b20cedfff5728aff4bae3e008f01020031288639280944fa39fd000c" \ 39 | "0da10024593f00594d2a00552672000efe010031e8633928098e044bfd00310d" \ 40 | "a1005f4e6d0061ee2c002e1f29000a02000031694f392809fcf73bfd00840da1" \ 41 | "00999c6a002b25180068af0900fcfffcff31516a3928090b0b2efd000c0da100" \ 42 | "305e98ff08b52000b433c2ffdcff000031dd03392809100c54fd00c60da1004d" \ 43 | "304200e7223a00dc4f1c00effdfdff319017392809091134fd00750da10040d6" \ 44 | "9bff0b5b6c0048bdbbffb8fd010031ea3a39280930fb3cfd00a70da100805943" \ 45 | "0092c79bff0b8c4700ebfe020031d6573928096a0750fd005f0ca100005b4400" \ 46 | "78eb1f006807230019ff000031b242392809b70d44fd009e0da100c15b18001b" \ 47 | "7ba9ff06ebdaffb4fd0200000000000000000000000000000000000000000000" \ 48 | "0000000000000000000000000000319b7e402809d5ca1aff01b5e8ca00d1c37b" \ 49 | "006c9ebfff1ab133000000000031c97e402809cccb25ff0178eeca008166c2ff" \ 50 | "64fcc0ff8fe1e8fff7ff000031857a402809639debfe013decca0068930700e1" \ 51 | "e5c0ff69a1a2ff16000000000000000000000000000000000000000000000000" \ 52 | "0000000000000000000000000000000000000000000000000000000000000000" \ 53 | "000000000000000000313101402809820063000167ebca00691b2b0016fb5000" \ 54 | "131be9ff00000000000000000000000000000000000000000000000000000000" \ 55 | "0000000000000000000000000000000000000000000000000000000000000000" \ 56 | "0000000000000000000000000000000000000000000000000000000000000000" \ 57 | "00000000000000000000000000000000000000000000000000000000000000" 58 | #define FACTORY_ALMANAC_CHECKSUM_V3 \ 59 | "a41402de5a704975ab78501f3ea26304dc4e0195eb2226bc03c6a3b29cc641b1" 60 | 61 | #endif /* FACTORY_ALMANAC_V3_H_ */ 62 | -------------------------------------------------------------------------------- /projects/geofencing/src/factory_almanac_v3.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023 Nordic Semiconductor ASA 3 | * 4 | * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause 5 | */ 6 | 7 | #ifndef FACTORY_ALMANAC_V3_H_ 8 | #define FACTORY_ALMANAC_V3_H_ 9 | 10 | /* Factory almanac generated on 2024-12-09 07:50:32. 11 | * 12 | * Note, that the almanac gets more inaccurate with time and it should be updated periodically. 13 | */ 14 | 15 | #define FACTORY_ALMANAC_DATA_V3 \ 16 | "f0ea030031280900000000000000000000000000000000000000000000000000" \ 17 | "00000000000000000000312287392809970f45fd00120da100fefabfffc4e6d3" \ 18 | "ffde88c4ffcbfe0200315f2e392809681d6afd00940ca100465feeffcef02e00" \ 19 | "184237008c02020031331a3928099a0f45fd00fe0ca100a2371a00885786ffdf" \ 20 | "4fb2ff0602020031392e392809d5145efd005d0da100352decff084e3600d669" \ 21 | "ccff33ff0000319c1b3928096b1e5bfd00a40da100022bc4ffd936e1ffbd0d71" \ 22 | "00dcfff8ff31469a392809db044afd00b50da100dc7143008cd8aaff44ac4e00" \ 23 | "efff000031e452392809270527fd00520da100663897ff31cc0f001935bfffc8" \ 24 | "01020031da19392809910b3efd00ea0ba10026bb1700edad520015b4d5fff901" \ 25 | "04003154523928092a1d69fd00240da100e944eeff685aa0ff9af20e0010fffc" \ 26 | "ff318c0e392809370f48fd004d0ca1003756c5ffdb8f9fff12429affebfc0000" \ 27 | "310e493928095e0c52fd00e20da10011ec7000eb8a3b00b9f93000b7fdffff31" \ 28 | "0448392809a2134afd00060da1002d9c1e00bfa92700bbfdb2ffd302000031a3" \ 29 | "29392809790145fd00350da1006dfc6e00eb558bff5eca2a0049020200312f84" \ 30 | "392809c1fc2afd00590da100e6501200b858380007eb95fffa00010031977539" \ 31 | "2809f70b50fd00ec0ca100cea371006d082300aad5e7ff90ff030031496e3928" \ 32 | "09690f33fd004c0ca10057349bff8ebbccff0f65bdff2602fcff316327392809" \ 33 | "92144dfd00690da1004e38c4ff806288ffc7106c004bfd000031c751392809e1" \ 34 | "0e31fd00570ca10085019dff17aa6d00ad7b090058020100318b1f3928093d09" \ 35 | "4ffd00dc0ca10014cae6ff29989dffeecc7c008201000031b1d2392809d60b43" \ 36 | "fd00a50ca100b1a3bfffa467ecff39cbb7ff6400000031a36f3928098c0b52fd" \ 37 | "00110da10032c6710091f2d3ff2c97d2ffa6ffffff319828392809f91966fd00" \ 38 | "ac0ca100b20cedfff5728aff4bae3e008f01020031288639280944fa39fd000c" \ 39 | "0da10024593f00594d2a00552672000efe010031e8633928098e044bfd00310d" \ 40 | "a1005f4e6d0061ee2c002e1f29000a02000031694f392809fcf73bfd00840da1" \ 41 | "00999c6a002b25180068af0900fcfffcff31516a3928090b0b2efd000c0da100" \ 42 | "305e98ff08b52000b433c2ffdcff000031dd03392809100c54fd00c60da1004d" \ 43 | "304200e7223a00dc4f1c00effdfdff319017392809091134fd00750da10040d6" \ 44 | "9bff0b5b6c0048bdbbffb8fd010031ea3a39280930fb3cfd00a70da100805943" \ 45 | "0092c79bff0b8c4700ebfe020031d6573928096a0750fd005f0ca100005b4400" \ 46 | "78eb1f006807230019ff000031b242392809b70d44fd009e0da100c15b18001b" \ 47 | "7ba9ff06ebdaffb4fd0200000000000000000000000000000000000000000000" \ 48 | "0000000000000000000000000000319b7e402809d5ca1aff01b5e8ca00d1c37b" \ 49 | "006c9ebfff1ab133000000000031c97e402809cccb25ff0178eeca008166c2ff" \ 50 | "64fcc0ff8fe1e8fff7ff000031857a402809639debfe013decca0068930700e1" \ 51 | "e5c0ff69a1a2ff16000000000000000000000000000000000000000000000000" \ 52 | "0000000000000000000000000000000000000000000000000000000000000000" \ 53 | "000000000000000000313101402809820063000167ebca00691b2b0016fb5000" \ 54 | "131be9ff00000000000000000000000000000000000000000000000000000000" \ 55 | "0000000000000000000000000000000000000000000000000000000000000000" \ 56 | "0000000000000000000000000000000000000000000000000000000000000000" \ 57 | "00000000000000000000000000000000000000000000000000000000000000" 58 | #define FACTORY_ALMANAC_CHECKSUM_V3 \ 59 | "a41402de5a704975ab78501f3ea26304dc4e0195eb2226bc03c6a3b29cc641b1" 60 | 61 | #endif /* FACTORY_ALMANAC_V3_H_ */ 62 | -------------------------------------------------------------------------------- /gnss/sample.yaml: -------------------------------------------------------------------------------- 1 | sample: 2 | name: GNSS sample 3 | tests: 4 | sample.cellular.gnss: 5 | sysbuild: true 6 | build_only: true 7 | integration_platforms: 8 | - nrf9151dk/nrf9151/ns 9 | - nrf9160dk/nrf9160/ns 10 | - nrf9161dk/nrf9161/ns 11 | platform_allow: 12 | - nrf9151dk/nrf9151/ns 13 | - nrf9160dk/nrf9160/ns 14 | - nrf9161dk/nrf9161/ns 15 | tags: ci_build sysbuild ci_samples_cellular 16 | 17 | # Following configurations will be used by the positioning CI integration job to verify PRs 18 | sample.cellular.gnss.integration_config_positioning_agnss_nrfcloud_ltem_pvt: 19 | sysbuild: true 20 | build_only: true 21 | extra_configs: 22 | - CONFIG_LTE_NETWORK_MODE_LTE_M_GPS=y 23 | - CONFIG_LTE_NETWORK_MODE_NBIOT_GPS=n 24 | - CONFIG_GNSS_SAMPLE_LTE_ON_DEMAND=y 25 | - CONFIG_GNSS_SAMPLE_ASSISTANCE_NRF_CLOUD=y 26 | - CONFIG_GNSS_SAMPLE_NMEA_ONLY=n 27 | - CONFIG_GNSS_SAMPLE_MODE_PERIODIC=n 28 | - CONFIG_GNSS_SAMPLE_ASSISTANCE_MINIMAL=n 29 | - CONFIG_LTE_LC_EDRX_MODULE=y 30 | - CONFIG_LTE_EDRX_REQ=y 31 | - CONFIG_FPU=y 32 | - CONFIG_GNSS_SAMPLE_REFERENCE_LATITUDE="61.49375330" 33 | - CONFIG_GNSS_SAMPLE_REFERENCE_LONGITUDE="23.77588976" 34 | - CONFIG_MODEM_ANTENNA_GNSS_EXTERNAL=y 35 | - CONFIG_LOG_BUFFER_SIZE=2048 36 | extra_args: gnss_SNIPPET="nrf91-modem-trace-uart" 37 | integration_platforms: 38 | - nrf9151dk/nrf9151/ns 39 | platform_allow: 40 | - nrf9151dk/nrf9151/ns 41 | tags: ci_build sysbuild ci_samples_cellular 42 | sample.cellular.gnss.integration_config_positioning_agnss_nrfcloud_nbiot_nmea_lte_on: 43 | sysbuild: true 44 | build_only: true 45 | extra_configs: 46 | - CONFIG_LTE_NETWORK_MODE_LTE_M_GPS=n 47 | - CONFIG_LTE_NETWORK_MODE_NBIOT_GPS=y 48 | - CONFIG_GNSS_SAMPLE_LTE_ON_DEMAND=n 49 | - CONFIG_GNSS_SAMPLE_ASSISTANCE_NRF_CLOUD=y 50 | - CONFIG_GNSS_SAMPLE_NMEA_ONLY=y 51 | - CONFIG_GNSS_SAMPLE_MODE_PERIODIC=n 52 | - CONFIG_GNSS_SAMPLE_ASSISTANCE_MINIMAL=n 53 | - CONFIG_LTE_LC_EDRX_MODULE=y 54 | - CONFIG_LTE_EDRX_REQ=y 55 | - CONFIG_FPU=y 56 | - CONFIG_GNSS_SAMPLE_REFERENCE_LATITUDE="61.49375330" 57 | - CONFIG_GNSS_SAMPLE_REFERENCE_LONGITUDE="23.77588976" 58 | - CONFIG_MODEM_ANTENNA_GNSS_EXTERNAL=y 59 | - CONFIG_LOG_BUFFER_SIZE=2048 60 | extra_args: gnss_SNIPPET="nrf91-modem-trace-uart" 61 | integration_platforms: 62 | - nrf9151dk/nrf9151/ns 63 | platform_allow: 64 | - nrf9151dk/nrf9151/ns 65 | tags: ci_build sysbuild ci_samples_cellular 66 | sample.cellular.gnss.integration_config_positioning_pgps_nrfcloud_ltem_nmea: 67 | sysbuild: true 68 | build_only: true 69 | extra_configs: 70 | - CONFIG_LTE_NETWORK_MODE_LTE_M_GPS=y 71 | - CONFIG_LTE_NETWORK_MODE_NBIOT_GPS=n 72 | - CONFIG_GNSS_SAMPLE_LTE_ON_DEMAND=n 73 | - CONFIG_GNSS_SAMPLE_ASSISTANCE_NRF_CLOUD=n 74 | - CONFIG_GNSS_SAMPLE_NMEA_ONLY=y 75 | - CONFIG_GNSS_SAMPLE_MODE_PERIODIC=n 76 | - CONFIG_GNSS_SAMPLE_ASSISTANCE_MINIMAL=n 77 | - CONFIG_LTE_EDRX_REQ=n 78 | - CONFIG_FPU=y 79 | - CONFIG_GNSS_SAMPLE_REFERENCE_LATITUDE="61.49375330" 80 | - CONFIG_GNSS_SAMPLE_REFERENCE_LONGITUDE="23.77588976" 81 | - CONFIG_MODEM_ANTENNA_GNSS_EXTERNAL=y 82 | - CONFIG_LOG_BUFFER_SIZE=2048 83 | extra_args: 84 | - EXTRA_CONF_FILE=overlay-pgps.conf 85 | - gnss_SNIPPET="nrf91-modem-trace-uart" 86 | integration_platforms: 87 | - nrf9151dk/nrf9151/ns 88 | platform_allow: 89 | - nrf9151dk/nrf9151/ns 90 | tags: ci_build sysbuild ci_samples_cellular 91 | sample.cellular.gnss.integration_config_positioning_gnss_ltem_pvt: 92 | sysbuild: true 93 | build_only: true 94 | extra_configs: 95 | - CONFIG_LTE_NETWORK_MODE_LTE_M_GPS=y 96 | - CONFIG_LTE_NETWORK_MODE_NBIOT_GPS=n 97 | - CONFIG_GNSS_SAMPLE_LTE_ON_DEMAND=n 98 | - CONFIG_GNSS_SAMPLE_ASSISTANCE_NRF_CLOUD=n 99 | - CONFIG_GNSS_SAMPLE_NMEA_ONLY=n 100 | - CONFIG_GNSS_SAMPLE_MODE_PERIODIC=n 101 | - CONFIG_GNSS_SAMPLE_ASSISTANCE_MINIMAL=n 102 | - CONFIG_LTE_LC_EDRX_MODULE=y 103 | - CONFIG_LTE_EDRX_REQ=y 104 | - CONFIG_FPU=y 105 | - CONFIG_GNSS_SAMPLE_REFERENCE_LATITUDE="61.49375330" 106 | - CONFIG_GNSS_SAMPLE_REFERENCE_LONGITUDE="23.77588976" 107 | - CONFIG_MODEM_ANTENNA_GNSS_EXTERNAL=y 108 | - CONFIG_LOG_BUFFER_SIZE=2048 109 | extra_args: gnss_SNIPPET="nrf91-modem-trace-uart" 110 | integration_platforms: 111 | - nrf9151dk/nrf9151/ns 112 | platform_allow: 113 | - nrf9151dk/nrf9151/ns 114 | tags: ci_build sysbuild ci_samples_cellular 115 | -------------------------------------------------------------------------------- /projects/geofencing/sample.yaml: -------------------------------------------------------------------------------- 1 | sample: 2 | name: GNSS sample 3 | tests: 4 | sample.cellular.gnss: 5 | sysbuild: true 6 | build_only: true 7 | integration_platforms: 8 | - nrf9151dk/nrf9151/ns 9 | - nrf9160dk/nrf9160/ns 10 | - nrf9161dk/nrf9161/ns 11 | platform_allow: 12 | - nrf9151dk/nrf9151/ns 13 | - nrf9160dk/nrf9160/ns 14 | - nrf9161dk/nrf9161/ns 15 | tags: ci_build sysbuild ci_samples_cellular 16 | 17 | # Following configurations will be used by the positioning CI integration job to verify PRs 18 | sample.cellular.gnss.integration_config_positioning_agnss_nrfcloud_ltem_pvt: 19 | sysbuild: true 20 | build_only: true 21 | extra_configs: 22 | - CONFIG_LTE_NETWORK_MODE_LTE_M_GPS=y 23 | - CONFIG_LTE_NETWORK_MODE_NBIOT_GPS=n 24 | - CONFIG_GNSS_SAMPLE_LTE_ON_DEMAND=y 25 | - CONFIG_GNSS_SAMPLE_ASSISTANCE_NRF_CLOUD=y 26 | - CONFIG_GNSS_SAMPLE_NMEA_ONLY=n 27 | - CONFIG_GNSS_SAMPLE_MODE_PERIODIC=n 28 | - CONFIG_GNSS_SAMPLE_ASSISTANCE_MINIMAL=n 29 | - CONFIG_LTE_LC_EDRX_MODULE=y 30 | - CONFIG_LTE_EDRX_REQ=y 31 | - CONFIG_FPU=y 32 | - CONFIG_GNSS_SAMPLE_REFERENCE_LATITUDE="61.49375330" 33 | - CONFIG_GNSS_SAMPLE_REFERENCE_LONGITUDE="23.77588976" 34 | - CONFIG_MODEM_ANTENNA_GNSS_EXTERNAL=y 35 | - CONFIG_LOG_BUFFER_SIZE=2048 36 | extra_args: gnss_SNIPPET="nrf91-modem-trace-uart" 37 | integration_platforms: 38 | - nrf9151dk/nrf9151/ns 39 | platform_allow: 40 | - nrf9151dk/nrf9151/ns 41 | tags: ci_build sysbuild ci_samples_cellular 42 | sample.cellular.gnss.integration_config_positioning_agnss_nrfcloud_nbiot_nmea_lte_on: 43 | sysbuild: true 44 | build_only: true 45 | extra_configs: 46 | - CONFIG_LTE_NETWORK_MODE_LTE_M_GPS=n 47 | - CONFIG_LTE_NETWORK_MODE_NBIOT_GPS=y 48 | - CONFIG_GNSS_SAMPLE_LTE_ON_DEMAND=n 49 | - CONFIG_GNSS_SAMPLE_ASSISTANCE_NRF_CLOUD=y 50 | - CONFIG_GNSS_SAMPLE_NMEA_ONLY=y 51 | - CONFIG_GNSS_SAMPLE_MODE_PERIODIC=n 52 | - CONFIG_GNSS_SAMPLE_ASSISTANCE_MINIMAL=n 53 | - CONFIG_LTE_LC_EDRX_MODULE=y 54 | - CONFIG_LTE_EDRX_REQ=y 55 | - CONFIG_FPU=y 56 | - CONFIG_GNSS_SAMPLE_REFERENCE_LATITUDE="61.49375330" 57 | - CONFIG_GNSS_SAMPLE_REFERENCE_LONGITUDE="23.77588976" 58 | - CONFIG_MODEM_ANTENNA_GNSS_EXTERNAL=y 59 | - CONFIG_LOG_BUFFER_SIZE=2048 60 | extra_args: gnss_SNIPPET="nrf91-modem-trace-uart" 61 | integration_platforms: 62 | - nrf9151dk/nrf9151/ns 63 | platform_allow: 64 | - nrf9151dk/nrf9151/ns 65 | tags: ci_build sysbuild ci_samples_cellular 66 | sample.cellular.gnss.integration_config_positioning_pgps_nrfcloud_ltem_nmea: 67 | sysbuild: true 68 | build_only: true 69 | extra_configs: 70 | - CONFIG_LTE_NETWORK_MODE_LTE_M_GPS=y 71 | - CONFIG_LTE_NETWORK_MODE_NBIOT_GPS=n 72 | - CONFIG_GNSS_SAMPLE_LTE_ON_DEMAND=n 73 | - CONFIG_GNSS_SAMPLE_ASSISTANCE_NRF_CLOUD=n 74 | - CONFIG_GNSS_SAMPLE_NMEA_ONLY=y 75 | - CONFIG_GNSS_SAMPLE_MODE_PERIODIC=n 76 | - CONFIG_GNSS_SAMPLE_ASSISTANCE_MINIMAL=n 77 | - CONFIG_LTE_EDRX_REQ=n 78 | - CONFIG_FPU=y 79 | - CONFIG_GNSS_SAMPLE_REFERENCE_LATITUDE="61.49375330" 80 | - CONFIG_GNSS_SAMPLE_REFERENCE_LONGITUDE="23.77588976" 81 | - CONFIG_MODEM_ANTENNA_GNSS_EXTERNAL=y 82 | - CONFIG_LOG_BUFFER_SIZE=2048 83 | extra_args: 84 | - EXTRA_CONF_FILE=overlay-pgps.conf 85 | - gnss_SNIPPET="nrf91-modem-trace-uart" 86 | integration_platforms: 87 | - nrf9151dk/nrf9151/ns 88 | platform_allow: 89 | - nrf9151dk/nrf9151/ns 90 | tags: ci_build sysbuild ci_samples_cellular 91 | sample.cellular.gnss.integration_config_positioning_gnss_ltem_pvt: 92 | sysbuild: true 93 | build_only: true 94 | extra_configs: 95 | - CONFIG_LTE_NETWORK_MODE_LTE_M_GPS=y 96 | - CONFIG_LTE_NETWORK_MODE_NBIOT_GPS=n 97 | - CONFIG_GNSS_SAMPLE_LTE_ON_DEMAND=n 98 | - CONFIG_GNSS_SAMPLE_ASSISTANCE_NRF_CLOUD=n 99 | - CONFIG_GNSS_SAMPLE_NMEA_ONLY=n 100 | - CONFIG_GNSS_SAMPLE_MODE_PERIODIC=n 101 | - CONFIG_GNSS_SAMPLE_ASSISTANCE_MINIMAL=n 102 | - CONFIG_LTE_LC_EDRX_MODULE=y 103 | - CONFIG_LTE_EDRX_REQ=y 104 | - CONFIG_FPU=y 105 | - CONFIG_GNSS_SAMPLE_REFERENCE_LATITUDE="61.49375330" 106 | - CONFIG_GNSS_SAMPLE_REFERENCE_LONGITUDE="23.77588976" 107 | - CONFIG_MODEM_ANTENNA_GNSS_EXTERNAL=y 108 | - CONFIG_LOG_BUFFER_SIZE=2048 109 | extra_args: gnss_SNIPPET="nrf91-modem-trace-uart" 110 | integration_platforms: 111 | - nrf9151dk/nrf9151/ns 112 | platform_allow: 113 | - nrf9151dk/nrf9151/ns 114 | tags: ci_build sysbuild ci_samples_cellular 115 | -------------------------------------------------------------------------------- /gnss/Kconfig: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (c) 2020 Nordic Semiconductor ASA 3 | # 4 | # SPDX-License-Identifier: LicenseRef-Nordic-5-Clause 5 | # 6 | 7 | menu "GNSS sample" 8 | 9 | choice 10 | default GNSS_SAMPLE_MODE_CONTINUOUS 11 | prompt "Select GNSS operation mode" 12 | 13 | config GNSS_SAMPLE_MODE_CONTINUOUS 14 | bool "Continuous tracking" 15 | 16 | config GNSS_SAMPLE_MODE_PERIODIC 17 | bool "Periodic fixes" 18 | 19 | config GNSS_SAMPLE_MODE_TTFF_TEST 20 | bool "Time-to-first-fix (TTFF) test mode" 21 | select GNSS_SAMPLE_NMEA_ONLY 22 | 23 | endchoice 24 | 25 | if GNSS_SAMPLE_MODE_PERIODIC 26 | 27 | config GNSS_SAMPLE_PERIODIC_INTERVAL 28 | int "Fix interval for periodic fixes" 29 | range 10 65535 30 | default 120 31 | help 32 | Fix interval (in seconds) for periodic fixes. 33 | 34 | config GNSS_SAMPLE_PERIODIC_TIMEOUT 35 | int "Fix timeout for periodic fixes" 36 | range 0 65535 37 | default 120 38 | help 39 | Fix timeout (in seconds) for periodic fixes. 40 | If set to zero, GNSS is allowed to run indefinitely until a valid PVT estimate is produced. 41 | 42 | endif # GNSS_SAMPLE_MODE_PERIODIC 43 | 44 | if GNSS_SAMPLE_MODE_TTFF_TEST 45 | 46 | config GNSS_SAMPLE_MODE_TTFF_TEST_COLD_START 47 | bool "Delete GNSS data before each start (cold start)" 48 | help 49 | When enabled, deletes all stored data from GNSS to force a cold start every time GNSS is started. 50 | 51 | config GNSS_SAMPLE_MODE_TTFF_TEST_INTERVAL 52 | int "Time to wait between TTFF tests in seconds" 53 | range 1 604800 54 | default 120 55 | 56 | endif # GNSS_SAMPLE_MODE_TTFF_TEST 57 | 58 | config GNSS_SAMPLE_NMEA_ONLY 59 | bool "Output only NMEA strings" 60 | help 61 | Outputs only NMEA strings from the GNSS. 62 | 63 | endmenu 64 | 65 | choice 66 | default GNSS_SAMPLE_ASSISTANCE_NONE 67 | prompt "Select whether GNSS assistance is used or not" 68 | 69 | config GNSS_SAMPLE_ASSISTANCE_NONE 70 | bool "Assistance not used" 71 | 72 | config GNSS_SAMPLE_ASSISTANCE_NRF_CLOUD 73 | bool "Use nRF Cloud A-GNSS" 74 | select NRF_CLOUD_REST 75 | imply NRF_CLOUD_AGNSS 76 | select MODEM_JWT 77 | select MODEM_INFO 78 | select DATE_TIME 79 | 80 | config GNSS_SAMPLE_ASSISTANCE_SUPL 81 | bool "Use SUPL assistance" 82 | select SUPL_CLIENT_LIB 83 | 84 | config GNSS_SAMPLE_ASSISTANCE_MINIMAL 85 | bool "Use factory almanac, LTE network time and MCC based location" 86 | select GNSS_SAMPLE_LTE_ON_DEMAND 87 | select SETTINGS 88 | select FCB 89 | select FLASH 90 | select FLASH_MAP 91 | 92 | endchoice 93 | 94 | if !GNSS_SAMPLE_ASSISTANCE_NONE 95 | 96 | config GNSS_SAMPLE_LTE_ON_DEMAND 97 | bool "LTE is activated only when needed to fetch A-GNSS data" 98 | depends on !NRF_CLOUD_PGPS 99 | help 100 | Activates LTE only when it is needed to fetch A-GNSS data. This is not supported when 101 | P-GPS is enabled. 102 | 103 | endif # !GNSS_SAMPLE_ASSISTANCE_NONE 104 | 105 | if GNSS_SAMPLE_ASSISTANCE_SUPL 106 | 107 | config GNSS_SAMPLE_SUPL_HOSTNAME 108 | string "SUPL server hostname" 109 | help 110 | SUPL server hostname. 111 | 112 | config GNSS_SAMPLE_SUPL_PORT 113 | int "SUPL server port number" 114 | range 0 65535 115 | default 7276 116 | help 117 | SUPL server port number. 118 | 119 | endif # GNSS_SAMPLE_ASSISTANCE_SUPL 120 | 121 | if GNSS_SAMPLE_MODE_CONTINUOUS 122 | 123 | choice 124 | default GNSS_SAMPLE_POWER_SAVING_DISABLED 125 | prompt "Select GNSS power saving mode (duty-cycling)" 126 | 127 | config GNSS_SAMPLE_POWER_SAVING_DISABLED 128 | bool "No power saving" 129 | 130 | config GNSS_SAMPLE_POWER_SAVING_MODERATE 131 | bool "Power saving without significant performance degradation" 132 | 133 | config GNSS_SAMPLE_POWER_SAVING_HIGH 134 | bool "Power saving with acceptable performance degradation" 135 | 136 | endchoice 137 | 138 | endif # GNSS_SAMPLE_MODE_CONTINUOUS 139 | 140 | config GNSS_SAMPLE_REFERENCE_LATITUDE 141 | string "Reference position latitude in decimal degrees" 142 | help 143 | When set, the sample calculates the distance from the reference position for each fix. 144 | Given in decimal degrees (DD), for example "61.500000". 145 | 146 | config GNSS_SAMPLE_REFERENCE_LONGITUDE 147 | string "Reference position longitude in decimal degrees" 148 | help 149 | When set, the sample calculates the distance from the reference position for each fix. 150 | Given in decimal degrees (DD), for example "23.800000". 151 | 152 | config GNSS_SAMPLE_LOW_ACCURACY 153 | bool "Allow low accuracy fixes" 154 | help 155 | Allows fixes with lower accuracy. 156 | 157 | if GNSS_SAMPLE_ASSISTANCE_MINIMAL && GNSS_SAMPLE_LOW_ACCURACY 158 | 159 | config GNSS_SAMPLE_ASSISTANCE_REFERENCE_ALT 160 | int "Reference altitude for 3-sat first fix in meters" 161 | range -32767 32767 162 | default -32767 163 | help 164 | Reference altitude for 3-sat first fix in meters above the reference ellipsoid surface. 165 | The default value -32767 implies that reference altitude is not injected. 166 | 167 | endif # GNSS_SAMPLE_ASSISTANCE_MINIMAL && GNSS_SAMPLE_LOW_ACCURACY 168 | 169 | menu "Zephyr Kernel" 170 | source "Kconfig.zephyr" 171 | endmenu 172 | 173 | module = GNSS_SAMPLE 174 | module-str = GNSS sample 175 | source "${ZEPHYR_BASE}/subsys/logging/Kconfig.template.log_config" 176 | -------------------------------------------------------------------------------- /projects/geofencing/Kconfig: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (c) 2020 Nordic Semiconductor ASA 3 | # 4 | # SPDX-License-Identifier: LicenseRef-Nordic-5-Clause 5 | # 6 | 7 | menu "GNSS sample" 8 | 9 | choice 10 | default GNSS_SAMPLE_MODE_CONTINUOUS 11 | prompt "Select GNSS operation mode" 12 | 13 | config GNSS_SAMPLE_MODE_CONTINUOUS 14 | bool "Continuous tracking" 15 | 16 | config GNSS_SAMPLE_MODE_PERIODIC 17 | bool "Periodic fixes" 18 | 19 | config GNSS_SAMPLE_MODE_TTFF_TEST 20 | bool "Time-to-first-fix (TTFF) test mode" 21 | select GNSS_SAMPLE_NMEA_ONLY 22 | 23 | endchoice 24 | 25 | if GNSS_SAMPLE_MODE_PERIODIC 26 | 27 | config GNSS_SAMPLE_PERIODIC_INTERVAL 28 | int "Fix interval for periodic fixes" 29 | range 10 65535 30 | default 120 31 | help 32 | Fix interval (in seconds) for periodic fixes. 33 | 34 | config GNSS_SAMPLE_PERIODIC_TIMEOUT 35 | int "Fix timeout for periodic fixes" 36 | range 0 65535 37 | default 120 38 | help 39 | Fix timeout (in seconds) for periodic fixes. 40 | If set to zero, GNSS is allowed to run indefinitely until a valid PVT estimate is produced. 41 | 42 | endif # GNSS_SAMPLE_MODE_PERIODIC 43 | 44 | if GNSS_SAMPLE_MODE_TTFF_TEST 45 | 46 | config GNSS_SAMPLE_MODE_TTFF_TEST_COLD_START 47 | bool "Delete GNSS data before each start (cold start)" 48 | help 49 | When enabled, deletes all stored data from GNSS to force a cold start every time GNSS is started. 50 | 51 | config GNSS_SAMPLE_MODE_TTFF_TEST_INTERVAL 52 | int "Time to wait between TTFF tests in seconds" 53 | range 1 604800 54 | default 120 55 | 56 | endif # GNSS_SAMPLE_MODE_TTFF_TEST 57 | 58 | config GNSS_SAMPLE_NMEA_ONLY 59 | bool "Output only NMEA strings" 60 | help 61 | Outputs only NMEA strings from the GNSS. 62 | 63 | endmenu 64 | 65 | choice 66 | default GNSS_SAMPLE_ASSISTANCE_NONE 67 | prompt "Select whether GNSS assistance is used or not" 68 | 69 | config GNSS_SAMPLE_ASSISTANCE_NONE 70 | bool "Assistance not used" 71 | 72 | config GNSS_SAMPLE_ASSISTANCE_NRF_CLOUD 73 | bool "Use nRF Cloud A-GNSS" 74 | select NRF_CLOUD_REST 75 | imply NRF_CLOUD_AGNSS 76 | select MODEM_JWT 77 | select MODEM_INFO 78 | select DATE_TIME 79 | 80 | config GNSS_SAMPLE_ASSISTANCE_SUPL 81 | bool "Use SUPL assistance" 82 | select SUPL_CLIENT_LIB 83 | 84 | config GNSS_SAMPLE_ASSISTANCE_MINIMAL 85 | bool "Use factory almanac, LTE network time and MCC based location" 86 | select GNSS_SAMPLE_LTE_ON_DEMAND 87 | select SETTINGS 88 | select FCB 89 | select FLASH 90 | select FLASH_MAP 91 | 92 | endchoice 93 | 94 | if !GNSS_SAMPLE_ASSISTANCE_NONE 95 | 96 | config GNSS_SAMPLE_LTE_ON_DEMAND 97 | bool "LTE is activated only when needed to fetch A-GNSS data" 98 | depends on !NRF_CLOUD_PGPS 99 | help 100 | Activates LTE only when it is needed to fetch A-GNSS data. This is not supported when 101 | P-GPS is enabled. 102 | 103 | endif # !GNSS_SAMPLE_ASSISTANCE_NONE 104 | 105 | if GNSS_SAMPLE_ASSISTANCE_SUPL 106 | 107 | config GNSS_SAMPLE_SUPL_HOSTNAME 108 | string "SUPL server hostname" 109 | help 110 | SUPL server hostname. 111 | 112 | config GNSS_SAMPLE_SUPL_PORT 113 | int "SUPL server port number" 114 | range 0 65535 115 | default 7276 116 | help 117 | SUPL server port number. 118 | 119 | endif # GNSS_SAMPLE_ASSISTANCE_SUPL 120 | 121 | if GNSS_SAMPLE_MODE_CONTINUOUS 122 | 123 | choice 124 | default GNSS_SAMPLE_POWER_SAVING_DISABLED 125 | prompt "Select GNSS power saving mode (duty-cycling)" 126 | 127 | config GNSS_SAMPLE_POWER_SAVING_DISABLED 128 | bool "No power saving" 129 | 130 | config GNSS_SAMPLE_POWER_SAVING_MODERATE 131 | bool "Power saving without significant performance degradation" 132 | 133 | config GNSS_SAMPLE_POWER_SAVING_HIGH 134 | bool "Power saving with acceptable performance degradation" 135 | 136 | endchoice 137 | 138 | endif # GNSS_SAMPLE_MODE_CONTINUOUS 139 | 140 | config GNSS_SAMPLE_REFERENCE_LATITUDE 141 | string "Reference position latitude in decimal degrees" 142 | help 143 | When set, the sample calculates the distance from the reference position for each fix. 144 | Given in decimal degrees (DD), for example "61.500000". 145 | 146 | config GNSS_SAMPLE_REFERENCE_LONGITUDE 147 | string "Reference position longitude in decimal degrees" 148 | help 149 | When set, the sample calculates the distance from the reference position for each fix. 150 | Given in decimal degrees (DD), for example "23.800000". 151 | 152 | config GNSS_SAMPLE_LOW_ACCURACY 153 | bool "Allow low accuracy fixes" 154 | help 155 | Allows fixes with lower accuracy. 156 | 157 | if GNSS_SAMPLE_ASSISTANCE_MINIMAL && GNSS_SAMPLE_LOW_ACCURACY 158 | 159 | config GNSS_SAMPLE_ASSISTANCE_REFERENCE_ALT 160 | int "Reference altitude for 3-sat first fix in meters" 161 | range -32767 32767 162 | default -32767 163 | help 164 | Reference altitude for 3-sat first fix in meters above the reference ellipsoid surface. 165 | The default value -32767 implies that reference altitude is not injected. 166 | 167 | endif # GNSS_SAMPLE_ASSISTANCE_MINIMAL && GNSS_SAMPLE_LOW_ACCURACY 168 | 169 | menu "Zephyr Kernel" 170 | source "Kconfig.zephyr" 171 | endmenu 172 | 173 | module = GNSS_SAMPLE 174 | module-str = GNSS sample 175 | source "${ZEPHYR_BASE}/subsys/logging/Kconfig.template.log_config" 176 | -------------------------------------------------------------------------------- /gnss/src/assistance_supl.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2019 Nordic Semiconductor ASA 3 | * 4 | * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause 5 | */ 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #include "assistance.h" 15 | 16 | LOG_MODULE_DECLARE(gnss_sample, CONFIG_GNSS_SAMPLE_LOG_LEVEL); 17 | 18 | #define SUPL_SERVER CONFIG_GNSS_SAMPLE_SUPL_HOSTNAME 19 | #define SUPL_SERVER_PORT CONFIG_GNSS_SAMPLE_SUPL_PORT 20 | 21 | BUILD_ASSERT(sizeof(CONFIG_GNSS_SAMPLE_SUPL_HOSTNAME) > 1, "Server hostname must be configured"); 22 | 23 | static int supl_fd = -1; 24 | static volatile bool assistance_active; 25 | 26 | static ssize_t supl_read(void *p_buff, size_t nbytes, void *user_data) 27 | { 28 | ARG_UNUSED(user_data); 29 | 30 | ssize_t rc = recv(supl_fd, p_buff, nbytes, 0); 31 | 32 | if (rc < 0 && (errno == EAGAIN)) { 33 | /* Return 0 to indicate a timeout. */ 34 | rc = 0; 35 | } else if (rc == 0) { 36 | /* Peer closed the socket, return an error. */ 37 | rc = -1; 38 | } 39 | 40 | return rc; 41 | } 42 | 43 | static ssize_t supl_write(const void *p_buff, size_t nbytes, void *user_data) 44 | { 45 | ARG_UNUSED(user_data); 46 | 47 | return send(supl_fd, p_buff, nbytes, 0); 48 | } 49 | 50 | static int inject_agnss_type(void *agnss, size_t agnss_size, uint16_t type, void *user_data) 51 | { 52 | ARG_UNUSED(user_data); 53 | 54 | int retval = nrf_modem_gnss_agnss_write(agnss, agnss_size, type); 55 | 56 | if (retval != 0) { 57 | LOG_ERR("Failed to write A-GNSS data, type: %d (errno: %d)", type, errno); 58 | return -1; 59 | } 60 | 61 | LOG_INF("Injected A-GNSS data, type: %d, size: %d", type, agnss_size); 62 | 63 | return 0; 64 | } 65 | 66 | static int supl_logger(int level, const char *fmt, ...) 67 | { 68 | char buffer[256] = { 0 }; 69 | va_list args; 70 | 71 | va_start(args, fmt); 72 | int ret = vsnprintk(buffer, sizeof(buffer), fmt, args); 73 | 74 | va_end(args); 75 | 76 | if (ret < 0) { 77 | LOG_ERR("%s: encoding error", __func__); 78 | return ret; 79 | } else if ((size_t)ret >= sizeof(buffer)) { 80 | LOG_ERR("%s: too long message," 81 | "it will be cut short", __func__); 82 | } 83 | 84 | LOG_INF("%s", buffer); 85 | 86 | return ret; 87 | } 88 | 89 | static int open_supl_socket(void) 90 | { 91 | int err; 92 | char port[6]; 93 | struct addrinfo *info; 94 | 95 | struct addrinfo hints = { 96 | .ai_flags = AI_NUMERICSERV, 97 | .ai_family = AF_UNSPEC, /* Both IPv4 and IPv6 addresses accepted. */ 98 | .ai_socktype = SOCK_STREAM 99 | }; 100 | 101 | snprintf(port, sizeof(port), "%d", SUPL_SERVER_PORT); 102 | 103 | err = getaddrinfo(SUPL_SERVER, port, &hints, &info); 104 | if (err) { 105 | LOG_ERR("Failed to resolve hostname %s, error: %d", SUPL_SERVER, err); 106 | 107 | return -1; 108 | } 109 | 110 | /* Not connected. */ 111 | err = -1; 112 | 113 | for (struct addrinfo *addr = info; addr != NULL; addr = addr->ai_next) { 114 | char ip[INET6_ADDRSTRLEN] = { 0 }; 115 | struct sockaddr *const sa = addr->ai_addr; 116 | 117 | supl_fd = socket(sa->sa_family, SOCK_STREAM, IPPROTO_TCP); 118 | if (supl_fd < 0) { 119 | LOG_ERR("Failed to create socket, errno %d", errno); 120 | goto cleanup; 121 | } 122 | 123 | /* The SUPL library expects a 1 second timeout for the read function. */ 124 | struct timeval timeout = { 125 | .tv_sec = 1, 126 | .tv_usec = 0, 127 | }; 128 | 129 | err = setsockopt(supl_fd, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout)); 130 | if (err) { 131 | LOG_ERR("Failed to set socket timeout, errno %d", errno); 132 | goto cleanup; 133 | } 134 | 135 | inet_ntop(sa->sa_family, 136 | (void *)&((struct sockaddr_in *)sa)->sin_addr, 137 | ip, 138 | INET6_ADDRSTRLEN); 139 | LOG_INF("Connecting to %s port %d", ip, SUPL_SERVER_PORT); 140 | 141 | err = connect(supl_fd, sa, addr->ai_addrlen); 142 | if (err) { 143 | close(supl_fd); 144 | supl_fd = -1; 145 | 146 | /* Try the next address. */ 147 | LOG_WRN("Connecting to server failed, errno %d", errno); 148 | } else { 149 | /* Connected. */ 150 | break; 151 | } 152 | } 153 | 154 | cleanup: 155 | freeaddrinfo(info); 156 | 157 | if (err) { 158 | /* Unable to connect, close socket. */ 159 | LOG_ERR("Could not connect to SUPL server"); 160 | if (supl_fd > -1) { 161 | close(supl_fd); 162 | supl_fd = -1; 163 | } 164 | return -1; 165 | } 166 | 167 | return 0; 168 | } 169 | 170 | static void close_supl_socket(void) 171 | { 172 | if (close(supl_fd) < 0) { 173 | LOG_ERR("Failed to close SUPL socket"); 174 | } 175 | } 176 | 177 | int assistance_init(struct k_work_q *assistance_work_q) 178 | { 179 | ARG_UNUSED(assistance_work_q); 180 | 181 | static struct supl_api supl_api = { 182 | .read = supl_read, 183 | .write = supl_write, 184 | .handler = inject_agnss_type, 185 | .logger = supl_logger, 186 | .counter_ms = k_uptime_get 187 | }; 188 | 189 | if (supl_init(&supl_api) != 0) { 190 | LOG_ERR("Failed to initialize SUPL library"); 191 | return -1; 192 | } 193 | 194 | return 0; 195 | } 196 | 197 | int assistance_request(struct nrf_modem_gnss_agnss_data_frame *agnss_request) 198 | { 199 | int err; 200 | 201 | assistance_active = true; 202 | 203 | err = open_supl_socket(); 204 | if (err) { 205 | goto exit; 206 | } 207 | 208 | LOG_INF("Starting SUPL session"); 209 | err = supl_session(agnss_request); 210 | LOG_INF("Done"); 211 | close_supl_socket(); 212 | 213 | exit: 214 | assistance_active = false; 215 | 216 | return err; 217 | } 218 | 219 | bool assistance_is_active(void) 220 | { 221 | return assistance_active; 222 | } 223 | -------------------------------------------------------------------------------- /projects/geofencing/src/assistance_supl.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2019 Nordic Semiconductor ASA 3 | * 4 | * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause 5 | */ 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #include "assistance.h" 15 | 16 | LOG_MODULE_DECLARE(gnss_sample, CONFIG_GNSS_SAMPLE_LOG_LEVEL); 17 | 18 | #define SUPL_SERVER CONFIG_GNSS_SAMPLE_SUPL_HOSTNAME 19 | #define SUPL_SERVER_PORT CONFIG_GNSS_SAMPLE_SUPL_PORT 20 | 21 | BUILD_ASSERT(sizeof(CONFIG_GNSS_SAMPLE_SUPL_HOSTNAME) > 1, "Server hostname must be configured"); 22 | 23 | static int supl_fd = -1; 24 | static volatile bool assistance_active; 25 | 26 | static ssize_t supl_read(void *p_buff, size_t nbytes, void *user_data) 27 | { 28 | ARG_UNUSED(user_data); 29 | 30 | ssize_t rc = recv(supl_fd, p_buff, nbytes, 0); 31 | 32 | if (rc < 0 && (errno == EAGAIN)) { 33 | /* Return 0 to indicate a timeout. */ 34 | rc = 0; 35 | } else if (rc == 0) { 36 | /* Peer closed the socket, return an error. */ 37 | rc = -1; 38 | } 39 | 40 | return rc; 41 | } 42 | 43 | static ssize_t supl_write(const void *p_buff, size_t nbytes, void *user_data) 44 | { 45 | ARG_UNUSED(user_data); 46 | 47 | return send(supl_fd, p_buff, nbytes, 0); 48 | } 49 | 50 | static int inject_agnss_type(void *agnss, size_t agnss_size, uint16_t type, void *user_data) 51 | { 52 | ARG_UNUSED(user_data); 53 | 54 | int retval = nrf_modem_gnss_agnss_write(agnss, agnss_size, type); 55 | 56 | if (retval != 0) { 57 | LOG_ERR("Failed to write A-GNSS data, type: %d (errno: %d)", type, errno); 58 | return -1; 59 | } 60 | 61 | LOG_INF("Injected A-GNSS data, type: %d, size: %d", type, agnss_size); 62 | 63 | return 0; 64 | } 65 | 66 | static int supl_logger(int level, const char *fmt, ...) 67 | { 68 | char buffer[256] = { 0 }; 69 | va_list args; 70 | 71 | va_start(args, fmt); 72 | int ret = vsnprintk(buffer, sizeof(buffer), fmt, args); 73 | 74 | va_end(args); 75 | 76 | if (ret < 0) { 77 | LOG_ERR("%s: encoding error", __func__); 78 | return ret; 79 | } else if ((size_t)ret >= sizeof(buffer)) { 80 | LOG_ERR("%s: too long message," 81 | "it will be cut short", __func__); 82 | } 83 | 84 | LOG_INF("%s", buffer); 85 | 86 | return ret; 87 | } 88 | 89 | static int open_supl_socket(void) 90 | { 91 | int err; 92 | char port[6]; 93 | struct addrinfo *info; 94 | 95 | struct addrinfo hints = { 96 | .ai_flags = AI_NUMERICSERV, 97 | .ai_family = AF_UNSPEC, /* Both IPv4 and IPv6 addresses accepted. */ 98 | .ai_socktype = SOCK_STREAM 99 | }; 100 | 101 | snprintf(port, sizeof(port), "%d", SUPL_SERVER_PORT); 102 | 103 | err = getaddrinfo(SUPL_SERVER, port, &hints, &info); 104 | if (err) { 105 | LOG_ERR("Failed to resolve hostname %s, error: %d", SUPL_SERVER, err); 106 | 107 | return -1; 108 | } 109 | 110 | /* Not connected. */ 111 | err = -1; 112 | 113 | for (struct addrinfo *addr = info; addr != NULL; addr = addr->ai_next) { 114 | char ip[INET6_ADDRSTRLEN] = { 0 }; 115 | struct sockaddr *const sa = addr->ai_addr; 116 | 117 | supl_fd = socket(sa->sa_family, SOCK_STREAM, IPPROTO_TCP); 118 | if (supl_fd < 0) { 119 | LOG_ERR("Failed to create socket, errno %d", errno); 120 | goto cleanup; 121 | } 122 | 123 | /* The SUPL library expects a 1 second timeout for the read function. */ 124 | struct timeval timeout = { 125 | .tv_sec = 1, 126 | .tv_usec = 0, 127 | }; 128 | 129 | err = setsockopt(supl_fd, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout)); 130 | if (err) { 131 | LOG_ERR("Failed to set socket timeout, errno %d", errno); 132 | goto cleanup; 133 | } 134 | 135 | inet_ntop(sa->sa_family, 136 | (void *)&((struct sockaddr_in *)sa)->sin_addr, 137 | ip, 138 | INET6_ADDRSTRLEN); 139 | LOG_INF("Connecting to %s port %d", ip, SUPL_SERVER_PORT); 140 | 141 | err = connect(supl_fd, sa, addr->ai_addrlen); 142 | if (err) { 143 | close(supl_fd); 144 | supl_fd = -1; 145 | 146 | /* Try the next address. */ 147 | LOG_WRN("Connecting to server failed, errno %d", errno); 148 | } else { 149 | /* Connected. */ 150 | break; 151 | } 152 | } 153 | 154 | cleanup: 155 | freeaddrinfo(info); 156 | 157 | if (err) { 158 | /* Unable to connect, close socket. */ 159 | LOG_ERR("Could not connect to SUPL server"); 160 | if (supl_fd > -1) { 161 | close(supl_fd); 162 | supl_fd = -1; 163 | } 164 | return -1; 165 | } 166 | 167 | return 0; 168 | } 169 | 170 | static void close_supl_socket(void) 171 | { 172 | if (close(supl_fd) < 0) { 173 | LOG_ERR("Failed to close SUPL socket"); 174 | } 175 | } 176 | 177 | int assistance_init(struct k_work_q *assistance_work_q) 178 | { 179 | ARG_UNUSED(assistance_work_q); 180 | 181 | static struct supl_api supl_api = { 182 | .read = supl_read, 183 | .write = supl_write, 184 | .handler = inject_agnss_type, 185 | .logger = supl_logger, 186 | .counter_ms = k_uptime_get 187 | }; 188 | 189 | if (supl_init(&supl_api) != 0) { 190 | LOG_ERR("Failed to initialize SUPL library"); 191 | return -1; 192 | } 193 | 194 | return 0; 195 | } 196 | 197 | int assistance_request(struct nrf_modem_gnss_agnss_data_frame *agnss_request) 198 | { 199 | int err; 200 | 201 | assistance_active = true; 202 | 203 | err = open_supl_socket(); 204 | if (err) { 205 | goto exit; 206 | } 207 | 208 | LOG_INF("Starting SUPL session"); 209 | err = supl_session(agnss_request); 210 | LOG_INF("Done"); 211 | close_supl_socket(); 212 | 213 | exit: 214 | assistance_active = false; 215 | 216 | return err; 217 | } 218 | 219 | bool assistance_is_active(void) 220 | { 221 | return assistance_active; 222 | } 223 | -------------------------------------------------------------------------------- /projects/geofencing/src/main.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Simplified GNSS Geofencing Application 3 | * - Green LED: Inside geofence 4 | * - Blue LED: Outside geofence 5 | * - Red LED: No GPS fix 6 | */ 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | 21 | LOG_MODULE_REGISTER(geofence_app, CONFIG_GNSS_SAMPLE_LOG_LEVEL); 22 | 23 | #define PI 3.14159265358979323846 24 | #define EARTH_RADIUS_METERS (6371.0 * 1000.0) 25 | 26 | // Geofence configuration 27 | #define GEOFENCE_RADIUS_METERS 1000.0 // 1 km radius 28 | #define GEOFENCE_CENTER_LAT 23.014975 29 | #define GEOFENCE_CENTER_LON 72.639570 30 | 31 | // LED configuration 32 | static const struct gpio_dt_spec red_led = GPIO_DT_SPEC_GET(DT_ALIAS(led0), gpios); 33 | static const struct gpio_dt_spec green_led = GPIO_DT_SPEC_GET(DT_ALIAS(led1), gpios); 34 | static const struct gpio_dt_spec blue_led = GPIO_DT_SPEC_GET(DT_ALIAS(led2), gpios); 35 | 36 | enum color_state { 37 | RED, // No fix 38 | GREEN, // Inside geofence 39 | BLUE, // Outside geofence 40 | OFF 41 | }; 42 | 43 | // Global variables 44 | static struct nrf_modem_gnss_pvt_data_frame last_pvt; 45 | static K_SEM_DEFINE(pvt_data_sem, 0, 1); 46 | 47 | // Convert degrees to radians 48 | static double deg_to_rad(double deg) { 49 | return deg * PI / 180.0; 50 | } 51 | 52 | // Calculate distance using Haversine formula 53 | static double haversine_distance(double lat1, double lon1, double lat2, double lon2) { 54 | double dlat = deg_to_rad(lat2 - lat1); 55 | double dlon = deg_to_rad(lon2 - lon1); 56 | 57 | lat1 = deg_to_rad(lat1); 58 | lat2 = deg_to_rad(lat2); 59 | 60 | double a = sin(dlat / 2.0) * sin(dlat / 2.0) + 61 | cos(lat1) * cos(lat2) * 62 | sin(dlon / 2.0) * sin(dlon / 2.0); 63 | 64 | double c = 2.0 * atan2(sqrt(a), sqrt(1.0 - a)); 65 | 66 | return EARTH_RADIUS_METERS * c; 67 | } 68 | 69 | // Check if point is outside geofence 70 | static bool is_outside_geofence(double lat, double lon) { 71 | double distance = haversine_distance(GEOFENCE_CENTER_LAT, GEOFENCE_CENTER_LON, lat, lon); 72 | LOG_INF("Distance from geofence center: %.2f meters", distance); 73 | return distance > GEOFENCE_RADIUS_METERS; 74 | } 75 | 76 | // Set LED color 77 | void set_color(enum color_state color) { 78 | // Turn all LEDs off first (assuming active low) 79 | gpio_pin_set_dt(&red_led, 1); 80 | gpio_pin_set_dt(&green_led, 1); 81 | gpio_pin_set_dt(&blue_led, 1); 82 | 83 | switch (color) { 84 | case RED: 85 | gpio_pin_set_dt(&red_led, 0); 86 | break; 87 | case GREEN: 88 | gpio_pin_set_dt(&green_led, 0); 89 | break; 90 | case BLUE: 91 | gpio_pin_set_dt(&blue_led, 0); 92 | break; 93 | case OFF: 94 | default: 95 | break; 96 | } 97 | } 98 | 99 | // GNSS event handler 100 | static void gnss_event_handler(int event) { 101 | int retval; 102 | 103 | switch (event) { 104 | case NRF_MODEM_GNSS_EVT_PVT: 105 | retval = nrf_modem_gnss_read(&last_pvt, sizeof(last_pvt), NRF_MODEM_GNSS_DATA_PVT); 106 | if (retval == 0) { 107 | k_sem_give(&pvt_data_sem); 108 | } 109 | break; 110 | default: 111 | break; 112 | } 113 | } 114 | 115 | // Initialize modem (without LTE connection) 116 | static int modem_init(void) { 117 | // No LTE connection needed for basic GNSS operation 118 | LOG_INF("Modem initialized"); 119 | return 0; 120 | } 121 | 122 | // Initialize and start GNSS 123 | static int gnss_init_and_start(void) { 124 | // Enable GNSS functional mode (like in original code for ASSISTANCE_NONE) 125 | if (lte_lc_func_mode_set(LTE_LC_FUNC_MODE_ACTIVATE_GNSS) != 0) { 126 | LOG_ERR("Failed to activate GNSS functional mode"); 127 | return -1; 128 | } 129 | 130 | // Set GNSS event handler 131 | if (nrf_modem_gnss_event_handler_set(gnss_event_handler) != 0) { 132 | LOG_ERR("Failed to set GNSS event handler"); 133 | return -1; 134 | } 135 | 136 | // Configure GNSS use case (from original code) 137 | uint8_t use_case = NRF_MODEM_GNSS_USE_CASE_MULTIPLE_HOT_START; 138 | if (nrf_modem_gnss_use_case_set(use_case) != 0) { 139 | LOG_WRN("Failed to set GNSS use case"); 140 | } 141 | 142 | // Configure for continuous tracking (from original code) 143 | if (nrf_modem_gnss_fix_retry_set(0) != 0) { 144 | LOG_ERR("Failed to set GNSS fix retry"); 145 | return -1; 146 | } 147 | 148 | if (nrf_modem_gnss_fix_interval_set(1) != 0) { 149 | LOG_ERR("Failed to set GNSS fix interval"); 150 | return -1; 151 | } 152 | 153 | // Start GNSS 154 | if (nrf_modem_gnss_start() != 0) { 155 | LOG_ERR("Failed to start GNSS"); 156 | return -1; 157 | } 158 | 159 | LOG_INF("GNSS started successfully"); 160 | return 0; 161 | } 162 | 163 | // Initialize LEDs 164 | static int led_init(void) { 165 | if (!device_is_ready(red_led.port) || 166 | !device_is_ready(green_led.port) || 167 | !device_is_ready(blue_led.port)) { 168 | LOG_ERR("LED GPIO not ready"); 169 | return -1; 170 | } 171 | 172 | gpio_pin_configure_dt(&red_led, GPIO_OUTPUT_INACTIVE); 173 | gpio_pin_configure_dt(&green_led, GPIO_OUTPUT_INACTIVE); 174 | gpio_pin_configure_dt(&blue_led, GPIO_OUTPUT_INACTIVE); 175 | 176 | set_color(OFF); // All LEDs off initially 177 | return 0; 178 | } 179 | 180 | // Process GPS fix and update geofence status 181 | static void process_gps_fix(void) { 182 | if (last_pvt.flags & NRF_MODEM_GNSS_PVT_FLAG_FIX_VALID) { 183 | double lat = last_pvt.latitude; 184 | double lon = last_pvt.longitude; 185 | double alt = last_pvt.altitude; 186 | 187 | LOG_INF("GPS Fix: Lat=%.6f, Lon=%.6f, Alt=%.2f m", lat, lon, alt); 188 | 189 | if (is_outside_geofence(lat, lon)) { 190 | set_color(BLUE); // Outside geofence 191 | LOG_INF("Status: OUTSIDE geofence"); 192 | } else { 193 | set_color(GREEN); // Inside geofence 194 | LOG_INF("Status: INSIDE geofence"); 195 | } 196 | } else { 197 | set_color(RED); // No fix 198 | LOG_INF("Status: NO GPS FIX"); 199 | } 200 | } 201 | 202 | int main(void) { 203 | int err; 204 | 205 | LOG_INF("Starting GNSS Geofencing Application"); 206 | LOG_INF("Geofence center: %.6f, %.6f", GEOFENCE_CENTER_LAT, GEOFENCE_CENTER_LON); 207 | LOG_INF("Geofence radius: %.0f meters", GEOFENCE_RADIUS_METERS); 208 | 209 | // Initialize LEDs 210 | if (led_init() != 0) { 211 | LOG_ERR("Failed to initialize LEDs"); 212 | return -1; 213 | } 214 | 215 | // Initialize modem library 216 | err = nrf_modem_lib_init(); 217 | if (err) { 218 | LOG_ERR("Modem library initialization failed, error: %d", err); 219 | return err; 220 | } 221 | 222 | // Initialize modem and GNSS (no LTE connection needed) 223 | if (modem_init() != 0) { 224 | LOG_ERR("Failed to initialize modem"); 225 | return -1; 226 | } 227 | 228 | // Initialize and start GNSS 229 | if (gnss_init_and_start() != 0) { 230 | LOG_ERR("Failed to initialize and start GNSS"); 231 | return -1; 232 | } 233 | 234 | LOG_INF("System initialized. Waiting for GPS fixes..."); 235 | 236 | // Main loop - wait for GPS data and process geofencing 237 | for (;;) { 238 | if (k_sem_take(&pvt_data_sem, K_FOREVER) == 0) { 239 | process_gps_fix(); 240 | } 241 | } 242 | 243 | return 0; 244 | } -------------------------------------------------------------------------------- /gnss/src/assistance_minimal.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021-2023 Nordic Semiconductor ASA 3 | * 4 | * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause 5 | */ 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | #include "assistance.h" 18 | #include "factory_almanac_v2.h" 19 | #include "factory_almanac_v3.h" 20 | #include "mcc_location_table.h" 21 | 22 | LOG_MODULE_DECLARE(gnss_sample, CONFIG_GNSS_SAMPLE_LOG_LEVEL); 23 | 24 | /* (6.1.1980 UTC - 1.1.1970 UTC) */ 25 | #define GPS_TO_UNIX_UTC_OFFSET_SECONDS (315964800UL) 26 | /* UTC/GPS time offset as of 1st of January 2017. */ 27 | #define GPS_TO_UTC_LEAP_SECONDS (18UL) 28 | #define SEC_PER_HOUR (MIN_PER_HOUR * SEC_PER_MIN) 29 | #define SEC_PER_DAY (HOUR_PER_DAY * SEC_PER_HOUR) 30 | #define DAYS_PER_WEEK (7UL) 31 | #define PLMN_STR_MAX_LEN 8 /* MCC + MNC + quotes */ 32 | 33 | enum almanac_version { 34 | FACTORY_ALMANAC_V2 = 2, 35 | FACTORY_ALMANAC_V3 = 3 36 | }; 37 | 38 | static char current_alm_checksum[64]; 39 | 40 | static int set(const char *key, size_t len_rd, settings_read_cb read_cb, void *cb_arg) 41 | { 42 | int len; 43 | int key_len; 44 | const char *next; 45 | 46 | if (!key) { 47 | return -ENOENT; 48 | } 49 | 50 | key_len = settings_name_next(key, &next); 51 | 52 | if (!strncmp(key, "almanac_checksum", key_len)) { 53 | len = read_cb(cb_arg, ¤t_alm_checksum, sizeof(current_alm_checksum)); 54 | if (len < sizeof(current_alm_checksum)) { 55 | LOG_ERR("Failed to read almanac checksum from settings"); 56 | } 57 | 58 | return 0; 59 | } 60 | 61 | return -ENOENT; 62 | } 63 | 64 | static struct settings_handler assistance_settings = { 65 | .name = "assistance", 66 | .h_set = set, 67 | }; 68 | 69 | static enum almanac_version factory_almanac_version_get(void) 70 | { 71 | char resp[32]; 72 | 73 | if (nrf_modem_at_cmd(resp, sizeof(resp), "AT+CGMM") == 0) { 74 | /* nRF9160 uses factory almanac file format version 2, while nRF91x1 uses 75 | * version 3. 76 | */ 77 | if (strstr(resp, "nRF9160") != NULL) { 78 | return FACTORY_ALMANAC_V2; 79 | } 80 | } 81 | 82 | return FACTORY_ALMANAC_V3; 83 | } 84 | 85 | static void factory_almanac_write(void) 86 | { 87 | int err; 88 | enum almanac_version alm_version; 89 | const char *alm_data; 90 | const char *alm_checksum; 91 | 92 | /* Get the supported factory almanac version. */ 93 | alm_version = factory_almanac_version_get(); 94 | LOG_DBG("Supported factory almanac version: %d", alm_version); 95 | 96 | if (alm_version == 3) { 97 | alm_data = FACTORY_ALMANAC_DATA_V3; 98 | alm_checksum = FACTORY_ALMANAC_CHECKSUM_V3; 99 | } else { 100 | alm_data = FACTORY_ALMANAC_DATA_V2; 101 | alm_checksum = FACTORY_ALMANAC_CHECKSUM_V2; 102 | } 103 | 104 | /* Check if the same almanac has already been written to prevent unnecessary writes 105 | * to flash memory. 106 | */ 107 | if (!strncmp(current_alm_checksum, alm_checksum, sizeof(current_alm_checksum))) { 108 | LOG_INF("Factory almanac has already been written, skipping writing"); 109 | return; 110 | } 111 | 112 | err = nrf_modem_at_printf("AT%%XFILEWRITE=1,\"%s\",\"%s\"", 113 | alm_data, alm_checksum); 114 | if (err != 0) { 115 | LOG_ERR("Failed to write factory almanac"); 116 | return; 117 | } 118 | 119 | LOG_INF("Wrote factory almanac"); 120 | 121 | err = settings_save_one("assistance/almanac_checksum", 122 | alm_checksum, 123 | sizeof(current_alm_checksum)); 124 | if (err) { 125 | LOG_ERR("Failed to write almanac checksum to settings, error %d", err); 126 | } 127 | } 128 | 129 | static int64_t utc_to_gps_sec(const int64_t utc_sec) 130 | { 131 | return (utc_sec - GPS_TO_UNIX_UTC_OFFSET_SECONDS) + GPS_TO_UTC_LEAP_SECONDS; 132 | } 133 | 134 | static void gps_sec_to_day_time(int64_t gps_sec, 135 | uint16_t *gps_day, 136 | uint32_t *gps_time_of_day) 137 | { 138 | *gps_day = (uint16_t)(gps_sec / SEC_PER_DAY); 139 | *gps_time_of_day = (uint32_t)(gps_sec % SEC_PER_DAY); 140 | } 141 | 142 | static void time_inject(void) 143 | { 144 | int ret; 145 | struct tm date_time; 146 | int64_t utc_sec; 147 | int64_t gps_sec; 148 | struct nrf_modem_gnss_agnss_gps_data_system_time_and_sv_tow gps_time = { 0 }; 149 | 150 | /* Read current UTC time from the modem. */ 151 | ret = nrf_modem_at_scanf("AT+CCLK?", 152 | "+CCLK: \"%u/%u/%u,%u:%u:%u", 153 | &date_time.tm_year, 154 | &date_time.tm_mon, 155 | &date_time.tm_mday, 156 | &date_time.tm_hour, 157 | &date_time.tm_min, 158 | &date_time.tm_sec 159 | ); 160 | if (ret != 6) { 161 | LOG_WRN("Couldn't read current time from modem, time assistance unavailable"); 162 | return; 163 | } 164 | 165 | /* Convert to struct tm format. */ 166 | date_time.tm_year = date_time.tm_year + 2000 - 1900; /* years since 1900 */ 167 | date_time.tm_mon--; /* months since January */ 168 | 169 | /* Convert time to seconds since Unix time epoch (1.1.1970). */ 170 | utc_sec = timeutil_timegm64(&date_time); 171 | /* Convert time to seconds since GPS time epoch (6.1.1980). */ 172 | gps_sec = utc_to_gps_sec(utc_sec); 173 | 174 | gps_sec_to_day_time(gps_sec, &gps_time.date_day, &gps_time.time_full_s); 175 | 176 | ret = nrf_modem_gnss_agnss_write(&gps_time, sizeof(gps_time), 177 | NRF_MODEM_GNSS_AGNSS_GPS_SYSTEM_CLOCK_AND_TOWS); 178 | if (ret != 0) { 179 | LOG_ERR("Failed to inject time, error %d", ret); 180 | return; 181 | } 182 | 183 | LOG_INF("Injected time (GPS day %u, GPS time of day %u)", 184 | gps_time.date_day, gps_time.time_full_s); 185 | } 186 | 187 | static void location_inject(void) 188 | { 189 | int err; 190 | char plmn_str[PLMN_STR_MAX_LEN + 1]; 191 | uint16_t mcc; 192 | const struct mcc_table *mcc_info; 193 | struct nrf_modem_gnss_agnss_data_location location = { 0 }; 194 | 195 | /* Read PLMN string from modem to get the MCC. */ 196 | err = nrf_modem_at_scanf( 197 | "AT%XMONITOR", 198 | "%%XMONITOR: " 199 | "%*d" /* : ignored */ 200 | ",%*[^,]" /* : ignored */ 201 | ",%*[^,]" /* : ignored */ 202 | ",%"STRINGIFY(PLMN_STR_MAX_LEN)"[^,]", /* */ 203 | plmn_str); 204 | if (err != 1) { 205 | LOG_WRN("Couldn't read PLMN from modem, location assistance unavailable"); 206 | return; 207 | } 208 | 209 | /* NULL terminate MCC and read it. */ 210 | plmn_str[4] = '\0'; 211 | mcc = strtol(plmn_str + 1, NULL, 10); 212 | 213 | mcc_info = mcc_lookup(mcc); 214 | if (mcc_info == NULL) { 215 | LOG_WRN("No location found for MCC %u", mcc); 216 | return; 217 | } 218 | 219 | location.latitude = lat_convert(mcc_info->lat); 220 | location.longitude = lon_convert(mcc_info->lon); 221 | location.unc_semimajor = mcc_info->unc_semimajor; 222 | location.unc_semiminor = mcc_info->unc_semiminor; 223 | location.orientation_major = mcc_info->orientation; 224 | location.confidence = mcc_info->confidence; 225 | 226 | #if defined(CONFIG_GNSS_SAMPLE_LOW_ACCURACY) 227 | if (CONFIG_GNSS_SAMPLE_ASSISTANCE_REFERENCE_ALT != -32767) { 228 | /* Use reference altitude to enable 3-sat first fix. */ 229 | LOG_INF("Using reference altitude %d meters", 230 | CONFIG_GNSS_SAMPLE_ASSISTANCE_REFERENCE_ALT); 231 | location.altitude = CONFIG_GNSS_SAMPLE_ASSISTANCE_REFERENCE_ALT; 232 | /* The altitude uncertainty has to be less than 100 meters (coded number K has to 233 | * be less than 48) for the altitude to be used for a 3-sat fix. GNSS increases 234 | * the uncertainty depending on the age of the altitude and whether the device is 235 | * stationary or moving. The uncertainty is set to 0 (meaning 0 meters), so that 236 | * it remains usable for a 3-sat fix for as long as possible. 237 | */ 238 | location.unc_altitude = 0; 239 | } else 240 | #endif 241 | { 242 | location.unc_altitude = 255; /* altitude not used */ 243 | } 244 | 245 | err = nrf_modem_gnss_agnss_write( 246 | &location, sizeof(location), NRF_MODEM_GNSS_AGNSS_LOCATION); 247 | if (err) { 248 | LOG_ERR("Failed to inject location for MCC %u, error %d", mcc, err); 249 | return; 250 | } 251 | 252 | LOG_INF("Injected location for MCC %u", mcc); 253 | } 254 | 255 | int assistance_init(struct k_work_q *assistance_work_q) 256 | { 257 | ARG_UNUSED(assistance_work_q); 258 | 259 | int err; 260 | 261 | err = settings_subsys_init(); 262 | if (err) { 263 | LOG_ERR("Settings subsystem initialization failed, error %d", err); 264 | return err; 265 | } 266 | 267 | err = settings_register(&assistance_settings); 268 | if (err) { 269 | LOG_ERR("Registering settings handler failed, error %d", err); 270 | return err; 271 | } 272 | 273 | err = settings_load(); 274 | if (err) { 275 | LOG_ERR("Loading settings failed, error %d", err); 276 | return err; 277 | } 278 | 279 | factory_almanac_write(); 280 | 281 | return 0; 282 | } 283 | 284 | int assistance_request(struct nrf_modem_gnss_agnss_data_frame *agnss_request) 285 | { 286 | if (agnss_request->data_flags & NRF_MODEM_GNSS_AGNSS_GPS_SYS_TIME_AND_SV_TOW_REQUEST) { 287 | time_inject(); 288 | } 289 | 290 | if (agnss_request->data_flags & NRF_MODEM_GNSS_AGNSS_POSITION_REQUEST) { 291 | location_inject(); 292 | } 293 | 294 | return 0; 295 | } 296 | 297 | bool assistance_is_active(void) 298 | { 299 | /* Always return false because assistance_request() doesn't take much time. */ 300 | return false; 301 | } 302 | -------------------------------------------------------------------------------- /projects/geofencing/src/assistance_minimal.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021-2023 Nordic Semiconductor ASA 3 | * 4 | * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause 5 | */ 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | #include "assistance.h" 18 | #include "factory_almanac_v2.h" 19 | #include "factory_almanac_v3.h" 20 | #include "mcc_location_table.h" 21 | 22 | LOG_MODULE_DECLARE(gnss_sample, CONFIG_GNSS_SAMPLE_LOG_LEVEL); 23 | 24 | /* (6.1.1980 UTC - 1.1.1970 UTC) */ 25 | #define GPS_TO_UNIX_UTC_OFFSET_SECONDS (315964800UL) 26 | /* UTC/GPS time offset as of 1st of January 2017. */ 27 | #define GPS_TO_UTC_LEAP_SECONDS (18UL) 28 | #define SEC_PER_HOUR (MIN_PER_HOUR * SEC_PER_MIN) 29 | #define SEC_PER_DAY (HOUR_PER_DAY * SEC_PER_HOUR) 30 | #define DAYS_PER_WEEK (7UL) 31 | #define PLMN_STR_MAX_LEN 8 /* MCC + MNC + quotes */ 32 | 33 | enum almanac_version { 34 | FACTORY_ALMANAC_V2 = 2, 35 | FACTORY_ALMANAC_V3 = 3 36 | }; 37 | 38 | static char current_alm_checksum[64]; 39 | 40 | static int set(const char *key, size_t len_rd, settings_read_cb read_cb, void *cb_arg) 41 | { 42 | int len; 43 | int key_len; 44 | const char *next; 45 | 46 | if (!key) { 47 | return -ENOENT; 48 | } 49 | 50 | key_len = settings_name_next(key, &next); 51 | 52 | if (!strncmp(key, "almanac_checksum", key_len)) { 53 | len = read_cb(cb_arg, ¤t_alm_checksum, sizeof(current_alm_checksum)); 54 | if (len < sizeof(current_alm_checksum)) { 55 | LOG_ERR("Failed to read almanac checksum from settings"); 56 | } 57 | 58 | return 0; 59 | } 60 | 61 | return -ENOENT; 62 | } 63 | 64 | static struct settings_handler assistance_settings = { 65 | .name = "assistance", 66 | .h_set = set, 67 | }; 68 | 69 | static enum almanac_version factory_almanac_version_get(void) 70 | { 71 | char resp[32]; 72 | 73 | if (nrf_modem_at_cmd(resp, sizeof(resp), "AT+CGMM") == 0) { 74 | /* nRF9160 uses factory almanac file format version 2, while nRF91x1 uses 75 | * version 3. 76 | */ 77 | if (strstr(resp, "nRF9160") != NULL) { 78 | return FACTORY_ALMANAC_V2; 79 | } 80 | } 81 | 82 | return FACTORY_ALMANAC_V3; 83 | } 84 | 85 | static void factory_almanac_write(void) 86 | { 87 | int err; 88 | enum almanac_version alm_version; 89 | const char *alm_data; 90 | const char *alm_checksum; 91 | 92 | /* Get the supported factory almanac version. */ 93 | alm_version = factory_almanac_version_get(); 94 | LOG_DBG("Supported factory almanac version: %d", alm_version); 95 | 96 | if (alm_version == 3) { 97 | alm_data = FACTORY_ALMANAC_DATA_V3; 98 | alm_checksum = FACTORY_ALMANAC_CHECKSUM_V3; 99 | } else { 100 | alm_data = FACTORY_ALMANAC_DATA_V2; 101 | alm_checksum = FACTORY_ALMANAC_CHECKSUM_V2; 102 | } 103 | 104 | /* Check if the same almanac has already been written to prevent unnecessary writes 105 | * to flash memory. 106 | */ 107 | if (!strncmp(current_alm_checksum, alm_checksum, sizeof(current_alm_checksum))) { 108 | LOG_INF("Factory almanac has already been written, skipping writing"); 109 | return; 110 | } 111 | 112 | err = nrf_modem_at_printf("AT%%XFILEWRITE=1,\"%s\",\"%s\"", 113 | alm_data, alm_checksum); 114 | if (err != 0) { 115 | LOG_ERR("Failed to write factory almanac"); 116 | return; 117 | } 118 | 119 | LOG_INF("Wrote factory almanac"); 120 | 121 | err = settings_save_one("assistance/almanac_checksum", 122 | alm_checksum, 123 | sizeof(current_alm_checksum)); 124 | if (err) { 125 | LOG_ERR("Failed to write almanac checksum to settings, error %d", err); 126 | } 127 | } 128 | 129 | static int64_t utc_to_gps_sec(const int64_t utc_sec) 130 | { 131 | return (utc_sec - GPS_TO_UNIX_UTC_OFFSET_SECONDS) + GPS_TO_UTC_LEAP_SECONDS; 132 | } 133 | 134 | static void gps_sec_to_day_time(int64_t gps_sec, 135 | uint16_t *gps_day, 136 | uint32_t *gps_time_of_day) 137 | { 138 | *gps_day = (uint16_t)(gps_sec / SEC_PER_DAY); 139 | *gps_time_of_day = (uint32_t)(gps_sec % SEC_PER_DAY); 140 | } 141 | 142 | static void time_inject(void) 143 | { 144 | int ret; 145 | struct tm date_time; 146 | int64_t utc_sec; 147 | int64_t gps_sec; 148 | struct nrf_modem_gnss_agnss_gps_data_system_time_and_sv_tow gps_time = { 0 }; 149 | 150 | /* Read current UTC time from the modem. */ 151 | ret = nrf_modem_at_scanf("AT+CCLK?", 152 | "+CCLK: \"%u/%u/%u,%u:%u:%u", 153 | &date_time.tm_year, 154 | &date_time.tm_mon, 155 | &date_time.tm_mday, 156 | &date_time.tm_hour, 157 | &date_time.tm_min, 158 | &date_time.tm_sec 159 | ); 160 | if (ret != 6) { 161 | LOG_WRN("Couldn't read current time from modem, time assistance unavailable"); 162 | return; 163 | } 164 | 165 | /* Convert to struct tm format. */ 166 | date_time.tm_year = date_time.tm_year + 2000 - 1900; /* years since 1900 */ 167 | date_time.tm_mon--; /* months since January */ 168 | 169 | /* Convert time to seconds since Unix time epoch (1.1.1970). */ 170 | utc_sec = timeutil_timegm64(&date_time); 171 | /* Convert time to seconds since GPS time epoch (6.1.1980). */ 172 | gps_sec = utc_to_gps_sec(utc_sec); 173 | 174 | gps_sec_to_day_time(gps_sec, &gps_time.date_day, &gps_time.time_full_s); 175 | 176 | ret = nrf_modem_gnss_agnss_write(&gps_time, sizeof(gps_time), 177 | NRF_MODEM_GNSS_AGNSS_GPS_SYSTEM_CLOCK_AND_TOWS); 178 | if (ret != 0) { 179 | LOG_ERR("Failed to inject time, error %d", ret); 180 | return; 181 | } 182 | 183 | LOG_INF("Injected time (GPS day %u, GPS time of day %u)", 184 | gps_time.date_day, gps_time.time_full_s); 185 | } 186 | 187 | static void location_inject(void) 188 | { 189 | int err; 190 | char plmn_str[PLMN_STR_MAX_LEN + 1]; 191 | uint16_t mcc; 192 | const struct mcc_table *mcc_info; 193 | struct nrf_modem_gnss_agnss_data_location location = { 0 }; 194 | 195 | /* Read PLMN string from modem to get the MCC. */ 196 | err = nrf_modem_at_scanf( 197 | "AT%XMONITOR", 198 | "%%XMONITOR: " 199 | "%*d" /* : ignored */ 200 | ",%*[^,]" /* : ignored */ 201 | ",%*[^,]" /* : ignored */ 202 | ",%"STRINGIFY(PLMN_STR_MAX_LEN)"[^,]", /* */ 203 | plmn_str); 204 | if (err != 1) { 205 | LOG_WRN("Couldn't read PLMN from modem, location assistance unavailable"); 206 | return; 207 | } 208 | 209 | /* NULL terminate MCC and read it. */ 210 | plmn_str[4] = '\0'; 211 | mcc = strtol(plmn_str + 1, NULL, 10); 212 | 213 | mcc_info = mcc_lookup(mcc); 214 | if (mcc_info == NULL) { 215 | LOG_WRN("No location found for MCC %u", mcc); 216 | return; 217 | } 218 | 219 | location.latitude = lat_convert(mcc_info->lat); 220 | location.longitude = lon_convert(mcc_info->lon); 221 | location.unc_semimajor = mcc_info->unc_semimajor; 222 | location.unc_semiminor = mcc_info->unc_semiminor; 223 | location.orientation_major = mcc_info->orientation; 224 | location.confidence = mcc_info->confidence; 225 | 226 | #if defined(CONFIG_GNSS_SAMPLE_LOW_ACCURACY) 227 | if (CONFIG_GNSS_SAMPLE_ASSISTANCE_REFERENCE_ALT != -32767) { 228 | /* Use reference altitude to enable 3-sat first fix. */ 229 | LOG_INF("Using reference altitude %d meters", 230 | CONFIG_GNSS_SAMPLE_ASSISTANCE_REFERENCE_ALT); 231 | location.altitude = CONFIG_GNSS_SAMPLE_ASSISTANCE_REFERENCE_ALT; 232 | /* The altitude uncertainty has to be less than 100 meters (coded number K has to 233 | * be less than 48) for the altitude to be used for a 3-sat fix. GNSS increases 234 | * the uncertainty depending on the age of the altitude and whether the device is 235 | * stationary or moving. The uncertainty is set to 0 (meaning 0 meters), so that 236 | * it remains usable for a 3-sat fix for as long as possible. 237 | */ 238 | location.unc_altitude = 0; 239 | } else 240 | #endif 241 | { 242 | location.unc_altitude = 255; /* altitude not used */ 243 | } 244 | 245 | err = nrf_modem_gnss_agnss_write( 246 | &location, sizeof(location), NRF_MODEM_GNSS_AGNSS_LOCATION); 247 | if (err) { 248 | LOG_ERR("Failed to inject location for MCC %u, error %d", mcc, err); 249 | return; 250 | } 251 | 252 | LOG_INF("Injected location for MCC %u", mcc); 253 | } 254 | 255 | int assistance_init(struct k_work_q *assistance_work_q) 256 | { 257 | ARG_UNUSED(assistance_work_q); 258 | 259 | int err; 260 | 261 | err = settings_subsys_init(); 262 | if (err) { 263 | LOG_ERR("Settings subsystem initialization failed, error %d", err); 264 | return err; 265 | } 266 | 267 | err = settings_register(&assistance_settings); 268 | if (err) { 269 | LOG_ERR("Registering settings handler failed, error %d", err); 270 | return err; 271 | } 272 | 273 | err = settings_load(); 274 | if (err) { 275 | LOG_ERR("Loading settings failed, error %d", err); 276 | return err; 277 | } 278 | 279 | factory_almanac_write(); 280 | 281 | return 0; 282 | } 283 | 284 | int assistance_request(struct nrf_modem_gnss_agnss_data_frame *agnss_request) 285 | { 286 | if (agnss_request->data_flags & NRF_MODEM_GNSS_AGNSS_GPS_SYS_TIME_AND_SV_TOW_REQUEST) { 287 | time_inject(); 288 | } 289 | 290 | if (agnss_request->data_flags & NRF_MODEM_GNSS_AGNSS_POSITION_REQUEST) { 291 | location_inject(); 292 | } 293 | 294 | return 0; 295 | } 296 | 297 | bool assistance_is_active(void) 298 | { 299 | /* Always return false because assistance_request() doesn't take much time. */ 300 | return false; 301 | } 302 | -------------------------------------------------------------------------------- /gnss/src/assistance.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021 Nordic Semiconductor ASA 3 | * 4 | * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause 5 | */ 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #if defined(CONFIG_NRF_CLOUD_AGNSS) 14 | #include 15 | #endif /* CONFIG_NRF_CLOUD_AGNSS */ 16 | #if defined(CONFIG_NRF_CLOUD_PGPS) 17 | #include 18 | #endif /* CONFIG_NRF_CLOUD_PGPS */ 19 | 20 | #include "assistance.h" 21 | 22 | LOG_MODULE_DECLARE(gnss_sample, CONFIG_GNSS_SAMPLE_LOG_LEVEL); 23 | 24 | static char jwt_buf[600]; 25 | static char rx_buf[2048]; 26 | 27 | #if defined(CONFIG_NRF_CLOUD_AGNSS) 28 | static char agnss_data_buf[3500]; 29 | #endif /* CONFIG_NRF_CLOUD_AGNSS */ 30 | 31 | #if defined(CONFIG_NRF_CLOUD_PGPS) 32 | static struct nrf_modem_gnss_agnss_data_frame agnss_need; 33 | static struct gps_pgps_request pgps_request; 34 | static struct nrf_cloud_pgps_prediction *prediction; 35 | static struct k_work get_pgps_data_work; 36 | static struct k_work inject_pgps_data_work; 37 | #endif /* CONFIG_NRF_CLOUD_PGPS */ 38 | 39 | static struct k_work_q *work_q; 40 | static volatile bool assistance_active; 41 | 42 | #if defined(CONFIG_NRF_CLOUD_AGNSS) 43 | static int serving_cell_info_get(struct lte_lc_cell *serving_cell) 44 | { 45 | int err; 46 | 47 | err = modem_info_init(); 48 | if (err) { 49 | return err; 50 | } 51 | 52 | char resp_buf[MODEM_INFO_MAX_RESPONSE_SIZE]; 53 | 54 | err = modem_info_string_get(MODEM_INFO_CELLID, 55 | resp_buf, 56 | MODEM_INFO_MAX_RESPONSE_SIZE); 57 | if (err < 0) { 58 | return err; 59 | } 60 | 61 | serving_cell->id = strtol(resp_buf, NULL, 16); 62 | 63 | err = modem_info_string_get(MODEM_INFO_AREA_CODE, 64 | resp_buf, 65 | MODEM_INFO_MAX_RESPONSE_SIZE); 66 | if (err < 0) { 67 | return err; 68 | } 69 | 70 | serving_cell->tac = strtol(resp_buf, NULL, 16); 71 | 72 | /* Request for MODEM_INFO_MNC returns both MNC and MCC in the same string. */ 73 | err = modem_info_string_get(MODEM_INFO_OPERATOR, 74 | resp_buf, 75 | MODEM_INFO_MAX_RESPONSE_SIZE); 76 | if (err < 0) { 77 | return err; 78 | } 79 | 80 | serving_cell->mnc = strtol(&resp_buf[3], NULL, 10); 81 | /* Null-terminate MCC, read and store it. */ 82 | resp_buf[3] = '\0'; 83 | serving_cell->mcc = strtol(resp_buf, NULL, 10); 84 | 85 | return 0; 86 | } 87 | #endif /* CONFIG_NRF_CLOUD_AGNSS */ 88 | 89 | #if defined(CONFIG_NRF_CLOUD_PGPS) 90 | static void get_pgps_data_work_fn(struct k_work *work) 91 | { 92 | ARG_UNUSED(work); 93 | 94 | int err; 95 | 96 | assistance_active = true; 97 | 98 | LOG_INF("Sending request for P-GPS predictions to nRF Cloud..."); 99 | 100 | err = nrf_cloud_jwt_generate(0, jwt_buf, sizeof(jwt_buf)); 101 | if (err) { 102 | LOG_ERR("Failed to generate JWT, error: %d", err); 103 | 104 | goto exit; 105 | } 106 | 107 | struct nrf_cloud_rest_context rest_ctx = { 108 | .connect_socket = -1, 109 | .keep_alive = false, 110 | .timeout_ms = NRF_CLOUD_REST_TIMEOUT_NONE, 111 | .auth = jwt_buf, 112 | .rx_buf = rx_buf, 113 | .rx_buf_len = sizeof(rx_buf), 114 | .fragment_size = 0, /* Defaults to CONFIG_NRF_CLOUD_REST_FRAGMENT_SIZE when 0 */ 115 | .status = 0, 116 | .response = NULL, 117 | .response_len = 0, 118 | .total_response_len = 0 119 | }; 120 | 121 | struct nrf_cloud_rest_pgps_request request = { 122 | .pgps_req = &pgps_request 123 | }; 124 | 125 | err = nrf_cloud_rest_pgps_data_get(&rest_ctx, &request); 126 | if (err) { 127 | LOG_ERR("Failed to send P-GPS request, error: %d", err); 128 | 129 | nrf_cloud_pgps_request_reset(); 130 | 131 | goto exit; 132 | } 133 | 134 | LOG_INF("Processing P-GPS response"); 135 | 136 | err = nrf_cloud_pgps_process(rest_ctx.response, rest_ctx.response_len); 137 | if (err) { 138 | LOG_ERR("Failed to process P-GPS response, error: %d", err); 139 | 140 | nrf_cloud_pgps_request_reset(); 141 | 142 | goto exit; 143 | } 144 | 145 | LOG_INF("P-GPS response processed"); 146 | 147 | exit: 148 | assistance_active = false; 149 | } 150 | 151 | static void inject_pgps_data_work_fn(struct k_work *work) 152 | { 153 | ARG_UNUSED(work); 154 | 155 | int err; 156 | 157 | assistance_active = true; 158 | 159 | LOG_INF("Injecting P-GPS ephemerides"); 160 | 161 | err = nrf_cloud_pgps_inject(prediction, &agnss_need); 162 | if (err) { 163 | LOG_ERR("Failed to inject P-GPS ephemerides"); 164 | } 165 | 166 | err = nrf_cloud_pgps_preemptive_updates(); 167 | if (err) { 168 | LOG_ERR("Failed to request P-GPS updates"); 169 | } 170 | 171 | assistance_active = false; 172 | } 173 | 174 | static void pgps_event_handler(struct nrf_cloud_pgps_event *event) 175 | { 176 | switch (event->type) { 177 | case PGPS_EVT_AVAILABLE: 178 | prediction = event->prediction; 179 | 180 | k_work_submit_to_queue(work_q, &inject_pgps_data_work); 181 | break; 182 | 183 | case PGPS_EVT_REQUEST: 184 | memcpy(&pgps_request, event->request, sizeof(pgps_request)); 185 | 186 | k_work_submit_to_queue(work_q, &get_pgps_data_work); 187 | break; 188 | 189 | case PGPS_EVT_LOADING: 190 | LOG_INF("Loading P-GPS predictions"); 191 | assistance_active = true; 192 | break; 193 | 194 | case PGPS_EVT_READY: 195 | LOG_INF("P-GPS predictions ready"); 196 | assistance_active = false; 197 | break; 198 | 199 | default: 200 | /* No action needed */ 201 | break; 202 | } 203 | } 204 | #endif /* CONFIG_NRF_CLOUD_PGPS */ 205 | 206 | #if defined(CONFIG_NRF_CLOUD_AGNSS) 207 | static const char *get_system_string(uint8_t system_id) 208 | { 209 | switch (system_id) { 210 | case NRF_MODEM_GNSS_SYSTEM_INVALID: 211 | return "invalid"; 212 | 213 | case NRF_MODEM_GNSS_SYSTEM_GPS: 214 | return "GPS"; 215 | 216 | case NRF_MODEM_GNSS_SYSTEM_QZSS: 217 | return "QZSS"; 218 | 219 | default: 220 | return "unknown"; 221 | } 222 | } 223 | #endif /* CONFIG_NRF_CLOUD_AGNSS */ 224 | 225 | int assistance_init(struct k_work_q *assistance_work_q) 226 | { 227 | work_q = assistance_work_q; 228 | 229 | #if defined(CONFIG_NRF_CLOUD_PGPS) 230 | k_work_init(&get_pgps_data_work, get_pgps_data_work_fn); 231 | k_work_init(&inject_pgps_data_work, inject_pgps_data_work_fn); 232 | 233 | struct nrf_cloud_pgps_init_param pgps_param = { 234 | .event_handler = pgps_event_handler, 235 | /* storage is defined by CONFIG_NRF_CLOUD_PGPS_STORAGE */ 236 | .storage_base = 0u, 237 | .storage_size = 0u 238 | }; 239 | 240 | if (nrf_cloud_pgps_init(&pgps_param) != 0) { 241 | LOG_ERR("Failed to initialize P-GPS"); 242 | return -1; 243 | } 244 | #endif /* CONFIG_NRF_CLOUD_PGPS */ 245 | 246 | return 0; 247 | } 248 | 249 | int assistance_request(struct nrf_modem_gnss_agnss_data_frame *agnss_request) 250 | { 251 | int err = 0; 252 | 253 | #if defined(CONFIG_NRF_CLOUD_PGPS) 254 | /* Store the A-GNSS data request for P-GPS use. */ 255 | memcpy(&agnss_need, agnss_request, sizeof(agnss_need)); 256 | 257 | #if defined(CONFIG_NRF_CLOUD_AGNSS) 258 | if (!agnss_request->data_flags) { 259 | /* No assistance needed from A-GNSS, skip directly to P-GPS. */ 260 | nrf_cloud_pgps_notify_prediction(); 261 | return 0; 262 | } 263 | 264 | /* P-GPS will handle GPS ephemerides, so skip those. */ 265 | agnss_request->system[0].sv_mask_ephe = 0; 266 | /* GPS almanacs are not needed with P-GPS, so skip those. */ 267 | agnss_request->system[0].sv_mask_alm = 0; 268 | #endif /* CONFIG_NRF_CLOUD_AGNSS */ 269 | #endif /* CONFIG_NRF_CLOUD_PGPS */ 270 | #if defined(CONFIG_NRF_CLOUD_AGNSS) 271 | assistance_active = true; 272 | 273 | err = nrf_cloud_jwt_generate(0, jwt_buf, sizeof(jwt_buf)); 274 | if (err) { 275 | LOG_ERR("Failed to generate JWT, error: %d", err); 276 | goto agnss_exit; 277 | } 278 | 279 | struct nrf_cloud_rest_context rest_ctx = { 280 | .connect_socket = -1, 281 | .keep_alive = false, 282 | .timeout_ms = NRF_CLOUD_REST_TIMEOUT_NONE, 283 | .auth = jwt_buf, 284 | .rx_buf = rx_buf, 285 | .rx_buf_len = sizeof(rx_buf), 286 | .fragment_size = 0, /* Defaults to CONFIG_NRF_CLOUD_REST_FRAGMENT_SIZE when 0 */ 287 | .status = 0, 288 | .response = NULL, 289 | .response_len = 0, 290 | .total_response_len = 0 291 | }; 292 | 293 | struct nrf_cloud_rest_agnss_request request = { 294 | .type = NRF_CLOUD_REST_AGNSS_REQ_CUSTOM, 295 | .agnss_req = agnss_request, 296 | .net_info = NULL, 297 | #if defined(CONFIG_NRF_CLOUD_AGNSS_FILTERED_RUNTIME) 298 | .filtered = true, 299 | /* Note: if you change the mask angle here, you may want to 300 | * also change it to match in gnss_init_and_start() in main.c. 301 | */ 302 | .mask_angle = CONFIG_NRF_CLOUD_AGNSS_ELEVATION_MASK 303 | /* Note: if CONFIG_NRF_CLOUD_AGNSS_FILTERED is enabled but 304 | * CONFIG_NRF_CLOUD_AGNSS_FILTERED_RUNTIME is not, 305 | * the nrf_cloud_rest library will set the above fields to 306 | * true and CONFIG_NRF_CLOUD_AGNSS_ELEVATION_MASK respectively. 307 | * When CONFIG_NRF_CLOUD_AGNSS_FILTERED is disabled, it will 308 | * set them to false and 0. 309 | */ 310 | #endif 311 | }; 312 | 313 | struct nrf_cloud_rest_agnss_result result = { 314 | .buf = agnss_data_buf, 315 | .buf_sz = sizeof(agnss_data_buf), 316 | .agnss_sz = 0 317 | }; 318 | 319 | struct lte_lc_cells_info net_info = { 0 }; 320 | 321 | err = serving_cell_info_get(&net_info.current_cell); 322 | if (err) { 323 | LOG_ERR("Could not get cell info, error: %d", err); 324 | } else { 325 | /* Network info for the location request. */ 326 | request.net_info = &net_info; 327 | } 328 | 329 | LOG_INF("Requesting A-GNSS data: data_flags: 0x%02x", agnss_request->data_flags); 330 | for (int i = 0; i < agnss_request->system_count; i++) { 331 | LOG_INF("Requesting A-GNSS data: %s ephe: 0x%llx, alm: 0x%llx", 332 | get_system_string(agnss_request->system[i].system_id), 333 | agnss_request->system[i].sv_mask_ephe, 334 | agnss_request->system[i].sv_mask_alm); 335 | } 336 | 337 | err = nrf_cloud_rest_agnss_data_get(&rest_ctx, &request, &result); 338 | if (err) { 339 | LOG_ERR("Failed to get A-GNSS data, error: %d", err); 340 | goto agnss_exit; 341 | } 342 | 343 | LOG_INF("Processing A-GNSS data"); 344 | 345 | err = nrf_cloud_agnss_process(result.buf, result.agnss_sz); 346 | if (err) { 347 | LOG_ERR("Failed to process A-GNSS data, error: %d", err); 348 | goto agnss_exit; 349 | } 350 | 351 | LOG_INF("A-GNSS data processed"); 352 | 353 | agnss_exit: 354 | assistance_active = false; 355 | #endif /* CONFIG_NRF_CLOUD_AGNSS */ 356 | 357 | #if defined(CONFIG_NRF_CLOUD_PGPS) 358 | nrf_cloud_pgps_notify_prediction(); 359 | #endif /* CONFIG_NRF_CLOUD_PGPS */ 360 | 361 | return err; 362 | } 363 | 364 | bool assistance_is_active(void) 365 | { 366 | return assistance_active; 367 | } 368 | -------------------------------------------------------------------------------- /projects/geofencing/src/assistance.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021 Nordic Semiconductor ASA 3 | * 4 | * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause 5 | */ 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #if defined(CONFIG_NRF_CLOUD_AGNSS) 14 | #include 15 | #endif /* CONFIG_NRF_CLOUD_AGNSS */ 16 | #if defined(CONFIG_NRF_CLOUD_PGPS) 17 | #include 18 | #endif /* CONFIG_NRF_CLOUD_PGPS */ 19 | 20 | #include "assistance.h" 21 | 22 | LOG_MODULE_DECLARE(gnss_sample, CONFIG_GNSS_SAMPLE_LOG_LEVEL); 23 | 24 | static char jwt_buf[600]; 25 | static char rx_buf[2048]; 26 | 27 | #if defined(CONFIG_NRF_CLOUD_AGNSS) 28 | static char agnss_data_buf[3500]; 29 | #endif /* CONFIG_NRF_CLOUD_AGNSS */ 30 | 31 | #if defined(CONFIG_NRF_CLOUD_PGPS) 32 | static struct nrf_modem_gnss_agnss_data_frame agnss_need; 33 | static struct gps_pgps_request pgps_request; 34 | static struct nrf_cloud_pgps_prediction *prediction; 35 | static struct k_work get_pgps_data_work; 36 | static struct k_work inject_pgps_data_work; 37 | #endif /* CONFIG_NRF_CLOUD_PGPS */ 38 | 39 | static struct k_work_q *work_q; 40 | static volatile bool assistance_active; 41 | 42 | #if defined(CONFIG_NRF_CLOUD_AGNSS) 43 | static int serving_cell_info_get(struct lte_lc_cell *serving_cell) 44 | { 45 | int err; 46 | 47 | err = modem_info_init(); 48 | if (err) { 49 | return err; 50 | } 51 | 52 | char resp_buf[MODEM_INFO_MAX_RESPONSE_SIZE]; 53 | 54 | err = modem_info_string_get(MODEM_INFO_CELLID, 55 | resp_buf, 56 | MODEM_INFO_MAX_RESPONSE_SIZE); 57 | if (err < 0) { 58 | return err; 59 | } 60 | 61 | serving_cell->id = strtol(resp_buf, NULL, 16); 62 | 63 | err = modem_info_string_get(MODEM_INFO_AREA_CODE, 64 | resp_buf, 65 | MODEM_INFO_MAX_RESPONSE_SIZE); 66 | if (err < 0) { 67 | return err; 68 | } 69 | 70 | serving_cell->tac = strtol(resp_buf, NULL, 16); 71 | 72 | /* Request for MODEM_INFO_MNC returns both MNC and MCC in the same string. */ 73 | err = modem_info_string_get(MODEM_INFO_OPERATOR, 74 | resp_buf, 75 | MODEM_INFO_MAX_RESPONSE_SIZE); 76 | if (err < 0) { 77 | return err; 78 | } 79 | 80 | serving_cell->mnc = strtol(&resp_buf[3], NULL, 10); 81 | /* Null-terminate MCC, read and store it. */ 82 | resp_buf[3] = '\0'; 83 | serving_cell->mcc = strtol(resp_buf, NULL, 10); 84 | 85 | return 0; 86 | } 87 | #endif /* CONFIG_NRF_CLOUD_AGNSS */ 88 | 89 | #if defined(CONFIG_NRF_CLOUD_PGPS) 90 | static void get_pgps_data_work_fn(struct k_work *work) 91 | { 92 | ARG_UNUSED(work); 93 | 94 | int err; 95 | 96 | assistance_active = true; 97 | 98 | LOG_INF("Sending request for P-GPS predictions to nRF Cloud..."); 99 | 100 | err = nrf_cloud_jwt_generate(0, jwt_buf, sizeof(jwt_buf)); 101 | if (err) { 102 | LOG_ERR("Failed to generate JWT, error: %d", err); 103 | 104 | goto exit; 105 | } 106 | 107 | struct nrf_cloud_rest_context rest_ctx = { 108 | .connect_socket = -1, 109 | .keep_alive = false, 110 | .timeout_ms = NRF_CLOUD_REST_TIMEOUT_NONE, 111 | .auth = jwt_buf, 112 | .rx_buf = rx_buf, 113 | .rx_buf_len = sizeof(rx_buf), 114 | .fragment_size = 0, /* Defaults to CONFIG_NRF_CLOUD_REST_FRAGMENT_SIZE when 0 */ 115 | .status = 0, 116 | .response = NULL, 117 | .response_len = 0, 118 | .total_response_len = 0 119 | }; 120 | 121 | struct nrf_cloud_rest_pgps_request request = { 122 | .pgps_req = &pgps_request 123 | }; 124 | 125 | err = nrf_cloud_rest_pgps_data_get(&rest_ctx, &request); 126 | if (err) { 127 | LOG_ERR("Failed to send P-GPS request, error: %d", err); 128 | 129 | nrf_cloud_pgps_request_reset(); 130 | 131 | goto exit; 132 | } 133 | 134 | LOG_INF("Processing P-GPS response"); 135 | 136 | err = nrf_cloud_pgps_process(rest_ctx.response, rest_ctx.response_len); 137 | if (err) { 138 | LOG_ERR("Failed to process P-GPS response, error: %d", err); 139 | 140 | nrf_cloud_pgps_request_reset(); 141 | 142 | goto exit; 143 | } 144 | 145 | LOG_INF("P-GPS response processed"); 146 | 147 | exit: 148 | assistance_active = false; 149 | } 150 | 151 | static void inject_pgps_data_work_fn(struct k_work *work) 152 | { 153 | ARG_UNUSED(work); 154 | 155 | int err; 156 | 157 | assistance_active = true; 158 | 159 | LOG_INF("Injecting P-GPS ephemerides"); 160 | 161 | err = nrf_cloud_pgps_inject(prediction, &agnss_need); 162 | if (err) { 163 | LOG_ERR("Failed to inject P-GPS ephemerides"); 164 | } 165 | 166 | err = nrf_cloud_pgps_preemptive_updates(); 167 | if (err) { 168 | LOG_ERR("Failed to request P-GPS updates"); 169 | } 170 | 171 | assistance_active = false; 172 | } 173 | 174 | static void pgps_event_handler(struct nrf_cloud_pgps_event *event) 175 | { 176 | switch (event->type) { 177 | case PGPS_EVT_AVAILABLE: 178 | prediction = event->prediction; 179 | 180 | k_work_submit_to_queue(work_q, &inject_pgps_data_work); 181 | break; 182 | 183 | case PGPS_EVT_REQUEST: 184 | memcpy(&pgps_request, event->request, sizeof(pgps_request)); 185 | 186 | k_work_submit_to_queue(work_q, &get_pgps_data_work); 187 | break; 188 | 189 | case PGPS_EVT_LOADING: 190 | LOG_INF("Loading P-GPS predictions"); 191 | assistance_active = true; 192 | break; 193 | 194 | case PGPS_EVT_READY: 195 | LOG_INF("P-GPS predictions ready"); 196 | assistance_active = false; 197 | break; 198 | 199 | default: 200 | /* No action needed */ 201 | break; 202 | } 203 | } 204 | #endif /* CONFIG_NRF_CLOUD_PGPS */ 205 | 206 | #if defined(CONFIG_NRF_CLOUD_AGNSS) 207 | static const char *get_system_string(uint8_t system_id) 208 | { 209 | switch (system_id) { 210 | case NRF_MODEM_GNSS_SYSTEM_INVALID: 211 | return "invalid"; 212 | 213 | case NRF_MODEM_GNSS_SYSTEM_GPS: 214 | return "GPS"; 215 | 216 | case NRF_MODEM_GNSS_SYSTEM_QZSS: 217 | return "QZSS"; 218 | 219 | default: 220 | return "unknown"; 221 | } 222 | } 223 | #endif /* CONFIG_NRF_CLOUD_AGNSS */ 224 | 225 | int assistance_init(struct k_work_q *assistance_work_q) 226 | { 227 | work_q = assistance_work_q; 228 | 229 | #if defined(CONFIG_NRF_CLOUD_PGPS) 230 | k_work_init(&get_pgps_data_work, get_pgps_data_work_fn); 231 | k_work_init(&inject_pgps_data_work, inject_pgps_data_work_fn); 232 | 233 | struct nrf_cloud_pgps_init_param pgps_param = { 234 | .event_handler = pgps_event_handler, 235 | /* storage is defined by CONFIG_NRF_CLOUD_PGPS_STORAGE */ 236 | .storage_base = 0u, 237 | .storage_size = 0u 238 | }; 239 | 240 | if (nrf_cloud_pgps_init(&pgps_param) != 0) { 241 | LOG_ERR("Failed to initialize P-GPS"); 242 | return -1; 243 | } 244 | #endif /* CONFIG_NRF_CLOUD_PGPS */ 245 | 246 | return 0; 247 | } 248 | 249 | int assistance_request(struct nrf_modem_gnss_agnss_data_frame *agnss_request) 250 | { 251 | int err = 0; 252 | 253 | #if defined(CONFIG_NRF_CLOUD_PGPS) 254 | /* Store the A-GNSS data request for P-GPS use. */ 255 | memcpy(&agnss_need, agnss_request, sizeof(agnss_need)); 256 | 257 | #if defined(CONFIG_NRF_CLOUD_AGNSS) 258 | if (!agnss_request->data_flags) { 259 | /* No assistance needed from A-GNSS, skip directly to P-GPS. */ 260 | nrf_cloud_pgps_notify_prediction(); 261 | return 0; 262 | } 263 | 264 | /* P-GPS will handle GPS ephemerides, so skip those. */ 265 | agnss_request->system[0].sv_mask_ephe = 0; 266 | /* GPS almanacs are not needed with P-GPS, so skip those. */ 267 | agnss_request->system[0].sv_mask_alm = 0; 268 | #endif /* CONFIG_NRF_CLOUD_AGNSS */ 269 | #endif /* CONFIG_NRF_CLOUD_PGPS */ 270 | #if defined(CONFIG_NRF_CLOUD_AGNSS) 271 | assistance_active = true; 272 | 273 | err = nrf_cloud_jwt_generate(0, jwt_buf, sizeof(jwt_buf)); 274 | if (err) { 275 | LOG_ERR("Failed to generate JWT, error: %d", err); 276 | goto agnss_exit; 277 | } 278 | 279 | struct nrf_cloud_rest_context rest_ctx = { 280 | .connect_socket = -1, 281 | .keep_alive = false, 282 | .timeout_ms = NRF_CLOUD_REST_TIMEOUT_NONE, 283 | .auth = jwt_buf, 284 | .rx_buf = rx_buf, 285 | .rx_buf_len = sizeof(rx_buf), 286 | .fragment_size = 0, /* Defaults to CONFIG_NRF_CLOUD_REST_FRAGMENT_SIZE when 0 */ 287 | .status = 0, 288 | .response = NULL, 289 | .response_len = 0, 290 | .total_response_len = 0 291 | }; 292 | 293 | struct nrf_cloud_rest_agnss_request request = { 294 | .type = NRF_CLOUD_REST_AGNSS_REQ_CUSTOM, 295 | .agnss_req = agnss_request, 296 | .net_info = NULL, 297 | #if defined(CONFIG_NRF_CLOUD_AGNSS_FILTERED_RUNTIME) 298 | .filtered = true, 299 | /* Note: if you change the mask angle here, you may want to 300 | * also change it to match in gnss_init_and_start() in main.c. 301 | */ 302 | .mask_angle = CONFIG_NRF_CLOUD_AGNSS_ELEVATION_MASK 303 | /* Note: if CONFIG_NRF_CLOUD_AGNSS_FILTERED is enabled but 304 | * CONFIG_NRF_CLOUD_AGNSS_FILTERED_RUNTIME is not, 305 | * the nrf_cloud_rest library will set the above fields to 306 | * true and CONFIG_NRF_CLOUD_AGNSS_ELEVATION_MASK respectively. 307 | * When CONFIG_NRF_CLOUD_AGNSS_FILTERED is disabled, it will 308 | * set them to false and 0. 309 | */ 310 | #endif 311 | }; 312 | 313 | struct nrf_cloud_rest_agnss_result result = { 314 | .buf = agnss_data_buf, 315 | .buf_sz = sizeof(agnss_data_buf), 316 | .agnss_sz = 0 317 | }; 318 | 319 | struct lte_lc_cells_info net_info = { 0 }; 320 | 321 | err = serving_cell_info_get(&net_info.current_cell); 322 | if (err) { 323 | LOG_ERR("Could not get cell info, error: %d", err); 324 | } else { 325 | /* Network info for the location request. */ 326 | request.net_info = &net_info; 327 | } 328 | 329 | LOG_INF("Requesting A-GNSS data: data_flags: 0x%02x", agnss_request->data_flags); 330 | for (int i = 0; i < agnss_request->system_count; i++) { 331 | LOG_INF("Requesting A-GNSS data: %s ephe: 0x%llx, alm: 0x%llx", 332 | get_system_string(agnss_request->system[i].system_id), 333 | agnss_request->system[i].sv_mask_ephe, 334 | agnss_request->system[i].sv_mask_alm); 335 | } 336 | 337 | err = nrf_cloud_rest_agnss_data_get(&rest_ctx, &request, &result); 338 | if (err) { 339 | LOG_ERR("Failed to get A-GNSS data, error: %d", err); 340 | goto agnss_exit; 341 | } 342 | 343 | LOG_INF("Processing A-GNSS data"); 344 | 345 | err = nrf_cloud_agnss_process(result.buf, result.agnss_sz); 346 | if (err) { 347 | LOG_ERR("Failed to process A-GNSS data, error: %d", err); 348 | goto agnss_exit; 349 | } 350 | 351 | LOG_INF("A-GNSS data processed"); 352 | 353 | agnss_exit: 354 | assistance_active = false; 355 | #endif /* CONFIG_NRF_CLOUD_AGNSS */ 356 | 357 | #if defined(CONFIG_NRF_CLOUD_PGPS) 358 | nrf_cloud_pgps_notify_prediction(); 359 | #endif /* CONFIG_NRF_CLOUD_PGPS */ 360 | 361 | return err; 362 | } 363 | 364 | bool assistance_is_active(void) 365 | { 366 | return assistance_active; 367 | } 368 | -------------------------------------------------------------------------------- /gnss/README.rst: -------------------------------------------------------------------------------- 1 | .. _gps_with_supl_support_sample: 2 | .. _agps_sample: 3 | .. _gnss_sample: 4 | 5 | Cellular: GNSS 6 | ############## 7 | 8 | .. contents:: 9 | :local: 10 | :depth: 2 11 | 12 | This sample demonstrates how to use the :ref:`nrfxlib:gnss_interface` to control the `GNSS`_ module. 13 | It also shows how to improve fix speed and accuracy with the :ref:`lib_nrf_cloud_agnss` library and how to use the :ref:`lib_nrf_cloud_pgps` library. 14 | Assistance data is downloaded from nRF Cloud using `nRF Cloud's REST-based device API `_. 15 | 16 | Requirements 17 | ************ 18 | 19 | The sample supports the following development kits: 20 | 21 | .. table-from-sample-yaml:: 22 | 23 | .. include:: /includes/tfm.txt 24 | 25 | Overview 26 | ******** 27 | 28 | The sample first initializes the GNSS module. 29 | Then it handles events from the interface, reads the associated data and outputs information to the console. 30 | Because `NMEA`_ data needs to be read as soon as an NMEA event is received, a :ref:`Zephyr message queue ` is used for buffering the NMEA strings. 31 | The event handler function reads the received NMEA strings and puts those into the message queue. 32 | The consumer loop reads from the queue and outputs the strings to the console. 33 | 34 | Operation modes 35 | =============== 36 | 37 | The sample supports different operation modes: 38 | 39 | * Continuous 40 | * Periodic 41 | * Time-to-first-fix (TTFF) test 42 | 43 | By default, the sample runs in continuous tracking mode. 44 | In continuous mode, GNSS tries to acquire a fix once a second. 45 | 46 | In periodic mode, fixes are acquired periodically with the set interval. 47 | 48 | In TTFF test mode, the sample acquires fixes periodically and calculates the TTFF for each fix. 49 | You can use the TTFF test mode without assistance or with any supported assistance method. 50 | You can also configure it to perform cold starts, where the stored data is deleted from GNSS before each start. 51 | If you enable assistance with cold starts, new assistance data is also downloaded and injected to GNSS before each start. 52 | 53 | Output modes 54 | ============ 55 | 56 | The sample supports two output modes: 57 | 58 | * Position, Velocity, and Time (PVT) and NMEA 59 | * NMEA-only 60 | 61 | By default, the sample displays information from both PVT and NMEA strings. 62 | You can also configure the sample to run in NMEA-only output mode, where only the NMEA strings are displayed in the console. 63 | In the NMEA-only output mode, you can visualize the data from the GNSS using a third-party tool. 64 | 65 | A-GNSS and P-GPS 66 | ================ 67 | 68 | When support for A-GNSS or P-GPS, or both, is enabled, a :ref:`Zephyr workqueue ` is used for downloading the assistance data. 69 | Downloading the data can take some time. 70 | The workqueue ensures that the main thread is not blocked during the operation. 71 | 72 | When assistance support is enabled, the sample receives an A-GNSS data request notification from the GNSS module, and it starts downloading the assistance data requested by the GNSS module. 73 | The sample then displays the information in the terminal about the download process. 74 | Finally, after the download completes, the sample switches back to the previous display mode. 75 | 76 | .. note:: 77 | To download assistance data, your device must have a valid JWT signing key installed and registered with `nRF Cloud`_. 78 | 79 | .. include:: /includes/nrf_cloud_rest_sample_requirements.txt 80 | :start-after: requirement_keysign_moreinfo_start 81 | 82 | Minimal assistance 83 | ================== 84 | 85 | GNSS satellite acquisition can also be assisted by providing factory almanac, GPS time, and coarse location to the GNSS module. 86 | Using this information, GNSS can calculate which satellites it should search for and what are the expected Doppler frequencies. 87 | 88 | The sample includes a factory almanac that is written to the file system when the sample starts. 89 | The date for the factory almanac generation is in the :file:`factory_almanac.h` file. 90 | The almanac gets inaccurate with time and should be updated occasionally. 91 | GNSS can use an almanac until it is two years old, but generally it should be updated every few months. 92 | 93 | Configuration 94 | ************* 95 | 96 | |config| 97 | 98 | Configuration options 99 | ===================== 100 | 101 | Check and configure the following Kconfig options for the sample: 102 | 103 | .. _CONFIG_GNSS_SAMPLE_NMEA_ONLY: 104 | 105 | CONFIG_GNSS_SAMPLE_NMEA_ONLY - To enable NMEA-only output mode 106 | The NMEA-only output mode can be used for example with 3rd party tools to visualize the GNSS output. 107 | 108 | .. _CONFIG_GNSS_SAMPLE_ASSISTANCE_NRF_CLOUD: 109 | 110 | CONFIG_GNSS_SAMPLE_ASSISTANCE_NRF_CLOUD - To use nRF Cloud A-GNSS 111 | This configuration option enables A-GNSS usage. 112 | 113 | .. _CONFIG_GNSS_SAMPLE_ASSISTANCE_MINIMAL: 114 | 115 | CONFIG_GNSS_SAMPLE_ASSISTANCE_MINIMAL - To use minimal assistance 116 | This configuration option enables assistance with factory almanac, time and location. 117 | 118 | .. _CONFIG_GNSS_SAMPLE_MODE_PERIODIC: 119 | 120 | CONFIG_GNSS_SAMPLE_MODE_PERIODIC - To enable periodic fixes 121 | This configuration option enables periodic fixes instead of continuous tracking. 122 | 123 | .. _CONFIG_GNSS_SAMPLE_PERIODIC_INTERVAL: 124 | 125 | CONFIG_GNSS_SAMPLE_PERIODIC_INTERVAL - To set interval (in seconds) for periodic fixes 126 | This configuration option sets the desired fix interval. 127 | 128 | .. _CONFIG_GNSS_SAMPLE_PERIODIC_TIMEOUT: 129 | 130 | CONFIG_GNSS_SAMPLE_PERIODIC_TIMEOUT - To set desired timeout (in seconds) for periodic fixes 131 | This configuration option sets the desired timeout for periodic fixes. 132 | 133 | .. _CONFIG_GNSS_SAMPLE_MODE_TTFF_TEST: 134 | 135 | CONFIG_GNSS_SAMPLE_MODE_TTFF_TEST - To enable time-to-first-fix (TTFF) test mode 136 | This configuration enables the TTFF test mode instead of continuous tracking. 137 | When TTFF test mode is enabled, the :ref:`CONFIG_GNSS_SAMPLE_NMEA_ONLY ` option is automatically selected. 138 | 139 | .. _CONFIG_GNSS_SAMPLE_MODE_TTFF_TEST_INTERVAL: 140 | 141 | CONFIG_GNSS_SAMPLE_MODE_TTFF_TEST_INTERVAL - To set the time between fixes in TTFF test mode 142 | This configuration option sets the time between fixes in TTFF test mode. 143 | 144 | .. _CONFIG_GNSS_SAMPLE_MODE_TTFF_TEST_COLD_START: 145 | 146 | CONFIG_GNSS_SAMPLE_MODE_TTFF_TEST_COLD_START - To perform cold starts in TTFF test mode 147 | This configuration option makes the sample perform GNSS cold starts instead of hot starts in TTFF test mode. 148 | When assistance is used, LTE may block the GNSS operation and increase the time needed to get a fix. 149 | 150 | .. _CONFIG_GNSS_SAMPLE_LTE_ON_DEMAND: 151 | 152 | CONFIG_GNSS_SAMPLE_LTE_ON_DEMAND - To disable LTE after assistance download 153 | When using assistance, LTE may block the GNSS operation and increase the time needed to get a fix. 154 | This configuration option disables LTE after the assistance data has been downloaded, so that GNSS can run without interruptions. 155 | 156 | Additional configuration 157 | ======================== 158 | 159 | Check and configure the following library option that is used by the sample: 160 | 161 | * :kconfig:option:`CONFIG_MODEM_ANTENNA_GNSS_EXTERNAL` - Selects an external GNSS antenna. 162 | 163 | .. include:: /libraries/modem/nrf_modem_lib/nrf_modem_lib_trace.rst 164 | :start-after: modem_lib_sending_traces_UART_start 165 | :end-before: modem_lib_sending_traces_UART_end 166 | 167 | Building and running 168 | ******************** 169 | 170 | .. |sample path| replace:: :file:`samples/cellular/gnss` 171 | 172 | .. include:: /includes/build_and_run_ns.txt 173 | 174 | If the sample is to be used with the SUPL client library, the library must be downloaded and enabled in the sample configuration. 175 | You can download it from the `Nordic Semiconductor website`_. 176 | See :ref:`supl_client` for information on installing and enabling the SUPL client library. 177 | 178 | Testing 179 | ======= 180 | 181 | After programming the sample and all the prerequisites to the development kit, test it by performing the following steps: 182 | 183 | 1. Connect your nRF91 Series DK to the PC using a USB cable and power on or reset your nRF91 Series DK. 184 | #. Open a terminal emulator. 185 | #. Test the sample by performing the following steps: 186 | 187 | If the default output mode is enabled: 188 | 189 | a. Observe that the following information is displayed in the terminal emulator: 190 | 191 | .. code-block:: console 192 | 193 | Tracking: 0 Using: 0 Unhealthy: 0 194 | ----------------------------------- 195 | Seconds since last fix: 1 196 | Searching [-] 197 | 198 | NMEA strings: 199 | 200 | $GPGGA,000000.00,,,,,0,,99.99,,M,0,,*37 201 | $GPGLL,,,,,000000.00,V,A*45 202 | $GPGSA,A,1,,,,,,,,,,,,,99.99,99.99,99.99,1*2D 203 | $GPGSV,1,1,0,,,,,,,,,,,,,,,,,1*54 204 | $GPRMC,000000.00,V,,,,,,,060180,,,N,V*08 205 | --------------------------------- 206 | 207 | #. Observe that the numbers associated with the displayed parameters **Tracking** and **Using** change. 208 | #. Observe that the sample displays the following information upon acquiring a fix: 209 | 210 | .. code-block:: console 211 | 212 | Tracking: 7 Using: 5 Unhealthy: 0 213 | ----------------------------------- 214 | Latitude: 61.491275 215 | Longitude: 23.771611 216 | Altitude: 116.3 m 217 | Accuracy: 4.2 m 218 | Speed: 0.0 m/s 219 | Speed accuracy: 0.8 m/s 220 | Heading: 0.0 deg 221 | Date: 2020-03-06 222 | Time (UTC): 05:48:24 223 | PDOP: 3.1 224 | HDOP: 2.1 225 | VDOP: 2.3 226 | TDOP: 1.8 227 | 228 | NMEA strings: 229 | 230 | $GPGGA,054824.58,6129.28608,N,02346.17887,E,1,07,2.05,116.27,M,0,,*22 231 | $GPGLL,6129.28608,N,02346.17887,E,054824.58,A,A*6B 232 | $GPGSA,A,3,10,12,17,24,28,,,,,,,,3.05,2.05,2.25,1*13 233 | $GPGSV,2,1,7,17,50,083,41,24,68,250,38,10,14,294,46,28,23,071,38,1*56 234 | $GPGSV,2,2,7,12,29,240,36,19,00,000,32,1,00,000,33,1*50 235 | $GPRMC,054824.58,A,6129.28608,N,02346.17887,E,0.08,0.00,030620,,,A,V*29 236 | --------------------------------- 237 | 238 | If NMEA-only output mode is enabled: 239 | 240 | a. Observe that the following information is displayed in the terminal emulator: 241 | 242 | .. code-block:: console 243 | 244 | $GPGGA,000000.00,,,,,0,,99.99,,M,0,,*37 245 | $GPGLL,,,,,000000.00,V,A*45 246 | $GPGSA,A,1,,,,,,,,,,,,,99.99,99.99,99.99,1*2D 247 | $GPGSV,1,1,0,,,,,,,,,,,,,,,,,1*54 248 | $GPRMC,000000.00,V,,,,,,,060180,,,N,V*08 249 | $GPGGA,000001.00,,,,,0,02,99.99,,M,0,,*34 250 | $GPGLL,,,,,000001.00,V,A*44 251 | $GPGSA,A,1,,,,,,,,,,,,,99.99,99.99,99.99,1*2D 252 | $GPGSV,1,1,2,17,,,24,1,,,28,1*6D 253 | $GPRMC,000001.00,V,,,,,,,060180,,,N,V*09 254 | $GPGGA,000002.00,,,,,0,02,99.99,,M,0,,*37 255 | $GPGLL,,,,,000002.00,V,A*47 256 | $GPGSA,A,1,,,,,,,,,,,,,99.99,99.99,99.99,1*2D 257 | $GPGSV,1,1,2,17,,,24,1,,,28,1*6D 258 | $GPRMC,000002.00,V,,,,,,,060180,,,N,V*0A 259 | 260 | If TTFF test mode is enabled: 261 | 262 | a. Observe that the following information is displayed in the terminal emulator: 263 | 264 | .. code-block:: console 265 | 266 | $GPGGA,000033.00,,,,,0,,99.99,,M,,M,,*66 267 | $GPGLL,,,,,000033.00,V,N*4A 268 | $GPGSA,A,1,,,,,,,,,,,,,99.99,99.99,99.99,1*2D 269 | $GPGSV,3,1,10,1,,,36,5,,,26,7,,,25,10,,,44,1*53 270 | $GPGSV,3,2,10,14,,,43,17,,,37,21,,,41,23,,,43,1*64 271 | $GPGSV,3,3,10,24,,,31,28,,,39,1*61 272 | $GPRMC,000033.00,V,,,,,,,060180,,,N,V*08 273 | $GPGGA,121300.68,6129.28608,N,02346.17887,E,1,05,2.41,123.44,M,,M,,*7A 274 | $GPGLL,6129.28608,N,02346.17887,E,121300.68,A,A*63 275 | $GPGSA,A,3,01,10,17,21,23,,,,,,,,6.32,2.41,5.84,1*12 276 | $GPGSV,4,1,14,1,17,047,37,7,-22,107,25,10,22,314,44,12,09,232,,1*41 277 | $GPGSV,4,2,14,13,29,173,,14,38,072,44,15,40,211,,17,46,106,37,1*65 278 | $GPGSV,4,3,14,19,35,139,,21,15,019,41,23,19,279,42,24,51,273,32,1*6F 279 | $GPGSV,4,4,14,28,,,39,30,00,110,,1*52 280 | $GPRMC,121300.68,A,6129.28608,N,02346.17887,E,0.10,0.00,200122,,,A,V*2C 281 | [00:00:34.790,649] gnss_sample: Time to fix: 34 282 | [00:00:34.796,447] gnss_sample: Sleeping for 120 seconds 283 | [00:02:34.699,493] gnss_sample: Starting GNSS 284 | $GPGGA,121500.82,,,,,0,,99.99,,M,,M,,*6B 285 | $GPGLL,,,,,121500.82,V,N*47 286 | $GPGSA,A,1,,,,,,,,,,,,,99.99,99.99,99.99,1*2D 287 | $GPGSV,1,1,0,,,,,,,,,,,,,,,,,1*54 288 | $GPRMC,121500.82,V,,,,,,,200122,,,N,V*09 289 | $GPGGA,121501.82,6129.28608,N,02346.17887,E,1,04,2.73,118.22,M,,M,,*78 290 | $GPGLL,6129.28608,N,02346.17887,E,121501.82,A,A*69 291 | $GPGSA,A,3,10,17,21,23,,,,,,,,,7.59,2.73,7.08,1*18 292 | $GPGSV,4,1,13,1,18,046,28,10,22,313,49,12,10,232,26,13,28,173,25,1*51 293 | $GPGSV,4,2,13,14,37,072,50,15,40,211,25,17,46,105,45,19,35,138,31,1*63 294 | $GPGSV,4,3,13,21,15,018,45,23,18,278,45,24,52,273,,28,,,44,1*57 295 | $GPGSV,4,4,13,30,00,110,,1*55 296 | $GPRMC,121501.82,A,6129.28608,N,02346.17887,E,0.16,0.00,200122,,,A,V*20 297 | [00:02:35.940,582] gnss_sample: Time to fix: 1 298 | [00:02:35.946,319] gnss_sample: Sleeping for 120 seconds 299 | 300 | #. Observe that the samples displays the time to fix for each fix. 301 | 302 | If A-GNSS and/or P-GPS support is enabled: 303 | 304 | a. Observe that the following message is displayed in the terminal emulator immediately after the device boots: 305 | 306 | .. code-block:: console 307 | 308 | [00:00:04.488,494] gnss_sample: Assistance data needed, ephe 0xffffffff, alm 0xffffffff, flags 0x3b 309 | 310 | #. Observe the following actions in the terminal emulator: 311 | 312 | i. The sample downloads the requested assistance data if needed (with P-GPS, the data may already be available in the flash memory). 313 | #. The sample continues after the download has completed. 314 | 315 | Dependencies 316 | ************ 317 | 318 | This sample uses the following |NCS| libraries: 319 | 320 | * :ref:`lib_nrf_cloud_agnss` 321 | * :ref:`lib_nrf_cloud_pgps` 322 | * :ref:`lib_nrf_cloud_rest` 323 | * :ref:`supl_client` 324 | * :ref:`lib_at_host` 325 | 326 | It uses the following `sdk-nrfxlib`_ library: 327 | 328 | * :ref:`nrfxlib:nrf_modem` 329 | 330 | It uses the following Zephyr library: 331 | 332 | * :ref:`net_socket_offloading` 333 | * :ref:`settings_api` 334 | 335 | In addition, it uses the following secure firmware component: 336 | 337 | * :ref:`Trusted Firmware-M ` 338 | -------------------------------------------------------------------------------- /projects/geofencing/README.rst: -------------------------------------------------------------------------------- 1 | .. _gps_with_supl_support_sample: 2 | .. _agps_sample: 3 | .. _gnss_sample: 4 | 5 | Cellular: GNSS 6 | ############## 7 | 8 | .. contents:: 9 | :local: 10 | :depth: 2 11 | 12 | This sample demonstrates how to use the :ref:`nrfxlib:gnss_interface` to control the `GNSS`_ module. 13 | It also shows how to improve fix speed and accuracy with the :ref:`lib_nrf_cloud_agnss` library and how to use the :ref:`lib_nrf_cloud_pgps` library. 14 | Assistance data is downloaded from nRF Cloud using `nRF Cloud's REST-based device API `_. 15 | 16 | Requirements 17 | ************ 18 | 19 | The sample supports the following development kits: 20 | 21 | .. table-from-sample-yaml:: 22 | 23 | .. include:: /includes/tfm.txt 24 | 25 | Overview 26 | ******** 27 | 28 | The sample first initializes the GNSS module. 29 | Then it handles events from the interface, reads the associated data and outputs information to the console. 30 | Because `NMEA`_ data needs to be read as soon as an NMEA event is received, a :ref:`Zephyr message queue ` is used for buffering the NMEA strings. 31 | The event handler function reads the received NMEA strings and puts those into the message queue. 32 | The consumer loop reads from the queue and outputs the strings to the console. 33 | 34 | Operation modes 35 | =============== 36 | 37 | The sample supports different operation modes: 38 | 39 | * Continuous 40 | * Periodic 41 | * Time-to-first-fix (TTFF) test 42 | 43 | By default, the sample runs in continuous tracking mode. 44 | In continuous mode, GNSS tries to acquire a fix once a second. 45 | 46 | In periodic mode, fixes are acquired periodically with the set interval. 47 | 48 | In TTFF test mode, the sample acquires fixes periodically and calculates the TTFF for each fix. 49 | You can use the TTFF test mode without assistance or with any supported assistance method. 50 | You can also configure it to perform cold starts, where the stored data is deleted from GNSS before each start. 51 | If you enable assistance with cold starts, new assistance data is also downloaded and injected to GNSS before each start. 52 | 53 | Output modes 54 | ============ 55 | 56 | The sample supports two output modes: 57 | 58 | * Position, Velocity, and Time (PVT) and NMEA 59 | * NMEA-only 60 | 61 | By default, the sample displays information from both PVT and NMEA strings. 62 | You can also configure the sample to run in NMEA-only output mode, where only the NMEA strings are displayed in the console. 63 | In the NMEA-only output mode, you can visualize the data from the GNSS using a third-party tool. 64 | 65 | A-GNSS and P-GPS 66 | ================ 67 | 68 | When support for A-GNSS or P-GPS, or both, is enabled, a :ref:`Zephyr workqueue ` is used for downloading the assistance data. 69 | Downloading the data can take some time. 70 | The workqueue ensures that the main thread is not blocked during the operation. 71 | 72 | When assistance support is enabled, the sample receives an A-GNSS data request notification from the GNSS module, and it starts downloading the assistance data requested by the GNSS module. 73 | The sample then displays the information in the terminal about the download process. 74 | Finally, after the download completes, the sample switches back to the previous display mode. 75 | 76 | .. note:: 77 | To download assistance data, your device must have a valid JWT signing key installed and registered with `nRF Cloud`_. 78 | 79 | .. include:: /includes/nrf_cloud_rest_sample_requirements.txt 80 | :start-after: requirement_keysign_moreinfo_start 81 | 82 | Minimal assistance 83 | ================== 84 | 85 | GNSS satellite acquisition can also be assisted by providing factory almanac, GPS time, and coarse location to the GNSS module. 86 | Using this information, GNSS can calculate which satellites it should search for and what are the expected Doppler frequencies. 87 | 88 | The sample includes a factory almanac that is written to the file system when the sample starts. 89 | The date for the factory almanac generation is in the :file:`factory_almanac.h` file. 90 | The almanac gets inaccurate with time and should be updated occasionally. 91 | GNSS can use an almanac until it is two years old, but generally it should be updated every few months. 92 | 93 | Configuration 94 | ************* 95 | 96 | |config| 97 | 98 | Configuration options 99 | ===================== 100 | 101 | Check and configure the following Kconfig options for the sample: 102 | 103 | .. _CONFIG_GNSS_SAMPLE_NMEA_ONLY: 104 | 105 | CONFIG_GNSS_SAMPLE_NMEA_ONLY - To enable NMEA-only output mode 106 | The NMEA-only output mode can be used for example with 3rd party tools to visualize the GNSS output. 107 | 108 | .. _CONFIG_GNSS_SAMPLE_ASSISTANCE_NRF_CLOUD: 109 | 110 | CONFIG_GNSS_SAMPLE_ASSISTANCE_NRF_CLOUD - To use nRF Cloud A-GNSS 111 | This configuration option enables A-GNSS usage. 112 | 113 | .. _CONFIG_GNSS_SAMPLE_ASSISTANCE_MINIMAL: 114 | 115 | CONFIG_GNSS_SAMPLE_ASSISTANCE_MINIMAL - To use minimal assistance 116 | This configuration option enables assistance with factory almanac, time and location. 117 | 118 | .. _CONFIG_GNSS_SAMPLE_MODE_PERIODIC: 119 | 120 | CONFIG_GNSS_SAMPLE_MODE_PERIODIC - To enable periodic fixes 121 | This configuration option enables periodic fixes instead of continuous tracking. 122 | 123 | .. _CONFIG_GNSS_SAMPLE_PERIODIC_INTERVAL: 124 | 125 | CONFIG_GNSS_SAMPLE_PERIODIC_INTERVAL - To set interval (in seconds) for periodic fixes 126 | This configuration option sets the desired fix interval. 127 | 128 | .. _CONFIG_GNSS_SAMPLE_PERIODIC_TIMEOUT: 129 | 130 | CONFIG_GNSS_SAMPLE_PERIODIC_TIMEOUT - To set desired timeout (in seconds) for periodic fixes 131 | This configuration option sets the desired timeout for periodic fixes. 132 | 133 | .. _CONFIG_GNSS_SAMPLE_MODE_TTFF_TEST: 134 | 135 | CONFIG_GNSS_SAMPLE_MODE_TTFF_TEST - To enable time-to-first-fix (TTFF) test mode 136 | This configuration enables the TTFF test mode instead of continuous tracking. 137 | When TTFF test mode is enabled, the :ref:`CONFIG_GNSS_SAMPLE_NMEA_ONLY ` option is automatically selected. 138 | 139 | .. _CONFIG_GNSS_SAMPLE_MODE_TTFF_TEST_INTERVAL: 140 | 141 | CONFIG_GNSS_SAMPLE_MODE_TTFF_TEST_INTERVAL - To set the time between fixes in TTFF test mode 142 | This configuration option sets the time between fixes in TTFF test mode. 143 | 144 | .. _CONFIG_GNSS_SAMPLE_MODE_TTFF_TEST_COLD_START: 145 | 146 | CONFIG_GNSS_SAMPLE_MODE_TTFF_TEST_COLD_START - To perform cold starts in TTFF test mode 147 | This configuration option makes the sample perform GNSS cold starts instead of hot starts in TTFF test mode. 148 | When assistance is used, LTE may block the GNSS operation and increase the time needed to get a fix. 149 | 150 | .. _CONFIG_GNSS_SAMPLE_LTE_ON_DEMAND: 151 | 152 | CONFIG_GNSS_SAMPLE_LTE_ON_DEMAND - To disable LTE after assistance download 153 | When using assistance, LTE may block the GNSS operation and increase the time needed to get a fix. 154 | This configuration option disables LTE after the assistance data has been downloaded, so that GNSS can run without interruptions. 155 | 156 | Additional configuration 157 | ======================== 158 | 159 | Check and configure the following library option that is used by the sample: 160 | 161 | * :kconfig:option:`CONFIG_MODEM_ANTENNA_GNSS_EXTERNAL` - Selects an external GNSS antenna. 162 | 163 | .. include:: /libraries/modem/nrf_modem_lib/nrf_modem_lib_trace.rst 164 | :start-after: modem_lib_sending_traces_UART_start 165 | :end-before: modem_lib_sending_traces_UART_end 166 | 167 | Building and running 168 | ******************** 169 | 170 | .. |sample path| replace:: :file:`samples/cellular/gnss` 171 | 172 | .. include:: /includes/build_and_run_ns.txt 173 | 174 | If the sample is to be used with the SUPL client library, the library must be downloaded and enabled in the sample configuration. 175 | You can download it from the `Nordic Semiconductor website`_. 176 | See :ref:`supl_client` for information on installing and enabling the SUPL client library. 177 | 178 | Testing 179 | ======= 180 | 181 | After programming the sample and all the prerequisites to the development kit, test it by performing the following steps: 182 | 183 | 1. Connect your nRF91 Series DK to the PC using a USB cable and power on or reset your nRF91 Series DK. 184 | #. Open a terminal emulator. 185 | #. Test the sample by performing the following steps: 186 | 187 | If the default output mode is enabled: 188 | 189 | a. Observe that the following information is displayed in the terminal emulator: 190 | 191 | .. code-block:: console 192 | 193 | Tracking: 0 Using: 0 Unhealthy: 0 194 | ----------------------------------- 195 | Seconds since last fix: 1 196 | Searching [-] 197 | 198 | NMEA strings: 199 | 200 | $GPGGA,000000.00,,,,,0,,99.99,,M,0,,*37 201 | $GPGLL,,,,,000000.00,V,A*45 202 | $GPGSA,A,1,,,,,,,,,,,,,99.99,99.99,99.99,1*2D 203 | $GPGSV,1,1,0,,,,,,,,,,,,,,,,,1*54 204 | $GPRMC,000000.00,V,,,,,,,060180,,,N,V*08 205 | --------------------------------- 206 | 207 | #. Observe that the numbers associated with the displayed parameters **Tracking** and **Using** change. 208 | #. Observe that the sample displays the following information upon acquiring a fix: 209 | 210 | .. code-block:: console 211 | 212 | Tracking: 7 Using: 5 Unhealthy: 0 213 | ----------------------------------- 214 | Latitude: 61.491275 215 | Longitude: 23.771611 216 | Altitude: 116.3 m 217 | Accuracy: 4.2 m 218 | Speed: 0.0 m/s 219 | Speed accuracy: 0.8 m/s 220 | Heading: 0.0 deg 221 | Date: 2020-03-06 222 | Time (UTC): 05:48:24 223 | PDOP: 3.1 224 | HDOP: 2.1 225 | VDOP: 2.3 226 | TDOP: 1.8 227 | 228 | NMEA strings: 229 | 230 | $GPGGA,054824.58,6129.28608,N,02346.17887,E,1,07,2.05,116.27,M,0,,*22 231 | $GPGLL,6129.28608,N,02346.17887,E,054824.58,A,A*6B 232 | $GPGSA,A,3,10,12,17,24,28,,,,,,,,3.05,2.05,2.25,1*13 233 | $GPGSV,2,1,7,17,50,083,41,24,68,250,38,10,14,294,46,28,23,071,38,1*56 234 | $GPGSV,2,2,7,12,29,240,36,19,00,000,32,1,00,000,33,1*50 235 | $GPRMC,054824.58,A,6129.28608,N,02346.17887,E,0.08,0.00,030620,,,A,V*29 236 | --------------------------------- 237 | 238 | If NMEA-only output mode is enabled: 239 | 240 | a. Observe that the following information is displayed in the terminal emulator: 241 | 242 | .. code-block:: console 243 | 244 | $GPGGA,000000.00,,,,,0,,99.99,,M,0,,*37 245 | $GPGLL,,,,,000000.00,V,A*45 246 | $GPGSA,A,1,,,,,,,,,,,,,99.99,99.99,99.99,1*2D 247 | $GPGSV,1,1,0,,,,,,,,,,,,,,,,,1*54 248 | $GPRMC,000000.00,V,,,,,,,060180,,,N,V*08 249 | $GPGGA,000001.00,,,,,0,02,99.99,,M,0,,*34 250 | $GPGLL,,,,,000001.00,V,A*44 251 | $GPGSA,A,1,,,,,,,,,,,,,99.99,99.99,99.99,1*2D 252 | $GPGSV,1,1,2,17,,,24,1,,,28,1*6D 253 | $GPRMC,000001.00,V,,,,,,,060180,,,N,V*09 254 | $GPGGA,000002.00,,,,,0,02,99.99,,M,0,,*37 255 | $GPGLL,,,,,000002.00,V,A*47 256 | $GPGSA,A,1,,,,,,,,,,,,,99.99,99.99,99.99,1*2D 257 | $GPGSV,1,1,2,17,,,24,1,,,28,1*6D 258 | $GPRMC,000002.00,V,,,,,,,060180,,,N,V*0A 259 | 260 | If TTFF test mode is enabled: 261 | 262 | a. Observe that the following information is displayed in the terminal emulator: 263 | 264 | .. code-block:: console 265 | 266 | $GPGGA,000033.00,,,,,0,,99.99,,M,,M,,*66 267 | $GPGLL,,,,,000033.00,V,N*4A 268 | $GPGSA,A,1,,,,,,,,,,,,,99.99,99.99,99.99,1*2D 269 | $GPGSV,3,1,10,1,,,36,5,,,26,7,,,25,10,,,44,1*53 270 | $GPGSV,3,2,10,14,,,43,17,,,37,21,,,41,23,,,43,1*64 271 | $GPGSV,3,3,10,24,,,31,28,,,39,1*61 272 | $GPRMC,000033.00,V,,,,,,,060180,,,N,V*08 273 | $GPGGA,121300.68,6129.28608,N,02346.17887,E,1,05,2.41,123.44,M,,M,,*7A 274 | $GPGLL,6129.28608,N,02346.17887,E,121300.68,A,A*63 275 | $GPGSA,A,3,01,10,17,21,23,,,,,,,,6.32,2.41,5.84,1*12 276 | $GPGSV,4,1,14,1,17,047,37,7,-22,107,25,10,22,314,44,12,09,232,,1*41 277 | $GPGSV,4,2,14,13,29,173,,14,38,072,44,15,40,211,,17,46,106,37,1*65 278 | $GPGSV,4,3,14,19,35,139,,21,15,019,41,23,19,279,42,24,51,273,32,1*6F 279 | $GPGSV,4,4,14,28,,,39,30,00,110,,1*52 280 | $GPRMC,121300.68,A,6129.28608,N,02346.17887,E,0.10,0.00,200122,,,A,V*2C 281 | [00:00:34.790,649] gnss_sample: Time to fix: 34 282 | [00:00:34.796,447] gnss_sample: Sleeping for 120 seconds 283 | [00:02:34.699,493] gnss_sample: Starting GNSS 284 | $GPGGA,121500.82,,,,,0,,99.99,,M,,M,,*6B 285 | $GPGLL,,,,,121500.82,V,N*47 286 | $GPGSA,A,1,,,,,,,,,,,,,99.99,99.99,99.99,1*2D 287 | $GPGSV,1,1,0,,,,,,,,,,,,,,,,,1*54 288 | $GPRMC,121500.82,V,,,,,,,200122,,,N,V*09 289 | $GPGGA,121501.82,6129.28608,N,02346.17887,E,1,04,2.73,118.22,M,,M,,*78 290 | $GPGLL,6129.28608,N,02346.17887,E,121501.82,A,A*69 291 | $GPGSA,A,3,10,17,21,23,,,,,,,,,7.59,2.73,7.08,1*18 292 | $GPGSV,4,1,13,1,18,046,28,10,22,313,49,12,10,232,26,13,28,173,25,1*51 293 | $GPGSV,4,2,13,14,37,072,50,15,40,211,25,17,46,105,45,19,35,138,31,1*63 294 | $GPGSV,4,3,13,21,15,018,45,23,18,278,45,24,52,273,,28,,,44,1*57 295 | $GPGSV,4,4,13,30,00,110,,1*55 296 | $GPRMC,121501.82,A,6129.28608,N,02346.17887,E,0.16,0.00,200122,,,A,V*20 297 | [00:02:35.940,582] gnss_sample: Time to fix: 1 298 | [00:02:35.946,319] gnss_sample: Sleeping for 120 seconds 299 | 300 | #. Observe that the samples displays the time to fix for each fix. 301 | 302 | If A-GNSS and/or P-GPS support is enabled: 303 | 304 | a. Observe that the following message is displayed in the terminal emulator immediately after the device boots: 305 | 306 | .. code-block:: console 307 | 308 | [00:00:04.488,494] gnss_sample: Assistance data needed, ephe 0xffffffff, alm 0xffffffff, flags 0x3b 309 | 310 | #. Observe the following actions in the terminal emulator: 311 | 312 | i. The sample downloads the requested assistance data if needed (with P-GPS, the data may already be available in the flash memory). 313 | #. The sample continues after the download has completed. 314 | 315 | Dependencies 316 | ************ 317 | 318 | This sample uses the following |NCS| libraries: 319 | 320 | * :ref:`lib_nrf_cloud_agnss` 321 | * :ref:`lib_nrf_cloud_pgps` 322 | * :ref:`lib_nrf_cloud_rest` 323 | * :ref:`supl_client` 324 | * :ref:`lib_at_host` 325 | 326 | It uses the following `sdk-nrfxlib`_ library: 327 | 328 | * :ref:`nrfxlib:nrf_modem` 329 | 330 | It uses the following Zephyr library: 331 | 332 | * :ref:`net_socket_offloading` 333 | * :ref:`settings_api` 334 | 335 | In addition, it uses the following secure firmware component: 336 | 337 | * :ref:`Trusted Firmware-M ` 338 | -------------------------------------------------------------------------------- /gnss/src/mcc_location_table.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021 Nordic Semiconductor ASA 3 | * 4 | * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause 5 | */ 6 | 7 | #include 8 | 9 | #include "mcc_location_table.h" 10 | 11 | /* Float latitude to integer conversion factor (2^23/90) */ 12 | #define LAT_CONV (8388608.0f / 90.0f) 13 | /* Float longitude to integer conversion factor (2^24/360) */ 14 | #define LON_CONV (16777216.0f / 360.0f) 15 | 16 | static const struct mcc_table mcc_table[] = { 17 | { 100, 104, 104, 0, 43.00f, 41.01f, 289 }, /* Abkhazia */ 18 | { 100, 119, 119, 0, 33.84f, 66.00f, 412 }, /* Afghanistan */ 19 | { 100, 104, 104, 0, 41.14f, 20.05f, 276 }, /* Albania */ 20 | { 100, 125, 125, 0, 28.16f, 2.62f, 603 }, /* Algeria */ 21 | { 100, 107, 107, 0, -14.31f, -170.70f, 544 }, /* American Samoa */ 22 | { 100, 80, 80, 0, 42.54f, 1.56f, 213 }, /* Andorra */ 23 | { 100, 121, 121, 0, -11.21f, 17.88f, 631 }, /* Angola */ 24 | { 100, 86, 86, 0, 18.22f, -63.06f, 365 }, /* Anguilla */ 25 | { 100, 88, 88, 0, 17.28f, -61.79f, 344 }, /* Antigua and Barbuda */ 26 | { 80, 118, 127, 0, -35.38f, -65.18f, 722 }, /* Argentina */ 27 | { 100, 104, 104, 0, 40.29f, 44.93f, 283 }, /* Armenia */ 28 | { 100, 78, 78, 0, 12.52f, -69.96f, 363 }, /* Aruba */ 29 | { 80, 127, 127, 0, -25.73f, 134.49f, 505 }, /* Australia */ 30 | { 100, 109, 109, 0, 47.59f, 14.13f, 232 }, /* Austria */ 31 | { 100, 109, 109, 0, 40.29f, 47.55f, 400 }, /* Azerbaijan */ 32 | { 100, 110, 113, 0, 24.29f, -76.63f, 364 }, /* Bahamas */ 33 | { 100, 84, 84, 0, 26.04f, 50.54f, 426 }, /* Bahrain */ 34 | { 100, 112, 112, 0, 23.87f, 90.24f, 470 }, /* Bangladesh */ 35 | { 100, 81, 81, 0, 13.18f, -59.56f, 342 }, /* Barbados */ 36 | { 100, 112, 112, 0, 53.53f, 28.03f, 257 }, /* Belarus */ 37 | { 100, 103, 103, 0, 50.64f, 4.64f, 206 }, /* Belgium */ 38 | { 100, 102, 102, 0, 17.20f, -88.71f, 702 }, /* Belize */ 39 | { 100, 111, 111, 0, 9.64f, 2.33f, 616 }, /* Benin */ 40 | { 100, 77, 77, 0, 32.31f, -64.75f, 350 }, /* Bermuda */ 41 | { 100, 104, 104, 0, 27.41f, 90.40f, 402 }, /* Bhutan */ 42 | { 100, 121, 121, 0, -16.71f, -64.69f, 736 }, /* Bolivia */ 43 | { 100, 105, 105, 0, 44.17f, 17.77f, 218 }, /* Bosnia and Herzegovina */ 44 | { 100, 118, 118, 0, -22.18f, 23.80f, 652 }, /* Botswana */ 45 | { 66, 127, 127, 0, -10.79f, -53.10f, 724 }, /* Brazil */ 46 | { 100, 101, 101, 0, -7.33f, 72.42f, 995 }, /* British Indian Ocean Territory */ 47 | { 100, 86, 86, 0, 18.42f, -64.59f, 348 }, /* British Virgin Islands */ 48 | { 100, 97, 97, 0, 4.52f, 114.72f, 528 }, /* Brunei */ 49 | { 100, 109, 109, 0, 42.77f, 25.22f, 284 }, /* Bulgaria */ 50 | { 100, 115, 115, 0, 12.27f, -1.75f, 613 }, /* Burkina Faso */ 51 | { 100, 102, 102, 0, -3.36f, 29.88f, 642 }, /* Burundi */ 52 | { 100, 111, 111, 0, 12.72f, 104.91f, 456 }, /* Cambodia */ 53 | { 100, 119, 119, 0, 5.69f, 12.74f, 624 }, /* Cameroon */ 54 | { 33, 127, 127, 0, 61.36f, -98.31f, 302 }, /* Canada */ 55 | { 100, 102, 102, 0, 15.96f, -23.96f, 625 }, /* Cape Verde */ 56 | { 100, 96, 96, 0, 19.43f, -80.91f, 346 }, /* Cayman Islands */ 57 | { 100, 120, 120, 0, 6.57f, 20.47f, 623 }, /* Central African Republic */ 58 | { 100, 122, 122, 0, 15.33f, 18.64f, 622 }, /* Chad */ 59 | { 75, 127, 127, 0, -37.73f, -71.38f, 730 }, /* Chile */ 60 | { 33, 127, 127, 0, 36.56f, 103.82f, 460 }, /* China */ 61 | { 33, 127, 127, 0, 36.56f, 103.82f, 461 }, /* China */ 62 | { 100, 124, 124, 0, 3.91f, -73.08f, 732 }, /* Colombia */ 63 | { 100, 96, 96, 0, -11.88f, 43.68f, 654 }, /* Comoros */ 64 | { 100, 117, 117, 0, -0.84f, 15.22f, 629 }, /* Congo */ 65 | { 100, 120, 120, 0, -21.22f, -159.79f, 548 }, /* Cook Islands */ 66 | { 100, 112, 112, 0, 9.98f, -84.19f, 712 }, /* Costa Rica */ 67 | { 100, 110, 110, 0, 45.08f, 16.40f, 219 }, /* Croatia */ 68 | { 100, 116, 116, 0, 21.62f, -79.02f, 368 }, /* Cuba */ 69 | { 100, 87, 87, 0, 12.20f, -68.97f, 362 }, /* Curacao */ 70 | { 100, 96, 96, 0, 34.92f, 33.01f, 280 }, /* Cyprus */ 71 | { 100, 108, 108, 0, 49.73f, 15.31f, 230 }, /* Czech Republic */ 72 | { 100, 125, 125, 0, -2.88f, 23.64f, 630 }, /* Democratic Republic of the Congo */ 73 | { 100, 108, 108, 0, 55.98f, 10.03f, 238 }, /* Denmark */ 74 | { 100, 100, 100, 0, 11.75f, 42.56f, 638 }, /* Djibouti */ 75 | { 100, 84, 84, 0, 15.44f, -61.36f, 366 }, /* Dominica */ 76 | { 100, 106, 106, 0, 18.89f, -70.51f, 370 }, /* Dominican Republic */ 77 | { 100, 104, 104, 0, -8.79f, 126.14f, 514 }, /* East Timor */ 78 | { 100, 121, 121, 0, -1.42f, -78.75f, 740 }, /* Ecuador */ 79 | { 100, 119, 119, 0, 26.50f, 29.86f, 602 }, /* Egypt */ 80 | { 100, 101, 101, 0, 13.74f, -88.87f, 706 }, /* El Salvador */ 81 | { 100, 112, 112, 0, 1.62f, 10.32f, 627 }, /* Equatorial Guinea */ 82 | { 100, 114, 114, 0, 15.36f, 38.85f, 657 }, /* Eritrea */ 83 | { 100, 105, 105, 0, 58.67f, 25.54f, 248 }, /* Estonia */ 84 | { 100, 122, 122, 0, 8.62f, 39.60f, 636 }, /* Ethiopia */ 85 | { 100, 101, 101, 0, -51.74f, -59.35f, 750 }, /* Falkland Islands */ 86 | { 100, 93, 93, 0, 62.05f, -6.88f, 288 }, /* Faroe Islands */ 87 | { 100, 117, 117, 0, -17.43f, 165.45f, 542 }, /* Fiji */ 88 | { 100, 116, 116, 0, 64.50f, 26.27f, 244 }, /* Finland */ 89 | { 90, 117, 117, 0, 42.17f, -2.76f, 208 }, /* France */ 90 | { 100, 104, 104, 0, 3.93f, -53.09f, 742 }, /* French Guiana */ 91 | { 50, 127, 127, 0, -21.13f, 55.53f, 647 }, /* French Indian Ocean Territories */ 92 | { 100, 125, 125, 0, -17.69f, -149.37f, 547 }, /* French Polynesia */ 93 | { 100, 113, 113, 0, -0.59f, 11.79f, 628 }, /* Gabon */ 94 | { 100, 103, 103, 0, 13.45f, -15.40f, 607 }, /* Gambia */ 95 | { 100, 109, 109, 0, 42.17f, 43.51f, 282 }, /* Georgia */ 96 | { 100, 115, 115, 0, 51.11f, 10.39f, 262 }, /* Germany */ 97 | { 100, 113, 113, 0, 7.95f, -1.22f, 620 }, /* Ghana */ 98 | { 100, 59, 59, 0, 36.14f, -5.35f, 266 }, /* Gibraltar */ 99 | { 100, 115, 115, 0, 39.07f, 22.96f, 202 }, /* Greece */ 100 | { 100, 126, 126, 0, 74.71f, -41.34f, 290 }, /* Greenland */ 101 | { 100, 86, 86, 0, 12.12f, -61.68f, 352 }, /* Grenada */ 102 | { 100, 90, 90, 0, 16.17f, -61.41f, 340 }, /* Guadeloupe */ 103 | { 100, 109, 109, 0, 15.69f, -90.36f, 704 }, /* Guatemala */ 104 | { 100, 114, 114, 0, 10.44f, -10.94f, 611 }, /* Guinea */ 105 | { 100, 104, 104, 0, 12.05f, -14.95f, 632 }, /* Guinea-Bissau */ 106 | { 100, 114, 114, 0, 4.79f, -58.98f, 738 }, /* Guyana */ 107 | { 100, 104, 104, 0, 18.94f, -72.69f, 372 }, /* Haiti */ 108 | { 100, 112, 112, 0, 14.83f, -86.62f, 708 }, /* Honduras */ 109 | { 100, 87, 87, 0, 22.40f, 114.11f, 454 }, /* Hong Kong */ 110 | { 100, 109, 109, 0, 47.16f, 19.40f, 216 }, /* Hungary */ 111 | { 100, 109, 109, 0, 65.00f, -18.57f, 274 }, /* Iceland */ 112 | { 100, 125, 125, 0, 22.89f, 79.61f, 404 }, /* India */ 113 | { 100, 125, 125, 0, 22.89f, 79.61f, 405 }, /* India */ 114 | { 100, 125, 125, 0, 22.89f, 79.61f, 406 }, /* India */ 115 | { 66, 121, 127, 0, -2.22f, 117.24f, 510 }, /* Indonesia */ 116 | { 100, 123, 123, 0, 32.58f, 54.27f, 432 }, /* Iran */ 117 | { 100, 117, 117, 0, 33.04f, 43.74f, 418 }, /* Iraq */ 118 | { 100, 107, 107, 0, 53.18f, -8.14f, 272 }, /* Ireland */ 119 | { 100, 106, 106, 0, 31.46f, 35.00f, 425 }, /* Israel */ 120 | { 100, 119, 119, 0, 42.80f, 12.07f, 222 }, /* Italy */ 121 | { 100, 114, 114, 0, 7.55f, -5.55f, 612 }, /* Ivory Coast */ 122 | { 100, 99, 99, 0, 18.16f, -77.31f, 338 }, /* Jamaica */ 123 | { 90, 127, 127, 0, 37.59f, 138.03f, 440 }, /* Japan */ 124 | { 90, 127, 127, 0, 37.59f, 138.03f, 441 }, /* Japan */ 125 | { 100, 109, 109, 0, 31.25f, 36.77f, 416 }, /* Jordan */ 126 | { 100, 127, 127, 0, 48.16f, 67.29f, 401 }, /* Kazakhstan */ 127 | { 100, 118, 118, 0, 0.60f, 37.80f, 639 }, /* Kenya */ 128 | { 95, 127, 127, 0, 1.87f, -157.36f, 545 }, /* Kiribati */ 129 | { 100, 112, 112, 0, 40.15f, 127.19f, 467 }, /* North Korea */ 130 | { 100, 113, 113, 0, 36.39f, 127.84f, 450 }, /* South Korea */ 131 | { 100, 98, 98, 0, 42.57f, 20.87f, 221 }, /* Kosovo */ 132 | { 100, 100, 100, 0, 29.33f, 47.59f, 419 }, /* Kuwait */ 133 | { 100, 114, 114, 0, 41.46f, 74.54f, 437 }, /* Kyrgyzstan */ 134 | { 100, 116, 116, 0, 18.21f, 103.89f, 457 }, /* Laos */ 135 | { 100, 107, 107, 0, 56.85f, 24.91f, 247 }, /* Latvia */ 136 | { 100, 99, 99, 0, 33.92f, 35.88f, 415 }, /* Lebanon */ 137 | { 100, 102, 102, 0, -29.58f, 28.23f, 651 }, /* Lesotho */ 138 | { 100, 110, 110, 0, 6.45f, -9.32f, 618 }, /* Liberia */ 139 | { 100, 122, 122, 0, 27.03f, 18.01f, 606 }, /* Libya */ 140 | { 100, 76, 76, 0, 47.14f, 9.54f, 295 }, /* Liechtenstein */ 141 | { 100, 106, 106, 0, 55.33f, 23.89f, 246 }, /* Lithuania */ 142 | { 100, 90, 90, 0, 49.77f, 6.07f, 270 }, /* Luxembourg */ 143 | { 100, 70, 70, 0, 22.22f, 113.51f, 455 }, /* Macau */ 144 | { 100, 100, 100, 0, 41.60f, 21.68f, 294 }, /* Macedonia */ 145 | { 100, 120, 120, 0, -19.37f, 46.70f, 646 }, /* Madagascar */ 146 | { 100, 113, 113, 0, -13.22f, 34.29f, 650 }, /* Malawi */ 147 | { 100, 123, 123, 0, 3.79f, 109.70f, 502 }, /* Malaysia */ 148 | { 100, 113, 113, 0, 3.73f, 73.46f, 472 }, /* Maldives */ 149 | { 100, 123, 123, 0, 17.35f, -3.54f, 610 }, /* Mali */ 150 | { 100, 82, 82, 0, 35.92f, 14.41f, 278 }, /* Malta */ 151 | { 100, 117, 117, 0, 7.00f, 170.34f, 551 }, /* Marshall Islands */ 152 | { 100, 121, 121, 0, 20.26f, -10.35f, 609 }, /* Mauritania */ 153 | { 100, 117, 117, 0, -20.28f, 57.57f, 617 }, /* Mauritius */ 154 | { 95, 127, 127, 0, 23.95f, -102.52f, 334 }, /* Mexico */ 155 | { 40, 127, 127, 0, 7.45f, 153.24f, 550 }, /* Micronesia */ 156 | { 100, 105, 105, 0, 47.19f, 28.46f, 259 }, /* Moldova */ 157 | { 100, 65, 65, 0, 43.75f, 7.41f, 212 }, /* Monaco */ 158 | { 100, 124, 124, 0, 46.83f, 103.05f, 428 }, /* Mongolia */ 159 | { 100, 99, 99, 0, 42.79f, 19.24f, 297 }, /* Montenegro */ 160 | { 100, 73, 73, 0, 16.74f, -62.19f, 354 }, /* Montserrat */ 161 | { 100, 123, 123, 0, 29.84f, -8.46f, 604 }, /* Morocco */ 162 | { 100, 122, 122, 0, -17.27f, 35.53f, 643 }, /* Mozambique */ 163 | { 100, 123, 123, 0, 21.19f, 96.49f, 414 }, /* Myanmar */ 164 | { 100, 67, 67, 0, -0.52f, 166.93f, 536 }, /* Nauru */ 165 | { 100, 113, 113, 0, 28.25f, 83.92f, 429 }, /* Nepal */ 166 | { 90, 103, 103, 0, 52.10f, 5.28f, 204 }, /* Netherlands */ 167 | { 100, 113, 113, 0, -21.30f, 165.68f, 546 }, /* New Caledonia */ 168 | { 70, 120, 127, 0, -41.81f, 171.48f, 530 }, /* New Zealand */ 169 | { 100, 111, 111, 0, 12.85f, -85.03f, 710 }, /* Nicaragua */ 170 | { 100, 122, 122, 0, 17.42f, 9.39f, 614 }, /* Niger */ 171 | { 100, 120, 120, 0, 9.59f, 8.09f, 621 }, /* Nigeria */ 172 | { 100, 77, 77, 0, -19.05f, -169.87f, 555 }, /* Niue */ 173 | { 85, 127, 117, 0, 68.75f, 15.35f, 242 }, /* Norway */ 174 | { 100, 117, 117, 0, 20.61f, 56.09f, 422 }, /* Oman */ 175 | { 100, 122, 122, 0, 29.95f, 69.34f, 410 }, /* Pakistan */ 176 | { 100, 110, 110, 0, 7.29f, 134.41f, 552 }, /* Palau */ 177 | { 100, 110, 110, 0, 8.52f, -80.12f, 714 }, /* Panama */ 178 | { 100, 121, 121, 0, -6.46f, 145.21f, 537 }, /* Papua New Guinea */ 179 | { 100, 116, 116, 0, -23.23f, -58.40f, 744 }, /* Paraguay */ 180 | { 100, 124, 124, 0, -9.15f, -74.38f, 716 }, /* Peru */ 181 | { 100, 122, 122, 0, 11.78f, 122.88f, 515 }, /* Philippines */ 182 | { 100, 113, 113, 0, 52.13f, 19.39f, 260 }, /* Poland */ 183 | { 100, 124, 124, 0, 39.60f, -8.50f, 268 }, /* Portugal */ 184 | { 100, 101, 101, 0, 18.23f, -66.47f, 330 }, /* Puerto Rico */ 185 | { 100, 97, 97, 0, 25.31f, 51.18f, 427 }, /* Qatar */ 186 | { 100, 113, 113, 0, 45.85f, 24.97f, 226 }, /* Romania */ 187 | { 20, 127, 127, 90, 61.98f, 96.69f, 250 }, /* Russian Federation */ 188 | { 100, 101, 101, 0, -1.99f, 29.92f, 635 }, /* Rwanda */ 189 | { 100, 72, 72, 0, -12.40f, -9.55f, 658 }, /* Saint Helena */ 190 | { 100, 83, 83, 0, 17.26f, -62.69f, 356 }, /* Saint Kitts and Nevis */ 191 | { 100, 83, 83, 0, 13.89f, -60.97f, 358 }, /* Saint Lucia */ 192 | { 100, 82, 82, 0, 46.92f, -56.30f, 308 }, /* Saint Pierre and Miquelon */ 193 | { 100, 89, 89, 0, 13.22f, -61.20f, 360 }, /* Saint Vincent and the Grenadines */ 194 | { 100, 95, 95, 0, -13.75f, -172.16f, 549 }, /* Samoa */ 195 | { 100, 70, 70, 0, 43.94f, 12.46f, 292 }, /* San Marino */ 196 | { 100, 98, 98, 0, 0.44f, 6.72f, 626 }, /* Sao Tome and Principe */ 197 | { 100, 125, 125, 0, 24.12f, 44.54f, 420 }, /* Saudi Arabia */ 198 | { 100, 112, 112, 0, 14.37f, -14.47f, 608 }, /* Senegal */ 199 | { 100, 107, 107, 0, 44.22f, 20.79f, 220 }, /* Serbia */ 200 | { 100, 117, 117, 0, -4.66f, 55.48f, 633 }, /* Seychelles */ 201 | { 100, 106, 106, 0, 8.56f, -11.79f, 619 }, /* Sierra Leone */ 202 | { 100, 82, 82, 0, 1.36f, 103.82f, 525 }, /* Singapore */ 203 | { 100, 106, 106, 0, 48.71f, 19.48f, 231 }, /* Slovakia */ 204 | { 100, 101, 101, 0, 46.12f, 14.80f, 293 }, /* Slovenia */ 205 | { 100, 119, 119, 0, -8.92f, 159.63f, 540 }, /* Solomon Islands */ 206 | { 100, 121, 121, 0, 4.75f, 45.71f, 637 }, /* Somalia */ 207 | { 100, 127, 127, 0, -29.00f, 25.08f, 655 }, /* South Africa */ 208 | { 100, 119, 119, 0, 7.31f, 30.25f, 659 }, /* South Sudan */ 209 | { 100, 125, 125, 0, 40.24f, -3.65f, 214 }, /* Spain */ 210 | { 100, 107, 107, 0, 7.61f, 80.70f, 413 }, /* Sri Lanka */ 211 | { 100, 123, 123, 0, 15.99f, 29.94f, 634 }, /* Sudan */ 212 | { 100, 110, 110, 0, 4.13f, -55.91f, 746 }, /* Suriname */ 213 | { 100, 96, 96, 0, -26.56f, 31.48f, 653 }, /* Swaziland */ 214 | { 100, 119, 119, 0, 62.78f, 16.75f, 240 }, /* Sweden */ 215 | { 100, 105, 105, 0, 46.80f, 8.21f, 228 }, /* Switzerland */ 216 | { 100, 112, 112, 0, 35.03f, 38.51f, 417 }, /* Syria */ 217 | { 100, 107, 107, 0, 23.75f, 120.95f, 466 }, /* Taiwan */ 218 | { 100, 112, 112, 0, 38.53f, 71.01f, 436 }, /* Tajikistan */ 219 | { 100, 118, 118, 0, -6.28f, 34.81f, 640 }, /* Tanzania */ 220 | { 100, 121, 121, 0, 15.12f, 101.00f, 520 }, /* Thailand */ 221 | { 100, 109, 109, 0, 8.53f, 0.96f, 615 }, /* Togo */ 222 | { 100, 96, 96, 0, -9.17f, -171.82f, 554 }, /* Tokelau */ 223 | { 100, 112, 112, 0, -20.43f, -174.81f, 539 }, /* Tonga */ 224 | { 100, 98, 98, 0, 10.46f, -61.27f, 374 }, /* Trinidad and Tobago */ 225 | { 100, 113, 113, 0, 34.12f, 9.55f, 605 }, /* Tunisia */ 226 | { 100, 120, 120, 0, 39.06f, 35.17f, 286 }, /* Turkey */ 227 | { 100, 118, 118, 0, 39.12f, 59.37f, 438 }, /* Turkmenistan */ 228 | { 100, 95, 95, 0, 21.83f, -71.97f, 376 }, /* Turks and Caicos Islands */ 229 | { 100, 108, 108, 0, -7.48f, 178.68f, 553 }, /* Tuvalu */ 230 | { 100, 113, 113, 0, 1.27f, 32.37f, 641 }, /* Uganda */ 231 | { 100, 119, 119, 0, 49.00f, 31.38f, 255 }, /* Ukraine */ 232 | { 100, 109, 109, 0, 24.35f, 53.94f, 424 }, /* United Arab Emirates */ 233 | { 100, 85, 85, 0, 24.47f, 54.37f, 430 }, /* United Arab Emirates (Abu Dhabi) */ 234 | { 100, 86, 86, 0, 25.07f, 55.17f, 431 }, /* United Arab Emirates (Dubai) */ 235 | { 100, 119, 119, 0, 54.12f, -2.87f, 234 }, /* United Kingdom */ 236 | { 100, 119, 119, 0, 54.12f, -2.87f, 235 }, /* United Kingdom */ 237 | { 35, 127, 127, 0, 45.68f, -112.46f, 310 }, /* United States of America */ 238 | { 35, 127, 127, 0, 45.68f, -112.46f, 311 }, /* United States of America */ 239 | { 35, 127, 127, 0, 45.68f, -112.46f, 312 }, /* United States of America */ 240 | { 35, 127, 127, 0, 45.68f, -112.46f, 313 }, /* United States of America */ 241 | { 35, 127, 127, 0, 45.68f, -112.46f, 314 }, /* United States of America */ 242 | { 35, 127, 127, 0, 45.68f, -112.46f, 315 }, /* United States of America */ 243 | { 35, 127, 127, 0, 45.68f, -112.46f, 316 }, /* United States of America */ 244 | { 100, 89, 89, 0, 17.96f, -64.80f, 332 }, /* United States Virgin Islands */ 245 | { 100, 111, 111, 0, -32.80f, -56.02f, 748 }, /* Uruguay */ 246 | { 100, 120, 120, 0, 41.76f, 63.14f, 434 }, /* Uzbekistan */ 247 | { 100, 113, 113, 0, -16.23f, 167.69f, 541 }, /* Vanuatu */ 248 | { 100, 122, 122, 0, 7.12f, -66.18f, 734 }, /* Venezuela */ 249 | { 100, 120, 120, 0, 16.65f, 106.30f, 452 }, /* Vietnam */ 250 | { 100, 100, 100, 0, -13.89f, -177.35f, 543 }, /* Wallis and Futuna */ 251 | { 100, 118, 118, 0, 15.91f, 47.59f, 421 }, /* Yemen */ 252 | { 100, 119, 119, 0, -13.46f, 27.77f, 645 }, /* Zambia */ 253 | { 100, 115, 115, 0, -19.00f, 29.85f, 648 }, /* Zimbabwe */ 254 | }; 255 | 256 | const struct mcc_table *mcc_lookup(uint16_t mcc) 257 | { 258 | for (int i = 0; i < ARRAY_SIZE(mcc_table); i++) { 259 | if (mcc_table[i].mcc == mcc) { 260 | return &mcc_table[i]; 261 | } 262 | } 263 | 264 | return NULL; 265 | } 266 | 267 | int32_t lat_convert(float lat) 268 | { 269 | return (int32_t)(lat * LAT_CONV); 270 | } 271 | 272 | int32_t lon_convert(float lon) 273 | { 274 | return (int32_t)(lon * LON_CONV); 275 | } 276 | -------------------------------------------------------------------------------- /projects/geofencing/src/mcc_location_table.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021 Nordic Semiconductor ASA 3 | * 4 | * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause 5 | */ 6 | 7 | #include 8 | 9 | #include "mcc_location_table.h" 10 | 11 | /* Float latitude to integer conversion factor (2^23/90) */ 12 | #define LAT_CONV (8388608.0f / 90.0f) 13 | /* Float longitude to integer conversion factor (2^24/360) */ 14 | #define LON_CONV (16777216.0f / 360.0f) 15 | 16 | static const struct mcc_table mcc_table[] = { 17 | { 100, 104, 104, 0, 43.00f, 41.01f, 289 }, /* Abkhazia */ 18 | { 100, 119, 119, 0, 33.84f, 66.00f, 412 }, /* Afghanistan */ 19 | { 100, 104, 104, 0, 41.14f, 20.05f, 276 }, /* Albania */ 20 | { 100, 125, 125, 0, 28.16f, 2.62f, 603 }, /* Algeria */ 21 | { 100, 107, 107, 0, -14.31f, -170.70f, 544 }, /* American Samoa */ 22 | { 100, 80, 80, 0, 42.54f, 1.56f, 213 }, /* Andorra */ 23 | { 100, 121, 121, 0, -11.21f, 17.88f, 631 }, /* Angola */ 24 | { 100, 86, 86, 0, 18.22f, -63.06f, 365 }, /* Anguilla */ 25 | { 100, 88, 88, 0, 17.28f, -61.79f, 344 }, /* Antigua and Barbuda */ 26 | { 80, 118, 127, 0, -35.38f, -65.18f, 722 }, /* Argentina */ 27 | { 100, 104, 104, 0, 40.29f, 44.93f, 283 }, /* Armenia */ 28 | { 100, 78, 78, 0, 12.52f, -69.96f, 363 }, /* Aruba */ 29 | { 80, 127, 127, 0, -25.73f, 134.49f, 505 }, /* Australia */ 30 | { 100, 109, 109, 0, 47.59f, 14.13f, 232 }, /* Austria */ 31 | { 100, 109, 109, 0, 40.29f, 47.55f, 400 }, /* Azerbaijan */ 32 | { 100, 110, 113, 0, 24.29f, -76.63f, 364 }, /* Bahamas */ 33 | { 100, 84, 84, 0, 26.04f, 50.54f, 426 }, /* Bahrain */ 34 | { 100, 112, 112, 0, 23.87f, 90.24f, 470 }, /* Bangladesh */ 35 | { 100, 81, 81, 0, 13.18f, -59.56f, 342 }, /* Barbados */ 36 | { 100, 112, 112, 0, 53.53f, 28.03f, 257 }, /* Belarus */ 37 | { 100, 103, 103, 0, 50.64f, 4.64f, 206 }, /* Belgium */ 38 | { 100, 102, 102, 0, 17.20f, -88.71f, 702 }, /* Belize */ 39 | { 100, 111, 111, 0, 9.64f, 2.33f, 616 }, /* Benin */ 40 | { 100, 77, 77, 0, 32.31f, -64.75f, 350 }, /* Bermuda */ 41 | { 100, 104, 104, 0, 27.41f, 90.40f, 402 }, /* Bhutan */ 42 | { 100, 121, 121, 0, -16.71f, -64.69f, 736 }, /* Bolivia */ 43 | { 100, 105, 105, 0, 44.17f, 17.77f, 218 }, /* Bosnia and Herzegovina */ 44 | { 100, 118, 118, 0, -22.18f, 23.80f, 652 }, /* Botswana */ 45 | { 66, 127, 127, 0, -10.79f, -53.10f, 724 }, /* Brazil */ 46 | { 100, 101, 101, 0, -7.33f, 72.42f, 995 }, /* British Indian Ocean Territory */ 47 | { 100, 86, 86, 0, 18.42f, -64.59f, 348 }, /* British Virgin Islands */ 48 | { 100, 97, 97, 0, 4.52f, 114.72f, 528 }, /* Brunei */ 49 | { 100, 109, 109, 0, 42.77f, 25.22f, 284 }, /* Bulgaria */ 50 | { 100, 115, 115, 0, 12.27f, -1.75f, 613 }, /* Burkina Faso */ 51 | { 100, 102, 102, 0, -3.36f, 29.88f, 642 }, /* Burundi */ 52 | { 100, 111, 111, 0, 12.72f, 104.91f, 456 }, /* Cambodia */ 53 | { 100, 119, 119, 0, 5.69f, 12.74f, 624 }, /* Cameroon */ 54 | { 33, 127, 127, 0, 61.36f, -98.31f, 302 }, /* Canada */ 55 | { 100, 102, 102, 0, 15.96f, -23.96f, 625 }, /* Cape Verde */ 56 | { 100, 96, 96, 0, 19.43f, -80.91f, 346 }, /* Cayman Islands */ 57 | { 100, 120, 120, 0, 6.57f, 20.47f, 623 }, /* Central African Republic */ 58 | { 100, 122, 122, 0, 15.33f, 18.64f, 622 }, /* Chad */ 59 | { 75, 127, 127, 0, -37.73f, -71.38f, 730 }, /* Chile */ 60 | { 33, 127, 127, 0, 36.56f, 103.82f, 460 }, /* China */ 61 | { 33, 127, 127, 0, 36.56f, 103.82f, 461 }, /* China */ 62 | { 100, 124, 124, 0, 3.91f, -73.08f, 732 }, /* Colombia */ 63 | { 100, 96, 96, 0, -11.88f, 43.68f, 654 }, /* Comoros */ 64 | { 100, 117, 117, 0, -0.84f, 15.22f, 629 }, /* Congo */ 65 | { 100, 120, 120, 0, -21.22f, -159.79f, 548 }, /* Cook Islands */ 66 | { 100, 112, 112, 0, 9.98f, -84.19f, 712 }, /* Costa Rica */ 67 | { 100, 110, 110, 0, 45.08f, 16.40f, 219 }, /* Croatia */ 68 | { 100, 116, 116, 0, 21.62f, -79.02f, 368 }, /* Cuba */ 69 | { 100, 87, 87, 0, 12.20f, -68.97f, 362 }, /* Curacao */ 70 | { 100, 96, 96, 0, 34.92f, 33.01f, 280 }, /* Cyprus */ 71 | { 100, 108, 108, 0, 49.73f, 15.31f, 230 }, /* Czech Republic */ 72 | { 100, 125, 125, 0, -2.88f, 23.64f, 630 }, /* Democratic Republic of the Congo */ 73 | { 100, 108, 108, 0, 55.98f, 10.03f, 238 }, /* Denmark */ 74 | { 100, 100, 100, 0, 11.75f, 42.56f, 638 }, /* Djibouti */ 75 | { 100, 84, 84, 0, 15.44f, -61.36f, 366 }, /* Dominica */ 76 | { 100, 106, 106, 0, 18.89f, -70.51f, 370 }, /* Dominican Republic */ 77 | { 100, 104, 104, 0, -8.79f, 126.14f, 514 }, /* East Timor */ 78 | { 100, 121, 121, 0, -1.42f, -78.75f, 740 }, /* Ecuador */ 79 | { 100, 119, 119, 0, 26.50f, 29.86f, 602 }, /* Egypt */ 80 | { 100, 101, 101, 0, 13.74f, -88.87f, 706 }, /* El Salvador */ 81 | { 100, 112, 112, 0, 1.62f, 10.32f, 627 }, /* Equatorial Guinea */ 82 | { 100, 114, 114, 0, 15.36f, 38.85f, 657 }, /* Eritrea */ 83 | { 100, 105, 105, 0, 58.67f, 25.54f, 248 }, /* Estonia */ 84 | { 100, 122, 122, 0, 8.62f, 39.60f, 636 }, /* Ethiopia */ 85 | { 100, 101, 101, 0, -51.74f, -59.35f, 750 }, /* Falkland Islands */ 86 | { 100, 93, 93, 0, 62.05f, -6.88f, 288 }, /* Faroe Islands */ 87 | { 100, 117, 117, 0, -17.43f, 165.45f, 542 }, /* Fiji */ 88 | { 100, 116, 116, 0, 64.50f, 26.27f, 244 }, /* Finland */ 89 | { 90, 117, 117, 0, 42.17f, -2.76f, 208 }, /* France */ 90 | { 100, 104, 104, 0, 3.93f, -53.09f, 742 }, /* French Guiana */ 91 | { 50, 127, 127, 0, -21.13f, 55.53f, 647 }, /* French Indian Ocean Territories */ 92 | { 100, 125, 125, 0, -17.69f, -149.37f, 547 }, /* French Polynesia */ 93 | { 100, 113, 113, 0, -0.59f, 11.79f, 628 }, /* Gabon */ 94 | { 100, 103, 103, 0, 13.45f, -15.40f, 607 }, /* Gambia */ 95 | { 100, 109, 109, 0, 42.17f, 43.51f, 282 }, /* Georgia */ 96 | { 100, 115, 115, 0, 51.11f, 10.39f, 262 }, /* Germany */ 97 | { 100, 113, 113, 0, 7.95f, -1.22f, 620 }, /* Ghana */ 98 | { 100, 59, 59, 0, 36.14f, -5.35f, 266 }, /* Gibraltar */ 99 | { 100, 115, 115, 0, 39.07f, 22.96f, 202 }, /* Greece */ 100 | { 100, 126, 126, 0, 74.71f, -41.34f, 290 }, /* Greenland */ 101 | { 100, 86, 86, 0, 12.12f, -61.68f, 352 }, /* Grenada */ 102 | { 100, 90, 90, 0, 16.17f, -61.41f, 340 }, /* Guadeloupe */ 103 | { 100, 109, 109, 0, 15.69f, -90.36f, 704 }, /* Guatemala */ 104 | { 100, 114, 114, 0, 10.44f, -10.94f, 611 }, /* Guinea */ 105 | { 100, 104, 104, 0, 12.05f, -14.95f, 632 }, /* Guinea-Bissau */ 106 | { 100, 114, 114, 0, 4.79f, -58.98f, 738 }, /* Guyana */ 107 | { 100, 104, 104, 0, 18.94f, -72.69f, 372 }, /* Haiti */ 108 | { 100, 112, 112, 0, 14.83f, -86.62f, 708 }, /* Honduras */ 109 | { 100, 87, 87, 0, 22.40f, 114.11f, 454 }, /* Hong Kong */ 110 | { 100, 109, 109, 0, 47.16f, 19.40f, 216 }, /* Hungary */ 111 | { 100, 109, 109, 0, 65.00f, -18.57f, 274 }, /* Iceland */ 112 | { 100, 125, 125, 0, 22.89f, 79.61f, 404 }, /* India */ 113 | { 100, 125, 125, 0, 22.89f, 79.61f, 405 }, /* India */ 114 | { 100, 125, 125, 0, 22.89f, 79.61f, 406 }, /* India */ 115 | { 66, 121, 127, 0, -2.22f, 117.24f, 510 }, /* Indonesia */ 116 | { 100, 123, 123, 0, 32.58f, 54.27f, 432 }, /* Iran */ 117 | { 100, 117, 117, 0, 33.04f, 43.74f, 418 }, /* Iraq */ 118 | { 100, 107, 107, 0, 53.18f, -8.14f, 272 }, /* Ireland */ 119 | { 100, 106, 106, 0, 31.46f, 35.00f, 425 }, /* Israel */ 120 | { 100, 119, 119, 0, 42.80f, 12.07f, 222 }, /* Italy */ 121 | { 100, 114, 114, 0, 7.55f, -5.55f, 612 }, /* Ivory Coast */ 122 | { 100, 99, 99, 0, 18.16f, -77.31f, 338 }, /* Jamaica */ 123 | { 90, 127, 127, 0, 37.59f, 138.03f, 440 }, /* Japan */ 124 | { 90, 127, 127, 0, 37.59f, 138.03f, 441 }, /* Japan */ 125 | { 100, 109, 109, 0, 31.25f, 36.77f, 416 }, /* Jordan */ 126 | { 100, 127, 127, 0, 48.16f, 67.29f, 401 }, /* Kazakhstan */ 127 | { 100, 118, 118, 0, 0.60f, 37.80f, 639 }, /* Kenya */ 128 | { 95, 127, 127, 0, 1.87f, -157.36f, 545 }, /* Kiribati */ 129 | { 100, 112, 112, 0, 40.15f, 127.19f, 467 }, /* North Korea */ 130 | { 100, 113, 113, 0, 36.39f, 127.84f, 450 }, /* South Korea */ 131 | { 100, 98, 98, 0, 42.57f, 20.87f, 221 }, /* Kosovo */ 132 | { 100, 100, 100, 0, 29.33f, 47.59f, 419 }, /* Kuwait */ 133 | { 100, 114, 114, 0, 41.46f, 74.54f, 437 }, /* Kyrgyzstan */ 134 | { 100, 116, 116, 0, 18.21f, 103.89f, 457 }, /* Laos */ 135 | { 100, 107, 107, 0, 56.85f, 24.91f, 247 }, /* Latvia */ 136 | { 100, 99, 99, 0, 33.92f, 35.88f, 415 }, /* Lebanon */ 137 | { 100, 102, 102, 0, -29.58f, 28.23f, 651 }, /* Lesotho */ 138 | { 100, 110, 110, 0, 6.45f, -9.32f, 618 }, /* Liberia */ 139 | { 100, 122, 122, 0, 27.03f, 18.01f, 606 }, /* Libya */ 140 | { 100, 76, 76, 0, 47.14f, 9.54f, 295 }, /* Liechtenstein */ 141 | { 100, 106, 106, 0, 55.33f, 23.89f, 246 }, /* Lithuania */ 142 | { 100, 90, 90, 0, 49.77f, 6.07f, 270 }, /* Luxembourg */ 143 | { 100, 70, 70, 0, 22.22f, 113.51f, 455 }, /* Macau */ 144 | { 100, 100, 100, 0, 41.60f, 21.68f, 294 }, /* Macedonia */ 145 | { 100, 120, 120, 0, -19.37f, 46.70f, 646 }, /* Madagascar */ 146 | { 100, 113, 113, 0, -13.22f, 34.29f, 650 }, /* Malawi */ 147 | { 100, 123, 123, 0, 3.79f, 109.70f, 502 }, /* Malaysia */ 148 | { 100, 113, 113, 0, 3.73f, 73.46f, 472 }, /* Maldives */ 149 | { 100, 123, 123, 0, 17.35f, -3.54f, 610 }, /* Mali */ 150 | { 100, 82, 82, 0, 35.92f, 14.41f, 278 }, /* Malta */ 151 | { 100, 117, 117, 0, 7.00f, 170.34f, 551 }, /* Marshall Islands */ 152 | { 100, 121, 121, 0, 20.26f, -10.35f, 609 }, /* Mauritania */ 153 | { 100, 117, 117, 0, -20.28f, 57.57f, 617 }, /* Mauritius */ 154 | { 95, 127, 127, 0, 23.95f, -102.52f, 334 }, /* Mexico */ 155 | { 40, 127, 127, 0, 7.45f, 153.24f, 550 }, /* Micronesia */ 156 | { 100, 105, 105, 0, 47.19f, 28.46f, 259 }, /* Moldova */ 157 | { 100, 65, 65, 0, 43.75f, 7.41f, 212 }, /* Monaco */ 158 | { 100, 124, 124, 0, 46.83f, 103.05f, 428 }, /* Mongolia */ 159 | { 100, 99, 99, 0, 42.79f, 19.24f, 297 }, /* Montenegro */ 160 | { 100, 73, 73, 0, 16.74f, -62.19f, 354 }, /* Montserrat */ 161 | { 100, 123, 123, 0, 29.84f, -8.46f, 604 }, /* Morocco */ 162 | { 100, 122, 122, 0, -17.27f, 35.53f, 643 }, /* Mozambique */ 163 | { 100, 123, 123, 0, 21.19f, 96.49f, 414 }, /* Myanmar */ 164 | { 100, 67, 67, 0, -0.52f, 166.93f, 536 }, /* Nauru */ 165 | { 100, 113, 113, 0, 28.25f, 83.92f, 429 }, /* Nepal */ 166 | { 90, 103, 103, 0, 52.10f, 5.28f, 204 }, /* Netherlands */ 167 | { 100, 113, 113, 0, -21.30f, 165.68f, 546 }, /* New Caledonia */ 168 | { 70, 120, 127, 0, -41.81f, 171.48f, 530 }, /* New Zealand */ 169 | { 100, 111, 111, 0, 12.85f, -85.03f, 710 }, /* Nicaragua */ 170 | { 100, 122, 122, 0, 17.42f, 9.39f, 614 }, /* Niger */ 171 | { 100, 120, 120, 0, 9.59f, 8.09f, 621 }, /* Nigeria */ 172 | { 100, 77, 77, 0, -19.05f, -169.87f, 555 }, /* Niue */ 173 | { 85, 127, 117, 0, 68.75f, 15.35f, 242 }, /* Norway */ 174 | { 100, 117, 117, 0, 20.61f, 56.09f, 422 }, /* Oman */ 175 | { 100, 122, 122, 0, 29.95f, 69.34f, 410 }, /* Pakistan */ 176 | { 100, 110, 110, 0, 7.29f, 134.41f, 552 }, /* Palau */ 177 | { 100, 110, 110, 0, 8.52f, -80.12f, 714 }, /* Panama */ 178 | { 100, 121, 121, 0, -6.46f, 145.21f, 537 }, /* Papua New Guinea */ 179 | { 100, 116, 116, 0, -23.23f, -58.40f, 744 }, /* Paraguay */ 180 | { 100, 124, 124, 0, -9.15f, -74.38f, 716 }, /* Peru */ 181 | { 100, 122, 122, 0, 11.78f, 122.88f, 515 }, /* Philippines */ 182 | { 100, 113, 113, 0, 52.13f, 19.39f, 260 }, /* Poland */ 183 | { 100, 124, 124, 0, 39.60f, -8.50f, 268 }, /* Portugal */ 184 | { 100, 101, 101, 0, 18.23f, -66.47f, 330 }, /* Puerto Rico */ 185 | { 100, 97, 97, 0, 25.31f, 51.18f, 427 }, /* Qatar */ 186 | { 100, 113, 113, 0, 45.85f, 24.97f, 226 }, /* Romania */ 187 | { 20, 127, 127, 90, 61.98f, 96.69f, 250 }, /* Russian Federation */ 188 | { 100, 101, 101, 0, -1.99f, 29.92f, 635 }, /* Rwanda */ 189 | { 100, 72, 72, 0, -12.40f, -9.55f, 658 }, /* Saint Helena */ 190 | { 100, 83, 83, 0, 17.26f, -62.69f, 356 }, /* Saint Kitts and Nevis */ 191 | { 100, 83, 83, 0, 13.89f, -60.97f, 358 }, /* Saint Lucia */ 192 | { 100, 82, 82, 0, 46.92f, -56.30f, 308 }, /* Saint Pierre and Miquelon */ 193 | { 100, 89, 89, 0, 13.22f, -61.20f, 360 }, /* Saint Vincent and the Grenadines */ 194 | { 100, 95, 95, 0, -13.75f, -172.16f, 549 }, /* Samoa */ 195 | { 100, 70, 70, 0, 43.94f, 12.46f, 292 }, /* San Marino */ 196 | { 100, 98, 98, 0, 0.44f, 6.72f, 626 }, /* Sao Tome and Principe */ 197 | { 100, 125, 125, 0, 24.12f, 44.54f, 420 }, /* Saudi Arabia */ 198 | { 100, 112, 112, 0, 14.37f, -14.47f, 608 }, /* Senegal */ 199 | { 100, 107, 107, 0, 44.22f, 20.79f, 220 }, /* Serbia */ 200 | { 100, 117, 117, 0, -4.66f, 55.48f, 633 }, /* Seychelles */ 201 | { 100, 106, 106, 0, 8.56f, -11.79f, 619 }, /* Sierra Leone */ 202 | { 100, 82, 82, 0, 1.36f, 103.82f, 525 }, /* Singapore */ 203 | { 100, 106, 106, 0, 48.71f, 19.48f, 231 }, /* Slovakia */ 204 | { 100, 101, 101, 0, 46.12f, 14.80f, 293 }, /* Slovenia */ 205 | { 100, 119, 119, 0, -8.92f, 159.63f, 540 }, /* Solomon Islands */ 206 | { 100, 121, 121, 0, 4.75f, 45.71f, 637 }, /* Somalia */ 207 | { 100, 127, 127, 0, -29.00f, 25.08f, 655 }, /* South Africa */ 208 | { 100, 119, 119, 0, 7.31f, 30.25f, 659 }, /* South Sudan */ 209 | { 100, 125, 125, 0, 40.24f, -3.65f, 214 }, /* Spain */ 210 | { 100, 107, 107, 0, 7.61f, 80.70f, 413 }, /* Sri Lanka */ 211 | { 100, 123, 123, 0, 15.99f, 29.94f, 634 }, /* Sudan */ 212 | { 100, 110, 110, 0, 4.13f, -55.91f, 746 }, /* Suriname */ 213 | { 100, 96, 96, 0, -26.56f, 31.48f, 653 }, /* Swaziland */ 214 | { 100, 119, 119, 0, 62.78f, 16.75f, 240 }, /* Sweden */ 215 | { 100, 105, 105, 0, 46.80f, 8.21f, 228 }, /* Switzerland */ 216 | { 100, 112, 112, 0, 35.03f, 38.51f, 417 }, /* Syria */ 217 | { 100, 107, 107, 0, 23.75f, 120.95f, 466 }, /* Taiwan */ 218 | { 100, 112, 112, 0, 38.53f, 71.01f, 436 }, /* Tajikistan */ 219 | { 100, 118, 118, 0, -6.28f, 34.81f, 640 }, /* Tanzania */ 220 | { 100, 121, 121, 0, 15.12f, 101.00f, 520 }, /* Thailand */ 221 | { 100, 109, 109, 0, 8.53f, 0.96f, 615 }, /* Togo */ 222 | { 100, 96, 96, 0, -9.17f, -171.82f, 554 }, /* Tokelau */ 223 | { 100, 112, 112, 0, -20.43f, -174.81f, 539 }, /* Tonga */ 224 | { 100, 98, 98, 0, 10.46f, -61.27f, 374 }, /* Trinidad and Tobago */ 225 | { 100, 113, 113, 0, 34.12f, 9.55f, 605 }, /* Tunisia */ 226 | { 100, 120, 120, 0, 39.06f, 35.17f, 286 }, /* Turkey */ 227 | { 100, 118, 118, 0, 39.12f, 59.37f, 438 }, /* Turkmenistan */ 228 | { 100, 95, 95, 0, 21.83f, -71.97f, 376 }, /* Turks and Caicos Islands */ 229 | { 100, 108, 108, 0, -7.48f, 178.68f, 553 }, /* Tuvalu */ 230 | { 100, 113, 113, 0, 1.27f, 32.37f, 641 }, /* Uganda */ 231 | { 100, 119, 119, 0, 49.00f, 31.38f, 255 }, /* Ukraine */ 232 | { 100, 109, 109, 0, 24.35f, 53.94f, 424 }, /* United Arab Emirates */ 233 | { 100, 85, 85, 0, 24.47f, 54.37f, 430 }, /* United Arab Emirates (Abu Dhabi) */ 234 | { 100, 86, 86, 0, 25.07f, 55.17f, 431 }, /* United Arab Emirates (Dubai) */ 235 | { 100, 119, 119, 0, 54.12f, -2.87f, 234 }, /* United Kingdom */ 236 | { 100, 119, 119, 0, 54.12f, -2.87f, 235 }, /* United Kingdom */ 237 | { 35, 127, 127, 0, 45.68f, -112.46f, 310 }, /* United States of America */ 238 | { 35, 127, 127, 0, 45.68f, -112.46f, 311 }, /* United States of America */ 239 | { 35, 127, 127, 0, 45.68f, -112.46f, 312 }, /* United States of America */ 240 | { 35, 127, 127, 0, 45.68f, -112.46f, 313 }, /* United States of America */ 241 | { 35, 127, 127, 0, 45.68f, -112.46f, 314 }, /* United States of America */ 242 | { 35, 127, 127, 0, 45.68f, -112.46f, 315 }, /* United States of America */ 243 | { 35, 127, 127, 0, 45.68f, -112.46f, 316 }, /* United States of America */ 244 | { 100, 89, 89, 0, 17.96f, -64.80f, 332 }, /* United States Virgin Islands */ 245 | { 100, 111, 111, 0, -32.80f, -56.02f, 748 }, /* Uruguay */ 246 | { 100, 120, 120, 0, 41.76f, 63.14f, 434 }, /* Uzbekistan */ 247 | { 100, 113, 113, 0, -16.23f, 167.69f, 541 }, /* Vanuatu */ 248 | { 100, 122, 122, 0, 7.12f, -66.18f, 734 }, /* Venezuela */ 249 | { 100, 120, 120, 0, 16.65f, 106.30f, 452 }, /* Vietnam */ 250 | { 100, 100, 100, 0, -13.89f, -177.35f, 543 }, /* Wallis and Futuna */ 251 | { 100, 118, 118, 0, 15.91f, 47.59f, 421 }, /* Yemen */ 252 | { 100, 119, 119, 0, -13.46f, 27.77f, 645 }, /* Zambia */ 253 | { 100, 115, 115, 0, -19.00f, 29.85f, 648 }, /* Zimbabwe */ 254 | }; 255 | 256 | const struct mcc_table *mcc_lookup(uint16_t mcc) 257 | { 258 | for (int i = 0; i < ARRAY_SIZE(mcc_table); i++) { 259 | if (mcc_table[i].mcc == mcc) { 260 | return &mcc_table[i]; 261 | } 262 | } 263 | 264 | return NULL; 265 | } 266 | 267 | int32_t lat_convert(float lat) 268 | { 269 | return (int32_t)(lat * LAT_CONV); 270 | } 271 | 272 | int32_t lon_convert(float lon) 273 | { 274 | return (int32_t)(lon * LON_CONV); 275 | } 276 | --------------------------------------------------------------------------------