// // This file is part of Dire Wolf, an amateur radio packet TNC. // // Copyright (C) 2013, 2014 John Langner, WB2OSZ // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program. If not, see . // /*------------------------------------------------------------------ * * Module: dwgps.c * * Purpose: Interface to location data, i.e. GPS receiver. * * Description: Tracker beacons need to know the current location. * At this time, I can't think of any other reason why * we would need this information. * * For Linux, we use gpsd and libgps. * This has the extra benefit that the system clock can * be set from the GPS signal. * * Not yet implemented for Windows. Not sure how yet. * The Windows location API is new in Windows 7. * At the end of 2013, about 1/3 of Windows users are * still using XP so that still needs to be supported. * * Reference: * *---------------------------------------------------------------*/ #if TEST #define ENABLE_GPS 1 #endif #include #include #include #include #include #include #include #if __WIN32__ #include #else #if ENABLE_GPS #include #if GPSD_API_MAJOR_VERSION != 5 #error libgps API version might be incompatible. #endif #endif #endif #include "direwolf.h" #include "textcolor.h" #include "dwgps.h" /* Was init successful? */ static enum { INIT_NOT_YET, INIT_SUCCESS, INIT_FAILED } init_status = INIT_NOT_YET; #if __WIN32__ #include #else #if ENABLE_GPS static struct gps_data_t gpsdata; #endif #endif /*------------------------------------------------------------------- * * Name: dwgps_init * * Purpose: Intialize the GPS interface. * * Inputs: none. * * Returns: 0 = success * -1 = failure * * Description: For Linux, this maps into gps_open. * Not yet implemented for Windows. * *--------------------------------------------------------------------*/ int dwgps_init (void) { #if __WIN32__ /* * Windows version. Not implemented yet. */ text_color_set(DW_COLOR_ERROR); dw_printf ("GPS interface not yet available in Windows version.\n"); init_status = INIT_FAILED; return (-1); #elif ENABLE_GPS int err; #if USE_GPS_SHM /* * Linux - Shared memory interface to gpsd. * * I wanted to use this method because it is simpler and more efficient. * * The current version of gpsd, supplied with Raspian, is 3.6 from back in * May 2012, is missing support for the shared memory interface. * https://github.com/raspberrypi/linux/issues/523 * * I tried to download a newer source and build with shared memory support * but ran into a couple other issues. * * sudo apt-get install libncurses5-dev * sudo apt-get install scons * cd ~ * wget http://download-mirror.savannah.gnu.org/releases/gpsd/gpsd-3.11.tar.gz * tar xfz gpsd-3.11.tar.gz * cd gpsd-3.11 * scons prefix=/usr libdir=lib/arm-linux-gnueabihf shm_export=True python=False * sudo scons udev-install * * For now, we will use the socket interface. * Maybe get back to this again someday. */ err = gps_open (GPSD_SHARED_MEMORY, NULL, &gpsdata); if (err != 0) { text_color_set(DW_COLOR_ERROR); dw_printf ("Unable to connect to GPSD shared memory interface, status=%d.\n", err); if (err == NL_NOHOST) { // I don't think this is right but we are not using it anyhow. dw_printf ("Shared memory interface is not enabled in libgps.\n"); dw_printf ("Download the gpsd source and build with 'shm_export=True' option.\n"); } else { dw_printf ("%s\n", gps_errstr(errno)); } init_status = INIT_FAILED; return (-1); } init_status = INIT_SUCCESS; return (0); #else /* * Linux - Socket interface to gpsd. */ err = gps_open ("localhost", DEFAULT_GPSD_PORT, &gpsdata); if (err != 0) { text_color_set(DW_COLOR_ERROR); dw_printf ("Unable to connect to GPSD stream, status%d.\n", err); dw_printf ("%s\n", gps_errstr(errno)); init_status = INIT_FAILED; return (-1); } gps_stream(&gpsdata, WATCH_ENABLE | WATCH_JSON, NULL); init_status = INIT_SUCCESS; return (0); #endif #else /* end ENABLE_GPS */ text_color_set(DW_COLOR_ERROR); dw_printf ("GPS interface not enabled in this version.\n"); dw_printf ("See documentation on how to rebuild with ENABLE_GPS.\n"); init_status = INIT_FAILED; return (-1); #endif } /* end dwgps_init */ /*------------------------------------------------------------------- * * Name: dwgps_read * * Purpose: Obtain current location from GPS receiver. * * Outputs: *plat - Latitude. * *plon - Longitude. * *pspeed - Speed, knots. * *pcourse - Course over ground, degrees. * *palt - Altitude, meters. * * Returns: -1 = error * 0 = location currently not available (no fix) * 2 = 2D fix, lat/lon, speed, and course are set. * 3 - 3D fix, altitude is also set. * *--------------------------------------------------------------------*/ int dwgps_read (double *plat, double *plon, float *pspeed, float *pcourse, float *palt) { #if __WIN32__ text_color_set(DW_COLOR_ERROR); dw_printf ("Internal error, dwgps_read, shouldn't be here.\n"); return (-1); #elif ENABLE_GPS int err; if (init_status != INIT_SUCCESS) { text_color_set(DW_COLOR_ERROR); dw_printf ("Internal error, dwgps_read without successful init.\n"); return (-1); } #if USE_GPS_SHM /* * Shared memory version. */ err = gps_read (&gpsdata); #if DEBUG dw_printf ("gps_read returns %d bytes\n", err); #endif #else /* * Socket version. */ // Wait for up to 1000 milliseconds. // This should only happen in the beaconing thread so // I'm not worried about other functions hanging. if (gps_waiting(&gpsdata, 1000)) { err = gps_read (&gpsdata); } else { gps_stream(&gpsdata, WATCH_ENABLE | WATCH_JSON, NULL); sleep (1); } #endif if (err > 0) { /* Data is available. */ if (gpsdata.status >= STATUS_FIX && gpsdata.fix.mode >= MODE_2D) { *plat = gpsdata.fix.latitude; *plon = gpsdata.fix.longitude; *pcourse = gpsdata.fix.track; *pspeed = MPS_TO_KNOTS * gpsdata.fix.speed; /* libgps uses meters/sec */ if (gpsdata.fix.mode >= MODE_3D) { *palt = gpsdata.fix.altitude; return (3); } return (2); } /* No fix. Probably temporary condition. */ return (0); } else if (err == 0) { /* No data available at the present time. */ return (0); } else { /* More serious error. */ return (-1); } #else text_color_set(DW_COLOR_ERROR); dw_printf ("Internal error, dwgps_read, shouldn't be here.\n"); return (-1); #endif } /* end dwgps_read */ /*------------------------------------------------------------------- * * Name: dwgps_term * * Purpose: Shut down GPS interface before exiting from application. * * Inputs: none. * * Returns: none. * *--------------------------------------------------------------------*/ void dwgps_term (void) { #if __WIN32__ #elif ENABLE_GPS if (init_status == INIT_SUCCESS) { #ifndef USE_GPS_SHM gps_stream(&gpsdata, WATCH_DISABLE, NULL); #endif gps_close (&gpsdata); } #else #endif } /* end dwgps_term */ /*------------------------------------------------------------------- * * Name: main * * Purpose: Simple unit test for other functions in this file. * * Description: Compile with -DTEST option. * * gcc -DTEST dwgps.c textcolor.c -lgps * ./a.out * *--------------------------------------------------------------------*/ #if TEST int main (int argc, char *argv[]) { #if __WIN32__ printf ("Not in win32 version yet.\n"); #elif ENABLE_GPS int err; int fix; double lat; double lon; float speed; float course; float alt; err = dwgps_init (); if (err != 0) exit(1); while (1) { fix = dwgps_read (&lat, &lon, &speed, &course, &alt) ; switch (fix) { case 3: case 2: dw_printf ("%.6f %.6f", lat, lon); dw_printf (" %.1f knots %.0f degrees", speed, course); if (fix==3) dw_printf (" altitude = %.1f meters", alt); dw_printf ("\n"); break; case 0: dw_printf ("location currently not available.\n"); break; default: dw_printf ("ERROR getting GPS information.\n"); } sleep (3); } #else printf ("Test: Shouldn't be here.\n"); #endif } /* end main */ #endif /* end dwgps.c */