This post is older than 2 years and might not be relevant anymore
More Info: Consider searching for newer posts

nrf9160 GPS hangs when starting after stopping

After successfully running the GPS and acquiring position the application goes into an infinite wait loop when attempting to start it again, but only if either or both of the following are true:

  1. The GNSS is stopped using nrf_setsockopt with the NRF_SO_GNSS_STOP option
  2. The NRF_AF_LOCAL socket is closed

Specifically it hangs at the first attempt to create or use the socket on the second attempt.

If the GNSS is not stopped and the socket is not closed subsequent acquisitions can happen, but this means the GPS is running the whole time. What is the correct approach to achieve low power?

Here is my prj.conf:

# General config
CONFIG_HEAP_MEM_POOL_SIZE=4096
CONFIG_NEWLIB_LIBC=y
CONFIG_NEWLIB_LIBC_FLOAT_PRINTF=y
CONFIG_MAIN_STACK_SIZE=4096
CONFIG_GPIO=y

# LOG stuff
CONFIG_LOG=y

# Networking
CONFIG_NETWORKING=y
CONFIG_NET_SOCKETS_OFFLOAD=y
CONFIG_NET_SOCKETS=y
CONFIG_NET_NATIVE=n
CONFIG_NET_SOCKETS_POSIX_NAMES=y

# BSD library
CONFIG_BSD_LIBRARY=y
CONFIG_BSD_LIBRARY_TRACE_ENABLED=n

And here is example code which shows this issue:

#include <stdio.h>
#include <string.h>
#include <zephyr.h>
#include <nrf_socket.h>

#define AT_XSYSTEMMODE "AT\%XSYSTEMMODE=0,0,1,0"
#define AT_MAGPIO      "AT\%XMAGPIO=1,0,0,1,1,1574,1577"
#define AT_COEX0       "AT\%XCOEX0=1,1,1570,1580"
#define AT_CFUN        "AT+CFUN=1"

static const char     at_commands[][31]  =
{
    AT_XSYSTEMMODE,
    AT_MAGPIO,
    AT_COEX0,
    AT_CFUN
};

static int            at_sock = -1;
static int            fd = -1;
static char           nmea_strings[10][NRF_GNSS_NMEA_MAX_LEN];
static u32_t          nmea_string_cnt;

nrf_gnss_data_frame_t last_fix;

K_SEM_DEFINE(gps_sem, 1, 1);

static void test_periodic_acq(struct k_timer *timer);
static bool acquire_gps(void);
static int init_gps(void);
static int enable_gps(void);
static int start_gps(void);
static int disable_gps(void);
static int process_gps_data(nrf_gnss_data_frame_t *gps_data, bool *p_is_fix_valid, u64_t *p_fix_timestamp);
static void print_satellite_stats(nrf_gnss_data_frame_t *pvt_data, u64_t fix_timestamp);

K_TIMER_DEFINE(meas_msg_timer, test_periodic_acq, 0);

int main(void)
{
    test_periodic_acq(&meas_msg_timer);

    return 0;
}

static void test_periodic_acq(struct k_timer *timer)
{
    if (acquire_gps())
    {
        printk("Acquired GPS. Set timer to retry in 10 seconds\n");
        k_timer_start(&meas_msg_timer, K_SECONDS(10), 0);
    }
    else
    {
        printk("Couldn't get GPS semaphore. Try again in 5 seconds\n");
        k_timer_start(&meas_msg_timer, K_SECONDS(5), 0);
    }
}

static bool acquire_gps(void)
{
    if (0 != k_sem_take(&gps_sem, K_NO_WAIT))
    {
        return false;
    }

    printk("Calling init_gps\n");
    init_gps();

    nrf_gnss_data_frame_t gps_data;
    u64_t fix_timestamp = k_uptime_get();
    bool is_fix_valid = false;
    u32_t count = 0;

    printk("Getting GPS data...\n");

    start_gps();

    printk("GPS started\n");

    while (!is_fix_valid)
    {
        int num_gps_bytes;
        do
        {
            num_gps_bytes = process_gps_data(&gps_data, &is_fix_valid, &fix_timestamp);
        } while (num_gps_bytes > 0);
        count++;

        if (!is_fix_valid && (1 == (count % 5)))
        {
            print_satellite_stats(&gps_data, fix_timestamp);
        }

        if (is_fix_valid)
        {
            print_satellite_stats(&gps_data, fix_timestamp);
            break;
        }

        k_sleep(K_MSEC(1000));
    }

    printk("Acquisition took %d seconds. Disable returned %d\n", count, disable_gps());

    k_sem_give(&gps_sem);

    return true;
}

