Exercise 1

In this exercise, we will use the GNSS interface in the Modem library to enable the GNSS receiver on the nRF9160 SiP and retrieve the GPS coordinates.

Exercise Steps

1. In the GitHub repository for this course, go to the base code for this exercise, found in lesson6/cellfund_less6_exer1.

2. Enable relevant configurations in prj.conf.

2.1 Enable the modem’s network mode for GPS through CONFIG_LTE_NETWORK_MODE_LTE_M_NBIOT.

2.2 Enable the following two configurations two enable printing floating-point numbers to console.

The GNSS interface is included when enabling the modem library (CONFIG_NRF_MODEM_LIB).

3. In the Kconfig file define the configurations for the fix interval and fix retry period.

3.1 Define the configuration GNSS_PERIODIC_INTERVAL that specifies at which interval the GNSS receiver will initiate searching for a fix.

3.2 Define the configuration GNSS_PERIODIC_TIMEOUT, which sets the maximum time the GNSS receiver is allowed to run while trying to produce a valid PVT estimate.

We are configuring the GNSS in periodic mode, by setting the fix interval to 2 minutes and the fix retry period to 8 minutes.

4. In main.c, include the header file for the GNSS interface.

#include <nrf_modem_gnss.h>

5. Define the PVT data frame variables, of type struct nrf_modem_gnss_pvt_data_frame. This will be used when reading PVT data from GNSS.

static struct nrf_modem_gnss_pvt_data_frame pvt_data;

6. Define the function print_fix_data(), to log fix data in a readable format.

This function will take a parameter of type struct nrf_modem_gnss_pvt_data_frame and print out the members double latitude, double longitude, float altitude and struct nrf_modem_gnss_datetime datetime

