wiswasi
wiswasi

Reputation: 51

How to feed NMEA data to gpsfake to run with gpsd

I have an NMEA test data file that contains a few gps points:

$GPGGA,170358.132,5230.704,N,01324.262,E,1,12,1.0,0.0,M,0.0,M,,*63
$GPGSA,A,3,01,02,03,04,05,06,07,08,09,10,11,12,1.0,1.0,1.0*30
$GPRMC,170358.132,A,5230.704,N,01324.262,E,4152.6,050.1,150822,000.0,W*4B
$GPGGA,170359.132,5231.634,N,01325.380,E,1,12,1.0,0.0,M,0.0,M,,*6D
$GPGSA,A,3,01,02,03,04,05,06,07,08,09,10,11,12,1.0,1.0,1.0*30
$GPRMC,170359.132,A,5231.634,N,01325.380,E,2369.6,047.3,150822,000.0,W*4D
$GPGGA,170400.132,5232.183,N,01325.977,E,1,12,1.0,0.0,M,0.0,M,,*6C
$GPGSA,A,3,01,02,03,04,05,06,07,08,09,10,11,12,1.0,1.0,1.0*30
$GPRMC,170400.132,A,5232.183,N,01325.977,E,1313.3,288.5,150822,000.0,W*40

I am trying to feed these to gpsfake to test it with a gpsd demo script I have. I have tried running gpsfake alone, piping it with gpsd, and gpsd alone, but no luck. Mind you, this is the first time I use gpsd.

I have tried running gpsd using my script and a physical GPS dongle, it runs perfectly fine and I get the following output per my code:

~$ /usr/sbin/gpsd -n -G -b /dev/ttyUSB0 &
[1] 4514
~$ ./demo
WARNING: Opening interface localhost:gpsd
WARNING: Entering GPS poll loop (2000000us)
Speed: 0.017
Speed: 0.018
Speed: 0.018
Speed: 0.021
speed: 0.046976
{
  "timestamp": 1660331437857,
  "latitude": 33.6973963,
  "longitude": -117.7707148,
  "eph": 4.563,
  "speed": 0.047,
  "eps": 0.460,
  "track": 0.000,
  "satellites_used": 8,
  "hdop": 0.000000,
  "vdop": 0.000000
}
Speed: 0.021
[1]+  Done                    /usr/sbin/gpsd -n -G -b /dev/ttyUSB0

Can someone please tell me how I can feed the fake data to gpsfake instead of using the GPS dongle? Thanks!

Upvotes: 3

Views: 1711

Answers (1)

Shrout1
Shrout1

Reputation: 2607

Today was the first time I've used gpsfake to spoof GPS data. It's rather straightforward. I'm running Kali Linux so this response should be valid for Debian distros, though it will likely work elsewhere.

Here is the command syntax I used:

sudo systemctl stop gpsd
sudo systemctl stop gpsd.socket
sudo gpsfake -l --cycle 0.142857 --slow /home/user/gps/gps_sample.nmea

A couple notes:

  1. gpsfake uses port 2947 by default; this is the same default port as gpsd
    • This is why I shutdown gpsd before running gpsfake
    • gpsd.socket binds to port 2947 and does not shut down concurrently with gpsd so it also must be stopped for the port to be free.
    • Note: gpsfake is essentially a full replacement for gpsd
      • Don't run them at the same time unless you've configured properly and intend to pull from multiple gps sources at once
  2. I set the cycle on this to about 7 messages per second so that my simulation doesn't make it look like I'm going mach 1
  3. For the software that uses gpsd, the switch to gpsfake was transparent
    • My applications are configured to look at port 2947, not the physical receiver
    • This allows for transparent switching between gpsd and gpsfake
  4. Check that gpsfake is working with cgps or gpspipe -r

That should be it!

I packaged it all in to a handy little python script that can be used to automatically switch between gpsd and gpsfake. The script restores gpsd functionality while it stops. Be sure to run the script as sudo since it starts and stops services:

import subprocess
import time

def log_journal(message):
    #Logs to journalctl for better debugging
    subprocess.run(['logger', message])
    print(message)

def check_and_stop_service(service_name):
    #Checks the status of a service before stopping it
    status = subprocess.run(['sudo', 'systemctl', 'is-active', '--quiet', service_name])
    if status.returncode == 0:  # Service is running
        log_journal(f'Stopping {service_name}...')
        subprocess.run(['sudo', 'systemctl', 'stop', service_name])
        return True
    return False

def start_service(service_name):
    #Starts the specified service name
    log_journal(f'Starting {service_name}...')
    subprocess.run(['sudo', 'systemctl', 'start', service_name])

def run_gpsfake():
    log_journal('Starting gpsfake...')
    return subprocess.Popen(
    ['sudo', 'gpsfake', '-l', '--cycle', '0.142857', '--slow', '/home/user/gps/gps_sample.nmea'],
    stdout=subprocess.PIPE, stderr=subprocess.PIPE
    )

def prompt_exit():
    input("Press Enter to stop gpsfake and restart gpsd...")

def terminate_process(proc):
    if proc:
        log_journal('Terminating gpsfake process...')
        proc.terminate()
        try:
            proc.wait(timeout=5)
        except subprocess.TimeoutExpired:
            proc.kill()

def main():
    gpsfake_proc = None
    try:
        # Step 1: Check and stop gpsd services
        gpsd_running = check_and_stop_service('gpsd')
        gpsd_socket_running = check_and_stop_service('gpsd.socket')
        
        # Step 2: Execute gpsfake
        gpsfake_proc = run_gpsfake()
        time.sleep(5)  # Allow some time for gpsfake to start
        
        # Wait for user input to terminate gpsfake and restart gpsd services
        prompt_exit()
        
    finally:
        # Step 3: Cleanup
        terminate_process(gpsfake_proc)
        start_service('gpsd')

if __name__ == "__main__":
    main()

Also, I used nmeagen to create a simple nmea file, however you can just capture gpspipe output while using a gps receiver for more realistic simulations.

Upvotes: 2

Related Questions