static int init_gps(void)
{
    int retval;

    nrf_gnss_fix_retry_t    fix_retry    = 0;
    nrf_gnss_fix_interval_t fix_interval = 1;
    nrf_gnss_nmea_mask_t    nmea_mask    = NRF_GNSS_NMEA_GSV_MASK |
                                           NRF_GNSS_NMEA_GSA_MASK |
                                           NRF_GNSS_NMEA_GLL_MASK |
                                           NRF_GNSS_NMEA_GGA_MASK |
                                           NRF_GNSS_NMEA_RMC_MASK;

    if (enable_gps() != 0)
    {
        printk("Failed to enable GPS\n");
        return -1;
    }

    if (fd < 0)
    {
        fd = nrf_socket(NRF_AF_LOCAL, NRF_SOCK_DGRAM, NRF_PROTO_GNSS);

        if (fd >= 0)
        {
            printk("GPS socket created\n");
        }
        else
        {
            printk("Could not init socket (err: %d)\n", fd);
            return -1;
        }
    }
    else
    {
        printk("GPS socket already created\n");
    }

    retval = nrf_setsockopt(fd,
                            NRF_SOL_GNSS,
                            NRF_SO_GNSS_FIX_RETRY,
                            &fix_retry,
                            sizeof(fix_retry));

    if (retval != 0)
    {
        printk("Failed to set fix retry value\n");
        return -1;
    }

    retval = nrf_setsockopt(fd,
                            NRF_SOL_GNSS,
                            NRF_SO_GNSS_FIX_INTERVAL,
                            &fix_interval,
                            sizeof(fix_interval));

    if (retval != 0)
    {
        printk("Failed to set fix interval value\n");
        return -1;
    }

    retval = nrf_setsockopt(fd,
                            NRF_SOL_GNSS,
                            NRF_SO_GNSS_NMEA_MASK,
                            &nmea_mask,
                            sizeof(nmea_mask));

    if (retval != 0)
    {
        printk("Failed to set nmea mask\n");
        return -1;
    }

    return 0;
}

static int enable_gps(void)
{
    int  bytes_sent;
    int  bytes_received;
    char buf[2];

    if (at_sock < 0)
    {
        at_sock = nrf_socket(NRF_AF_LTE, 0, NRF_PROTO_AT);
    }
    else
    {
        // GPS has already been configured
        return 0;
    }

    if (at_sock < 0)
    {
        return -1;
    }

    for (int i = 0; i < ARRAY_SIZE(at_commands); i++)
    {
        printk("Sending %s to modem\n", at_commands[i]);
        bytes_sent = nrf_send(at_sock, at_commands[i], strlen(at_commands[i]), 0);

        if (bytes_sent < 0)
        {
            nrf_close(at_sock);
            at_sock = -1;
            return -1;
        }

        do
        {
            bytes_received = nrf_recv(at_sock, buf, 2, 0);
        } while (bytes_received == 0);

        if (memcmp(buf, "OK", 2) != 0)
        {
            nrf_close(at_sock);
            at_sock = -1;
            return -1;
        }
    }

    nrf_close(at_sock);
    at_sock = -1;

    return 0;
}

static int start_gps(void)
{
    nrf_gnss_delete_mask_t  delete_mask  = 0;
    int retval = nrf_setsockopt(fd,
                                NRF_SOL_GNSS,
                                NRF_SO_GNSS_START,
                                &delete_mask,
                                sizeof(delete_mask));

    if (retval != 0)
    {
        printk("Failed to start GPS\n");
        return -1;
    }

    return 0;
}