static void print_fix_data(struct nrf_modem_gnss_pvt_data_frame *pvt_data)
	LOG_INF("Latitude:       %.06f", pvt_data->latitude);
	LOG_INF("Longitude:      %.06f", pvt_data->longitude);
	LOG_INF("Altitude:       %.01f m", pvt_data->altitude);
	LOG_INF("Time (UTC):     %02u:%02u:%02u.%03u",


When printing floats, the format specifier (%*.*f) determines how many digits to print. The number before the decimal point sets the number of integers and the number after sets the number of decimals.

7. In the event handler gnss_event_handler(), add the events EVT_PVT, EVT_PERIODIC_WAKEUP and EVT_SLEEP_AFTER_FIX.

7.1 On a PVT event, read PVT data from GNSS, check if it’s a valid fix and print fix data to console.

Pass the data frame variable pvt_data, and the data type NRF_MODEM_GNSS_DATA_PVT to nrf_modem_gnss_read() to read the PVT data. Then check if it’s a valid fix by seeing if the NRF_MODEM_GNSS_PVT_FLAG_FIX_VALID flag is set. If so, call print_fix_data() to print the information to console.

	/* STEP 15 - Print satellite information */
	err = nrf_modem_gnss_read(&pvt_data, sizeof(pvt_data), NRF_MODEM_GNSS_DATA_PVT);
	if (err) {
		LOG_ERR("nrf_modem_gnss_read failed, err %d", err);
	if (pvt_data.flags & NRF_MODEM_GNSS_PVT_FLAG_FIX_VALID) {
		/* STEP 12.3 - Print the time to first fix */

Ignore the comments, that’s to help you later.

7.2. Add EVT_PERIODIC_WAKEUP and EVT_SLEEP_AFTER_FIX to the event handler, and log the events to console.

This will tell us when the GNSS is sleeping and when it wakes up to get the next fix.

	LOG_INF("GNSS has woken up");
	LOG_INF("GNSS enters sleep after fix");

8. In main(), make sure to deactivate the LTE modem.

Using lte_lc_func_mode_set(), set the modem to normal mode. Then make sure to deactivate LTE using LTE_LC_FUNC_MODE_DEACTIVATE_LTE, so that the GNSS can operate as intended.

if (lte_lc_func_mode_set(LTE_LC_FUNC_MODE_NORMAL) != 0) {
	LOG_ERR("Failed to activate GNSS functional mode");
	return -1;

if (lte_lc_func_mode_set(LTE_LC_FUNC_MODE_DEACTIVATE_LTE) != 0) {
	LOG_ERR("Failed to activate GNSS functional mode");

In an actual application, you wouldn’t completely deactivate LTE but rather configure PSM or eDRX so that the GNSS and LTE can be used rather often without having to completely activate and deactivate the modes.

We will take a look at how to do this in Exercise 2.

9. Then register the GNSS event handler, using nrf_modem_gnss_event_handler_set().

if (nrf_modem_gnss_event_handler_set(gnss_event_handler) != 0) {
	LOG_ERR("Failed to set GNSS event handler");

10. Using nrf_modem_gnss_fix_interval_set() and nrf_modem_gnss_fix_retry_set(), set the GNSS fix interval and GNSS fix retry period, which we defined in the Kconfig file.

if (nrf_modem_gnss_fix_interval_set(CONFIG_GNSS_PERIODIC_INTERVAL) != 0) {
	LOG_ERR("Failed to set GNSS fix interval");

if (nrf_modem_gnss_fix_retry_set(CONFIG_GNSS_PERIODIC_TIMEOUT) != 0) {
	LOG_ERR("Failed to set GNSS fix retry");

11. When everything is configured, start the GNSS receiver using nrf_modem_gnss_start().

LOG_INF("Starting GNSS");
if (nrf_modem_gnss_start() != 0) {
	LOG_ERR("Failed to start GNSS");

12. Upon the first fix, print the time it took from GNSS start to first fix (Time-To-First-Fix).

12.1 Declare the variable gnss_start_time to store the GNSS start time and a flag first_fix.

static int64_t gnss_start_time;
static bool first_fix = false;

12.2 After starting GNSS, log the system uptime using k_uptime_get().

gnss_start_time = k_uptime_get();

12.3 In gnss_event_handler(), check whether valid fixes are the first using the first_fix flag, and if so, print the time from GNSS start to the current system uptime and set the first_fix flag false.

if (!first_fix) {
	first_fix = true;
	LOG_INF("Time to first fix: %2.1lld s", (k_uptime_get() - gnss_start_time)/1000);

13. Build the exercise and flash it to your board.

14. To test the application, make sure you are outdoors under an open sky to achieve the best conditions.

14.1 Run the application until the LED indicating the first fix lights up. Your console should look something like this.

We can see that the time to first fix took 34 seconds, which is an expected amount of time.

14.2. Now run the appliation for around 10 minutes. The output should look something like this.

From the time stamps, it is clear that after getting the first fix, the GNSS sleeps then wakes up when the GNSS fix interval has passed (120 seconds or 2 minutes) and starts searching for a new fix. Then it sleeps and repeats.

Let’s modify the application to print a bit more debugging information when the GNSS is running.

15. Print the satellites that the GNSS is currently tracking and their signal strength.

The PVT event payload contains the array sv[12] of type struct nrf_modem_gnss_sv, which describes up to 12 of the space vehicles used for the measurements. It has the following signature

We are interested in the carrier-to-noise density ratio for all the valid satellites the GNSS is currently tracking, which is found in the member cn0. This tells us something about the signal strength from that satellite and it has to be above 30 dB/Hz (cn0 = 300) for the GNSS receiver to consider the satellite “healthy” and use it in calculations.

Insert the following code in the PVT event in gnss_event_handler().

int num_satellites = 0;
for (int i = 0; i < 12 ; i++) {
	if (pvt_data.sv[i].signal != 0) {
		LOG_INF("sv: %d, cn0: %d", pvt_data.sv[i].sv, pvt_data.sv[i].cn0);

LOG_INF("Number of satellites: %d", num_satellites);

16. Build the exercise and flash it to your board.

You should have a log output that looks something like this.

Printing the satellite information and signal strength is useful for debugging purposes if your application is not getting a fix.

Because the GNSS is solving for four unknown variables, it needs at least 4 satellites to get a valid fix.

Register an account
Already have an account? Log in
(All fields are required unless specified optional)

Forgot your password?
Enter your email address, and we will send a link to reset your password.