static int process_gps_data(nrf_gnss_data_frame_t *gps_data, bool *p_is_fix_valid, u64_t *p_fix_timestamp)
{
    int retval;

    retval = nrf_recv(fd, gps_data, sizeof(nrf_gnss_data_frame_t), NRF_MSG_DONTWAIT);

    if (retval > 0)
    {
        switch (gps_data->data_id)
        {
            case NRF_GNSS_PVT_DATA_ID:

                if ((gps_data->pvt.flags & NRF_GNSS_PVT_FLAG_FIX_VALID_BIT) == NRF_GNSS_PVT_FLAG_FIX_VALID_BIT)
                {
                    *p_fix_timestamp = k_uptime_get();
                    *p_is_fix_valid = true;
                    memcpy(&last_fix, gps_data, sizeof(nrf_gnss_data_frame_t));

                    nmea_string_cnt = 0;
                }
                break;

            case NRF_GNSS_NMEA_DATA_ID:
                if (nmea_string_cnt < 10)
                {
                    memcpy(nmea_strings[nmea_string_cnt++], gps_data->nmea, retval);
                }
                break;

            default:
                break;
        }
    }

    return retval;
}

static void print_satellite_stats(nrf_gnss_data_frame_t *p_gps_data, u64_t fix_timestamp)
{
    u8_t  tracked          = 0;
    u8_t  in_fix           = 0;
    u8_t  unhealthy        = 0;

    for (int i = 0; i < NRF_GNSS_MAX_SATELLITES; ++i)
    {

        if ((p_gps_data->pvt.sv[i].sv > 0) &&
            (p_gps_data->pvt.sv[i].sv < 33))
        {
            tracked++;

            if (p_gps_data->pvt.sv[i].flags & NRF_GNSS_SV_FLAG_USED_IN_FIX)
            {
                in_fix++;
            }

            if (p_gps_data->pvt.sv[i].flags & NRF_GNSS_SV_FLAG_UNHEALTHY)
            {
                unhealthy++;
            }
        }
    }

    printk("Tracking: %d Using: %d Unhealthy: %d\n", tracked, in_fix, unhealthy);
    printk("Seconds since last fix %lld\n", (k_uptime_get() - fix_timestamp) / 1000);
}

static int disable_gps(void)
{
    int retval = nrf_setsockopt(fd,
                                NRF_SOL_GNSS,
                                NRF_SO_GNSS_STOP,
                                NULL,
                                0);

    while (retval != 0)
    {
        printk("Failed to stop GPS (retval %d)\n", retval);
        retval = nrf_setsockopt(fd,
                                NRF_SOL_GNSS,
                                NRF_SO_GNSS_STOP,
                                NULL,
                                0);
        k_sleep(3000);
    }

    retval = nrf_close(fd);

    if (retval != 0)
    {
        printk("Failed to close fd (retval %d)\n", retval);
    }

    fd = -1;

    return 0;
}

If the contents of the disable_gps function are commented out periodic GPS acquisitions happen but the code as-is hangs during the second call to init_gps. Example debug output:

***** Booting Zephyr OS build v2.0.99-ncs1 *****
Calling init_gps
Sending AT%XSYSTEMMODE=0,0,1,0 to modem
Sending AT%XMAGPIO=1,0,0,1,1,1574,1577 to modem
Sending AT%XCOEX0=1,1,1570,1580 to modem
Sending AT+CFUN=1 to modem
GPS socket created
Getting GPS data...
GPS started
Tracking: 1 Using: 0 Unhealthy: 0
Seconds since last fix 0
Tracking: 9 Using: 0 Unhealthy: 0
Seconds since last fix 5
Tracking: 10 Using: 0 Unhealthy: 0
Seconds since last fix 10
Tracking: 11 Using: 0 Unhealthy: 0
Seconds since last fix 15
Tracking: 11 Using: 0 Unhealthy: 0
Seconds since last fix 20
Tracking: 12 Using: 0 Unhealthy: 0
Seconds since last fix 25
Tracking: 12 Using: 0 Unhealthy: 0
Seconds since last fix 30
Tracking: 11 Using: 8 Unhealthy: 0
Seconds since last fix 0
Acquisition took 35 seconds. Disable returned 0
Acquired GPS. Set timer to retry in 10 seconds
Calling init_gps

Parents Reply
  • I have looked at your code, and while I was not able to confirm the "but only if either or both of the following are true:" part, I got results similar to you.

    You only need to call gps_enable once, at the beginning of your program. In fact, none of the AT commands you send has any effect after the modem is turned on.

    That, along with moving the GPS handling out of the timer interrupt (either to a loop in main or a work queue), solved the problem for me.

Children
No Data
Related