├── Makefile.am ├── waf ├── doc ├── packets.txt ├── packets2.txt ├── notes.txt └── LICENSE ├── src ├── allicon2.png ├── allicons.png ├── Makefile.am ├── mapviewer.h ├── station.h ├── aprsis.h ├── callbacks.h ├── callbacks.c ├── aprsis.c ├── mapviewer.c ├── station.c └── mapviewer.ui ├── tools ├── mb7uah.txt ├── packets.txt ├── packets2.txt └── aprsfake ├── .gitignore ├── configure.ac ├── README.md ├── wscript └── dbmap.py /Makefile.am: -------------------------------------------------------------------------------- 1 | SUBDIRS = src 2 | 3 | -------------------------------------------------------------------------------- /waf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gordonjcp/aprsmap/HEAD/waf -------------------------------------------------------------------------------- /doc/packets.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gordonjcp/aprsmap/HEAD/doc/packets.txt -------------------------------------------------------------------------------- /src/allicon2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gordonjcp/aprsmap/HEAD/src/allicon2.png -------------------------------------------------------------------------------- /src/allicons.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gordonjcp/aprsmap/HEAD/src/allicons.png -------------------------------------------------------------------------------- /tools/mb7uah.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gordonjcp/aprsmap/HEAD/tools/mb7uah.txt -------------------------------------------------------------------------------- /tools/packets.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gordonjcp/aprsmap/HEAD/tools/packets.txt -------------------------------------------------------------------------------- /tools/packets2.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gordonjcp/aprsmap/HEAD/tools/packets2.txt -------------------------------------------------------------------------------- /src/Makefile.am: -------------------------------------------------------------------------------- 1 | AM_CFLAGS = $(APRSMAP_CFLAGS) 2 | bin_PROGRAMS = aprsmap 3 | aprsmap_SOURCES = aprsis.c callbacks.c mapviewer.c station.c 4 | aprsmap_LDADD = -lm $(APRSMAP_LIBS) 5 | -------------------------------------------------------------------------------- /src/mapviewer.h: -------------------------------------------------------------------------------- 1 | #ifndef MAPVIEWER_H 2 | #define MAPVIEWER_H 3 | 4 | gboolean process_packet(gchar *msg); 5 | 6 | void aprsmap_set_status(gchar *msg); 7 | 8 | 9 | #endif 10 | 11 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | build 2 | .waf* 3 | .lock-wscript 4 | .lock-wafbuild 5 | aprs.db 6 | Makefile.in 7 | aclocal.m4 8 | autom4te.cache/ 9 | compile 10 | configure 11 | depcomp 12 | install-sh 13 | missing 14 | src/.deps/ 15 | src/Makefile.in 16 | config.log 17 | config.status 18 | Makefile 19 | src/Makefile 20 | src/*.o 21 | src/aprsmap 22 | 23 | -------------------------------------------------------------------------------- /configure.ac: -------------------------------------------------------------------------------- 1 | AC_INIT([aprsmap], [0.5], [gordonjcp@gjcp.net]) 2 | AC_CONFIG_SRCDIR([src/]) 3 | AM_INIT_AUTOMAKE([-Wall foreign]) 4 | #AM_SILENT_RULES([yes]) 5 | 6 | AC_PROG_CC 7 | PKG_PROG_PKG_CONFIG 8 | 9 | PKG_CHECK_MODULES([APRSMAP], [ 10 | glib-2.0 11 | gtk+-3.0 12 | gdk-3.0 13 | osmgpsmap-1.0 14 | sqlite3 15 | libfap 16 | 17 | ]) 18 | AC_CONFIG_FILES([ 19 | Makefile 20 | src/Makefile 21 | ]) 22 | AC_OUTPUT 23 | 24 | -------------------------------------------------------------------------------- /src/station.h: -------------------------------------------------------------------------------- 1 | // station.h 2 | 3 | #ifndef STATION_H 4 | #define STATION_H 5 | 6 | #include 7 | #include 8 | 9 | typedef enum { 10 | APRS_NOFIX, 11 | APRS_VALIDFIX 12 | } aprs_fix_t; 13 | 14 | typedef struct { 15 | gchar *callsign; 16 | gchar symbol[2]; 17 | OsmGpsMapImage *image; 18 | OsmGpsMapTrack *track; 19 | GdkPixbuf *pix; 20 | cairo_surface_t *icon; 21 | gdouble speed; 22 | gdouble course; 23 | gdouble lat, lon; 24 | aprs_fix_t fix; 25 | } APRSMapStation; 26 | 27 | gboolean process_packet(gchar *msg); 28 | void write_to_db(double latitude, double longitude, float course, char *call, char 29 | *object, time_t *timestamp); 30 | #endif 31 | /* vim: set noexpandtab ai ts=4 sw=4 tw=4: */ 32 | -------------------------------------------------------------------------------- /tools/aprsfake: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import os, sys, time, random, socket 4 | 5 | def usage(): 6 | print "Usage: %s FILE" 7 | print "\tPORT is the TCP port to bind to" 8 | print "\tFILE is the file to read - one packet per line" 9 | 10 | 11 | if len(sys.argv) < 2: 12 | usage() 13 | exit(-1) 14 | 15 | #PORT = int(sys.argv[1]) 16 | PORT = 14580 17 | FILE = sys.argv[1] 18 | clients = [] 19 | 20 | 21 | s = socket.socket( 22 | socket.AF_INET, socket.SOCK_STREAM) 23 | 24 | s.bind((socket.gethostname(), PORT)) 25 | s.listen(5) 26 | 27 | (client, address) = s.accept() 28 | 29 | f = open(FILE) 30 | while 1: 31 | for l in f: 32 | client.send(l) 33 | time.sleep(0.1) #random.randint(1, 10)) 34 | 35 | -------------------------------------------------------------------------------- /doc/packets2.txt: -------------------------------------------------------------------------------- 1 | MB7UM>WIDE,WIDE2-1,qAR,MB7UKS-1:T#220,141,275,225,010,739,11111111 2 | MB7UYL>APZ187,TCPIP*,qAC,AHUBSWE2:=5224.00N/00215.00Wn Kidderminster, Worc's {Xrouter 187x}4$}= 3 | G6LTT>APU25N,TCPIP*,qAC,T2NUENGLD:@092225z5136.58N/00013.16W_225/000g000t032r000p...P000h86b10066 / 4 | MB7UC>APN982,WIDE2-2,qAR,G3PWJ-3:$ULTW00A80084015B000027B6000594F00001----0008054100000070 5 | MB7UIK>APU25N,TCPIP*,qAC,AHUBSWE2:APZ19,WIDE2-1,qAR,EI3RCW-2:!5202.37NS00737.67W#PHG3660/W3, SEARG APRS Digi Co. Waterford 7 | G4ZJH-2>APU25N,WIDE5-5,qAR,G8ZQA:;TRAFFIC *092222z5206.15N\00135.20E?35 In 10 Minutes 8 | 2M0OVV>UV0PQR,MB7UD*,qAR,GM7GDE-12:`zGNlH"j/]"4$}= 9 | MM0YEQ-9>APAND1,TCPIP*,qAU,jFindU-JS:!5557.68N\00407.35Wu 10 | MM0YEQ>APX198,WIDE2-2,qAR,MM1PTT:=5557.3 N/00408.1 W-XASTIR-Linux 11 | 12 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | aprsmap 2 | ======= 3 | 4 | An APRS mapping application developed by Gordon JC Pearce (MM0YEQ) and 5 | others, with key goals of being easier to use and looking prettier 6 | than the standard Amateur Radio software. The first in a series of 7 | applications based around the APRS protocol. 8 | 9 | This application is currently in the earliest stages of development, and 10 | is not representative of the quality of the final application, if we 11 | ever get there. 12 | 13 | It requires osm-gps-map libraries, and libfap libraries. If you're using 14 | Arch Linux, you can install both of these from AUR. Debian and Ubuntu 15 | users may need to install libsoup2.4-dev due to a packaging bug. 16 | 17 | aprsmap uses waf for building. To build: 18 | 19 | ./waf configure 20 | ./waf 21 | 22 | To run: 23 | 24 | ./build/aprsmap 25 | 26 | -------------------------------------------------------------------------------- /src/aprsis.h: -------------------------------------------------------------------------------- 1 | #ifndef APRSIS_H 2 | #define APRSIS_H 3 | 4 | #define APRSIS_LOGIN "user %s pass %s vers aprsmap 0.0" 5 | 6 | typedef struct _aprsis_ctx { 7 | GSocket *skt; 8 | guint state; 9 | int sockfd; 10 | char *host; 11 | char *port; 12 | char *user; 13 | char *pass; 14 | 15 | double latitude; 16 | double longitude; 17 | int radius; 18 | FILE *log_file; 19 | } aprsis_ctx; 20 | 21 | //aprs details structure - enables passing of variables between the properties pop up and the main program 22 | 23 | typedef struct _aprs_details { 24 | double lat; 25 | double lon; 26 | int range; 27 | aprsis_ctx *ctx; 28 | } aprs_details; 29 | 30 | aprs_details *aprs_details_new(double lat,double lon,int range,aprsis_ctx *ctx); 31 | 32 | 33 | GError *aprsis_connect(aprsis_ctx *ctx); 34 | 35 | void start_aprsis(aprsis_ctx *ctx); 36 | aprsis_ctx *aprsis_new(const char *host, const char *port, const char *user, const char *pass); 37 | int aprsis_read(aprsis_ctx *ctx, char *buf, size_t len); 38 | int aprsis_write(aprsis_ctx *ctx, char *buf, size_t len); 39 | void aprsis_write_log(aprsis_ctx *ctx, char *buf, size_t len); 40 | void aprsis_set_log(aprsis_ctx *ctx, FILE *log_file); 41 | 42 | void aprsis_set_filter(aprsis_ctx *ctx, double latitude, double longitude, int radius); 43 | int aprsis_login(aprsis_ctx *ctx); 44 | void aprsis_close(aprsis_ctx *ctx); 45 | void start_aprsis(aprsis_ctx *ctx); 46 | 47 | #endif 48 | 49 | /* vim: set noexpandtab ai ts=4 sw=4 tw=4: */ 50 | -------------------------------------------------------------------------------- /doc/notes.txt: -------------------------------------------------------------------------------- 1 | ------------------------------------------ 2 | Read 57 bytes: M3FVX-9>UR4UT2,MB7UTG*,WIDE1,qAR,G3ZCV:`v3Xl )>/`"40}_" 3 | inserted station M3FVX-9 4 | 5 | ------------------------------------------ 6 | Read 108 bytes: G7FIA-9>GPSC75,WIDE1-1,WIDE2-2,qAS,MB7UBP:$GPRMC,194416.000,A,5123.0943,N,00019.9043,E,0.00,,240511,,,A*71 7 | no checksum in (GPRMC,194416.000,A,5123.0943,N,00019.9043,E,0.00,,240511,,,A*71 8 | )couldn't decode that... 9 | Invalid date in GPRMC sentence 10 | ------------------------------------------ 11 | Read 95 bytes: 2E1GJN-1>APU25N,TCPIP*,qAC,AHUBSWE2:=5200.44N/00044.81W#www.2e1gjn.com - DIGI & RX ONLY IGATE 12 | inserted station 2E1GJN-1 13 | 14 | ------------------------------------------ 15 | Read 80 bytes: G7FAL>APU25N,TCPIP*,qAC,AHUBSWE2:>241643zCOW-WAY WX @ WWW.COW-WAY.TALKTALK.NET 16 | 17 | 18 | 19 | ------------------------------------------ 20 | Read 128 bytes: G6UIM>APWW08,TCPIP*,qAC,T2HAM:;QRUG6UIM *195205h5024.51N\00333.82W?RNG0100?SRAIL(26) ?LIFEBOAT(238) ?HOSP(250) ?ARCLUB(4)!w>K! 21 | 5c 3f 22 | inserted station G6UIM 23 | 24 | ------------------------------------------ 25 | Read 57 bytes: G6UIM>APWW08,TCPIP*,qAC,T2HAM::ANSRVR :CQ DEERCAM{BR} 26 | already got station G6UIM 27 | 28 | 29 | G4WAX>APNU19,NONE,qAR,MB7UXX:!5456.74N/00135.56W# (G4WAX APRS DIGI UIDIGI 1.9B3) 30 | 31 | G4ZJH-2, G0DEC, G7HEJ seem to form a triangle 32 | they all report a "traffic" object 33 | 34 | sp3sux has a good track 35 | g0hwc has a good track 36 | mb7uah has many points 37 | EI2MLP? seems to have shot across from England 38 | 2E0ICB 39 | MM1MPB 40 | MB7UXX 41 | M1PJB 42 | 2M0HCD 43 | M3GDW 44 | M6NHK 45 | MB7UR 46 | 47 | 2E0VCU-9>GPSC08,MB7UC*,WIDE2-1,qAR,G3PWJ-3:$GPGGA,112044.000,5057.4426,N,00030.0019,W,1,08,1.1,16.8,M,47.1,M,,0000*71 48 | caused core 49 | 50 | -------------------------------------------------------------------------------- /src/callbacks.h: -------------------------------------------------------------------------------- 1 | #ifndef CALLBACKS_H 2 | #define CALLBACKS_H 3 | 4 | #include "aprsis.h" 5 | #include "mapviewer.h" 6 | 7 | extern OsmGpsMap *map; 8 | extern GtkEntry *latent; 9 | extern GtkEntry *lonent; 10 | extern GtkEntry *rangeent; 11 | extern GtkWidget *about; 12 | extern GtkWidget *popup; 13 | extern GtkComboBox *server; 14 | extern sqlite3 *db; 15 | 16 | G_MODULE_EXPORT int callback(void *NotUsed, int argc, char **argv, char **azColName); 17 | 18 | G_MODULE_EXPORT int call_callback(void *NotUsed, int argc, char **argv, char **azColName); 19 | 20 | G_MODULE_EXPORT int user_callback(aprs_details *properties, int argc, char **argv, char **azColName); 21 | 22 | G_MODULE_EXPORT gboolean 23 | on_button_press_event (GtkWidget *widget, GdkEventButton *event, gpointer user_data); 24 | 25 | G_MODULE_EXPORT gboolean 26 | on_button_release_event (GtkWidget *widget, GdkEventButton *event, gpointer user_data); 27 | 28 | G_MODULE_EXPORT gboolean 29 | on_set_home_activate_event (GtkWidget *widget, aprs_details *properties); 30 | 31 | G_MODULE_EXPORT gboolean 32 | on_zoom_in_clicked_event (GtkWidget *widget, gpointer user_data); 33 | 34 | G_MODULE_EXPORT gboolean 35 | on_zoom_out_clicked_event (GtkWidget *widget, gpointer user_data); 36 | 37 | G_MODULE_EXPORT gboolean 38 | on_home_clicked_event (GtkWidget *widget, aprs_details *properties); 39 | 40 | G_MODULE_EXPORT void 41 | on_about_clicked_event (GtkWidget *widget, gpointer user_data); 42 | 43 | G_MODULE_EXPORT gpointer 44 | on_properties_clicked_event (GtkWidget *widget, aprs_details *properties); 45 | 46 | G_MODULE_EXPORT gboolean 47 | on_properties_ok_clicked (GtkWidget *widget, aprs_details *properties); 48 | 49 | G_MODULE_EXPORT gboolean 50 | on_properties_hide_event (GtkWidget *widget, gpointer user_data); 51 | 52 | G_MODULE_EXPORT void 53 | on_close (GtkWidget *widget, gpointer user_data); 54 | 55 | #endif 56 | 57 | 58 | -------------------------------------------------------------------------------- /wscript: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python 2 | 3 | # the following two variables are used by the target "waf dist" 4 | VERSION='0' 5 | APPNAME='aprsmap' 6 | 7 | # these variables are mandatory ('/' are converted automatically) 8 | top = '.' 9 | out = 'build' 10 | 11 | def options(opt): 12 | opt.tool_options('compiler_cc') 13 | 14 | def configure(conf): 15 | conf.check_tool('compiler_cc') 16 | conf.check(header_name='stdlib.h') 17 | conf.check(header_name='math.h') 18 | 19 | conf.check_cc(lib='m', uselib_store='M') 20 | 21 | conf.env.CCFLAGS = ['-O0', '-g3', '-Wall', '-Werror', '-DGTK_DISABLE_SINGLE_INCLUDES', '-DGDK_DISABLE_DEPRECATED', '-DGTK_DISABLE_DEPRECATED', '-DGSEAL_ENABLE'] 22 | 23 | conf.check_cfg(package='gtk+-2.0', uselib_store='GTK', atleast_version='2.6.0', mandatory=True, args='--cflags --libs') 24 | conf.check_cfg(package='gmodule-2.0', uselib_store='GMODULE', atleast_version='2.18.0', args='--cflags --libs') 25 | conf.check_cfg(package='gthread-2.0', uselib_store='GTHREAD', atleast_version = '2.32.1', args = '--cflags --libs') 26 | # because of a packaging bug is libosmgpsmap, check for libsoup explicitly 27 | conf.check_cfg(package='libsoup-2.4', atleast_version = '2.4', args = '--cflags --libs') 28 | 29 | conf.check_cfg(package='osmgpsmap', uselib_store='OSMGPSMAP', atleast_version = '0.7.2', args = '--cflags --libs') 30 | conf.check_cfg(package='libfap', uselib_store='FAP', atleast_version = '1.1', args = '--cflags --libs') 31 | conf.check_cfg(package='sqlite3', uselib_store='SQL', atleast_version = '3', args = '--cflags --libs') 32 | conf.check_cfg(package='gthread-2.0', uselib_store='GTHREAD2', atleast_version = '2.32.1', args = '--cflags --libs') 33 | 34 | 35 | def build(bld): 36 | # aprsmap 37 | bld( 38 | features = 'c cprogram', 39 | source = ['aprsis.c', 'callbacks.c', 'mapviewer.c', 'station.c'], 40 | target = 'aprsmap', 41 | uselib = 'GTK OSMGPSMAP FAP GMODULE SQL GTHREAD2 M', 42 | includes = '. /usr/include') 43 | 44 | -------------------------------------------------------------------------------- /src/callbacks.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | #include 10 | #include 11 | 12 | #include "callbacks.h" 13 | #include "aprsis.h" 14 | 15 | 16 | G_MODULE_EXPORT int callback(void *NotUsed, int argc, char **argv, char **azColName){ 17 | int i; 18 | for(i=0; ilat = g_ascii_strtod (argv[i],NULL); 39 | //printf("%f\n", properties->lat); //debug 40 | printf("Lat Data Loaded\n"); 41 | } else if (strcmp (azColName[i],"lon") == 0) { 42 | properties->lon = g_ascii_strtod (argv[i],NULL); 43 | //printf("%f\n", properties->lon); //debug 44 | printf("Lon Data Loaded\n"); 45 | } 46 | 47 | //osm_gps_map_set_center_and_zoom(map,properties->lat,properties->lon,5); 48 | } 49 | 50 | printf("\n"); 51 | return 0; 52 | } 53 | 54 | G_MODULE_EXPORT gboolean 55 | on_button_press_event (GtkWidget *widget, GdkEventButton *event, gpointer user_data) 56 | { 57 | if(event->type == GDK_2BUTTON_PRESS) { 58 | int zoom; 59 | g_object_get(map, "zoom", &zoom, NULL); 60 | osm_gps_map_set_zoom(map, zoom+1); 61 | } 62 | return FALSE; 63 | } 64 | /* 65 | Unconnected callbacks that will likely become useful in time. 66 | G_MODULE_EXPORT gboolean 67 | on_button_release_event (GtkWidget *widget, GdkEventButton *event, gpointer user_data) 68 | { 69 | float lat,lon; 70 | OsmGpsMap *map = OSM_GPS_MAP(widget); 71 | 72 | g_object_get(map, "latitude", &lat, "longitude", &lon, NULL); 73 | gchar *msg = g_strdup_printf("%f,%f",lat,lon); 74 | g_free(msg); 75 | 76 | return FALSE; 77 | } */ 78 | 79 | G_MODULE_EXPORT gboolean 80 | on_set_home_activate_event (GtkWidget *widget, aprs_details *properties) 81 | { 82 | float lat,lon; 83 | 84 | g_object_get(map, "latitude", &lat, "longitude", &lon, NULL); 85 | 86 | properties->lat = lat; 87 | properties->lon = lon; 88 | 89 | //set filter around home area 90 | aprsis_set_filter(properties->ctx, properties->lat, properties->lon, properties->range); 91 | //update text boxes in properties 92 | gtk_entry_set_text(latent, g_strdup_printf("%f",properties->lat)); 93 | gtk_entry_set_text(lonent, g_strdup_printf("%f",properties->lon)); 94 | gtk_entry_set_text(rangeent, g_strdup_printf("%d",properties->range)); 95 | return FALSE; 96 | } 97 | 98 | G_MODULE_EXPORT gboolean 99 | on_zoom_in_clicked_event (GtkWidget *widget, gpointer user_data) 100 | { 101 | int zoom; 102 | g_object_get(map, "zoom", &zoom, NULL); 103 | osm_gps_map_set_zoom(map, zoom+1); 104 | return FALSE; 105 | } 106 | 107 | G_MODULE_EXPORT gboolean 108 | on_zoom_out_clicked_event (GtkWidget *widget, gpointer user_data) 109 | { 110 | int zoom; 111 | g_object_get(map, "zoom", &zoom, NULL); 112 | osm_gps_map_set_zoom(map, zoom-1); 113 | return FALSE; 114 | } 115 | 116 | G_MODULE_EXPORT gboolean 117 | on_home_clicked_event (GtkWidget *widget, aprs_details *properties) 118 | { 119 | //change this bitch up 120 | osm_gps_map_set_center_and_zoom(map,properties->lat,properties->lon,5); 121 | return FALSE; 122 | } 123 | G_MODULE_EXPORT void 124 | on_about_clicked_event (GtkWidget *widget, gpointer user_data) 125 | { 126 | gtk_dialog_run (GTK_DIALOG(about) ); 127 | gtk_widget_hide (GTK_WIDGET(about)); 128 | } 129 | G_MODULE_EXPORT gpointer 130 | on_properties_clicked_event (GtkWidget *widget, aprs_details *properties) 131 | { 132 | gtk_window_present( GTK_WINDOW( popup ) ); 133 | return FALSE; 134 | } 135 | G_MODULE_EXPORT gboolean 136 | on_properties_ok_clicked (GtkWidget *widget, aprs_details *properties) 137 | { 138 | double oldlat = properties->lat; 139 | double oldlon = properties->lon; 140 | printf("We were: %f%f\n",oldlat,oldlon); 141 | properties->lat=g_ascii_strtod (gtk_entry_get_text(GTK_ENTRY(latent)),NULL); 142 | properties->lon=g_ascii_strtod (gtk_entry_get_text(GTK_ENTRY(lonent)), NULL); 143 | properties->range=g_ascii_strtod (gtk_entry_get_text(GTK_ENTRY(rangeent)), NULL); 144 | printf("We are: %f%f\n",properties->lat,properties->lon); 145 | //Check Latitude/Longitude entries are correct 146 | if(properties->lat > 89.9 || properties->lat < -89.9) { 147 | //printf("Invalid Lat\n"); 148 | //double homelat = oldlat; 149 | //printf("New Lat:%f\n", homelat); 150 | gtk_entry_set_text(latent, g_strdup_printf("%f",properties->lat)); 151 | } 152 | if(properties->lon > 180 || properties->lon < -180) { 153 | //printf("Invalid Lon\n"); 154 | //double homelon = oldlon; 155 | //printf("New Lon:%f\n", homelon); 156 | gtk_entry_set_text(lonent, g_strdup_printf("%f",properties->lon)); 157 | } 158 | //centre map on new coordinates after widget closed 159 | osm_gps_map_set_center_and_zoom(map,properties->lat, properties->lon, 5); 160 | aprsis_set_filter(properties->ctx, properties->lat,properties->lon,properties->range); 161 | gtk_widget_hide( GTK_WIDGET( popup ) ); 162 | return FALSE; 163 | } 164 | G_MODULE_EXPORT gboolean 165 | on_properties_hide_event (GtkWidget *widget, gpointer user_data) 166 | { 167 | gtk_widget_hide( GTK_WIDGET( popup ) ); 168 | return FALSE; 169 | } 170 | 171 | G_MODULE_EXPORT void 172 | on_close (GtkWidget *widget, gpointer user_data) 173 | { 174 | gtk_widget_destroy(widget); 175 | gtk_main_quit(); 176 | } 177 | -------------------------------------------------------------------------------- /src/aprsis.c: -------------------------------------------------------------------------------- 1 | // network.c 2 | // connect to APRS-IS server 3 | // Copyright 2011 Gordonjcp MM0YEQ 4 | // GPL V3 applies 5 | 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | #include "aprsis.h" 19 | #include "station.h" 20 | #include "mapviewer.h" 21 | 22 | static guint aprs1; 23 | static guint reconnect_timer; 24 | static GIOChannel *aprsis_io; 25 | 26 | enum { APRSIS_DISCONNECTED, APRSIS_CONNECTING, APRSIS_CONNECTED }; 27 | 28 | aprsis_ctx *aprsis_new(const char *host, const char *port, const char *user, const char *pass) { 29 | aprsis_ctx *ctx = calloc(1, sizeof(aprsis_ctx)); 30 | 31 | ctx->sockfd = -1; 32 | ctx->host = strdup(host); 33 | ctx->port = strdup(port); 34 | ctx->user = strdup(user); 35 | ctx->pass = strdup(pass); 36 | 37 | ctx->log_file = NULL; 38 | 39 | return ctx; 40 | } 41 | 42 | void aprsis_set_log(aprsis_ctx *ctx, FILE *file) { 43 | ctx->log_file = file; 44 | } 45 | 46 | int aprsis_read(aprsis_ctx *ctx, char *buf, size_t len) { 47 | // read a line from the GSocket, and maybe log it to a file 48 | GError *err; 49 | int count = g_socket_receive(ctx->skt, buf, len, NULL, &err); 50 | 51 | if (ctx->log_file != NULL) { 52 | printf("Logging read\n"); 53 | fprintf(ctx->log_file, "< %s\n", buf); 54 | } 55 | return count; 56 | } 57 | 58 | int aprsis_write(aprsis_ctx *ctx, char *buf, size_t len) { 59 | // send a line to the GSocket 60 | GError *err; 61 | int count = g_socket_send(ctx->skt, buf, len, NULL, &err); 62 | 63 | if (ctx->log_file != NULL) { 64 | printf("Logging write\n"); 65 | fprintf(ctx->log_file, "> %s\n", buf); 66 | } 67 | 68 | return count; 69 | } 70 | 71 | void aprsis_write_log(aprsis_ctx *ctx, char *buf, size_t len) { 72 | if (ctx->log_file != NULL) { 73 | fprintf(ctx->log_file, "< %s\n", buf); 74 | } 75 | } 76 | 77 | GError *aprsis_connect(aprsis_ctx *ctx) { 78 | // connect to an APRS-IS server 79 | // return GError 80 | 81 | ctx->state = APRSIS_CONNECTING; 82 | GError *err = NULL; 83 | GSocketClient *client = g_socket_client_new(); 84 | GSocketConnection *conn = g_socket_client_connect_to_host(client, ctx->host, 14580, NULL, &err); // FIXME needs to convert string to int 85 | 86 | if (conn) { 87 | ctx->skt = g_socket_connection_get_socket(conn); 88 | } 89 | if (ctx->skt) { 90 | ctx->sockfd = g_socket_get_fd(ctx->skt); 91 | } 92 | 93 | if(err) { 94 | g_message ("%s", err->message); 95 | ctx->state = APRSIS_DISCONNECTED; 96 | } 97 | return err; 98 | } 99 | 100 | int aprsis_login(aprsis_ctx *ctx) { 101 | // wait for prompt, send filter message 102 | char buf[256]; 103 | int n; 104 | 105 | // note that this doesn't *actually* check what the prompt is 106 | bzero(&buf, 256); 107 | n = aprsis_read(ctx, buf, 256); 108 | if (n<0) { 109 | g_error("couldn't read from socket"); 110 | } 111 | g_message("got: %s",buf); 112 | 113 | sprintf(buf, APRSIS_LOGIN"\n", ctx->user, ctx->pass); 114 | g_message("sending: %s", buf); 115 | aprsis_write(ctx, buf, strlen(buf)); 116 | bzero(&buf, 256); 117 | n = aprsis_read(ctx, buf, 256); 118 | if (n<0) { 119 | g_error("couldn't read from socket"); 120 | } 121 | g_message("got: %s",buf); 122 | 123 | return 0; 124 | } 125 | 126 | void aprsis_set_filter(aprsis_ctx *ctx, double latitude, double longitude, int radius) { 127 | // sets a filter given latitude, longitude and radius 128 | ctx->latitude = latitude; 129 | ctx->longitude = longitude; 130 | ctx->radius = radius; 131 | 132 | if (ctx->skt) { 133 | char buf[64]; 134 | snprintf(buf, sizeof(buf), "#filter r/%.0f/%.0f/%d\n", latitude, longitude, radius); 135 | g_message("Sending filter: %s", buf); 136 | aprsis_write(ctx, buf, strlen(buf)); 137 | } 138 | } 139 | 140 | void aprsis_set_filter_string(aprsis_ctx *ctx, char *filter) { 141 | // send a filter string, for more complex filters 142 | if (ctx->sockfd != -1) { 143 | char buf[64]; 144 | snprintf(buf, sizeof(buf), "#filter %s\n", filter); 145 | g_message("Sending filter: %s", buf); 146 | aprsis_write(ctx, buf, strlen(buf)); 147 | } 148 | } 149 | 150 | 151 | void aprsis_close(aprsis_ctx *ctx) { 152 | // close the connection and clean up 153 | close(ctx->sockfd); 154 | if (ctx->host != NULL) { 155 | free(ctx->host); 156 | } 157 | 158 | if (ctx->port != NULL) { 159 | free(ctx->port); 160 | } 161 | 162 | if (ctx->user != NULL) { 163 | free(ctx->user); 164 | } 165 | 166 | if (ctx->pass != NULL) { 167 | free(ctx->pass); 168 | } 169 | free(ctx); 170 | if (aprs1) { 171 | g_source_remove(aprs1); 172 | } 173 | if (aprsis_io) { 174 | g_io_channel_unref (aprsis_io); 175 | } 176 | 177 | } 178 | 179 | static gboolean aprsis_got_packet(GIOChannel *gio, GIOCondition condition, gpointer data) { 180 | // callback when GIOChannel tells us there's an APRS packet to be handled 181 | GIOStatus ret; 182 | GError *err = NULL; 183 | gchar *msg; 184 | gsize len; 185 | aprsis_ctx *ctx = (aprsis_ctx *) data; 186 | 187 | if (condition & G_IO_HUP) 188 | g_message ("Read end of pipe died!"); // FIXME - handle this more gracefully 189 | 190 | if (condition & G_IO_ERR) { 191 | g_message ("IO error"); 192 | return FALSE; 193 | } 194 | 195 | ret = g_io_channel_read_line (gio, &msg, &len, NULL, &err); 196 | if (ret == G_IO_STATUS_ERROR) { 197 | g_message("Error reading: %s", err->message); 198 | ctx->state = APRSIS_DISCONNECTED; 199 | return FALSE; 200 | } 201 | if (ret == G_IO_STATUS_EOF) { 202 | g_message("EOF (server disconnected)"); 203 | ctx->state = APRSIS_DISCONNECTED; 204 | return FALSE; // shut down the callback, for now 205 | } 206 | 207 | aprsis_write_log(ctx, msg, len); 208 | 209 | if (msg[0] == '#') { 210 | printf("can ignore comment message: %s\n", msg); 211 | } else { 212 | process_packet(msg); 213 | } 214 | 215 | g_free(msg); 216 | return TRUE; 217 | } 218 | 219 | static gboolean aprsis_reconnect(void *ptr) { 220 | // called once a second when the timer times out 221 | aprsis_ctx *ctx = ptr; 222 | printf("*** %s(): \n",__PRETTY_FUNCTION__); 223 | start_aprsis(ctx); 224 | return 0; 225 | } 226 | 227 | static gboolean 228 | aprsis_io_error(GIOChannel *src, GIOCondition condition, void *ptr) 229 | { 230 | printf("*** %s(): \n",__PRETTY_FUNCTION__); 231 | aprsis_ctx *ctx = ptr; 232 | ctx->state = APRSIS_DISCONNECTED; 233 | reconnect_timer = g_timeout_add_seconds(5, aprsis_reconnect, ctx); 234 | return FALSE; 235 | } 236 | 237 | static void start_aprsis_thread(void *ptr) { 238 | 239 | GError *error = NULL; 240 | aprsis_ctx *ctx = ptr; 241 | 242 | if (!reconnect_timer) reconnect_timer = g_timeout_add_seconds(10, aprsis_reconnect, ctx); 243 | 244 | if (aprsis_connect(ctx)) { 245 | g_message("failed to connect"); 246 | return; 247 | } 248 | 249 | g_message("logging in..."); 250 | aprsis_login(ctx); 251 | 252 | g_source_remove (reconnect_timer); 253 | reconnect_timer = 0; 254 | 255 | aprsis_set_filter(ctx, 55, -4, 600); 256 | //aprsis_set_filter_string(ctx, "p/M/G/2"); // callsigns beginning with G, M or 2 - UK callsigns, normally 257 | //aprsis_set_filter_string(ctx, "p/HB9"); // Swiss callsigns 258 | 259 | aprsis_io = g_io_channel_unix_new (ctx->sockfd); 260 | g_io_channel_set_encoding(aprsis_io, NULL, &error); 261 | if (!g_io_add_watch (aprsis_io, G_IO_IN, aprsis_got_packet, ctx)) 262 | g_error ("Cannot add watch on GIOChannel G_IO_IN"); 263 | 264 | 265 | if (!g_io_add_watch (aprsis_io, G_IO_ERR | G_IO_HUP, aprsis_io_error, ctx)) 266 | g_error ("Cannot add watch on GIOChannel G_IO_IN"); 267 | 268 | ctx->state = APRSIS_CONNECTED; 269 | } 270 | 271 | void start_aprsis(aprsis_ctx *ctx) { 272 | // prepare the APRS-IS connection thread 273 | 274 | printf("ctx->state = %d\n", ctx->state); 275 | if (ctx->state != APRSIS_DISCONNECTED) return; 276 | 277 | // remove the IO channel and watch 278 | if (aprs1) { 279 | g_source_remove(aprs1); 280 | aprs1=0; 281 | } 282 | if (reconnect_timer) { 283 | g_source_remove (reconnect_timer); 284 | reconnect_timer = 0; 285 | } 286 | if (aprsis_io) { 287 | g_io_channel_unref (aprsis_io); 288 | aprsis_io = NULL; 289 | } 290 | g_thread_new("aprsis-thread",(GThreadFunc) start_aprsis_thread, ctx); 291 | } 292 | 293 | /* vim: set noexpandtab ai ts=4 sw=4 tw=4: */ 294 | -------------------------------------------------------------------------------- /dbmap.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python2 2 | 3 | """ 4 | Copyright (C) Hadley Rich 2008 5 | based on main.c - with thanks to John Stowers 6 | 7 | This is free software: you can redistribute it and/or modify it 8 | under the terms of the GNU General Public License 9 | as published by the Free Software Foundation; version 2. 10 | 11 | This program is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with this program; if not, see . 18 | """ 19 | 20 | import sys 21 | import os.path 22 | import gtk.gdk 23 | import gobject 24 | 25 | import sqlite3, time 26 | 27 | gobject.threads_init() 28 | gtk.gdk.threads_init() 29 | 30 | #Try static lib first 31 | mydir = os.path.dirname(os.path.abspath(__file__)) 32 | libdir = os.path.abspath(os.path.join(mydir, "..", "python", ".libs")) 33 | sys.path.insert(0, libdir) 34 | 35 | import osmgpsmap 36 | print "using library: %s (version %s)" % (osmgpsmap.__file__, osmgpsmap.__version__) 37 | 38 | assert osmgpsmap.__version__ == "0.7.3" 39 | 40 | class DummyMapNoGpsPoint(osmgpsmap.GpsMap): 41 | def do_draw_gps_point(self, drawable): 42 | pass 43 | gobject.type_register(DummyMapNoGpsPoint) 44 | 45 | class DummyLayer(gobject.GObject, osmgpsmap.GpsMapLayer): 46 | def __init__(self): 47 | gobject.GObject.__init__(self) 48 | 49 | def do_draw(self, gpsmap, gdkdrawable): 50 | pass 51 | 52 | def do_render(self, gpsmap): 53 | pass 54 | 55 | def do_busy(self): 56 | return False 57 | 58 | def do_button_press(self, gpsmap, gdkeventbutton): 59 | return False 60 | gobject.type_register(DummyLayer) 61 | 62 | class Database(): 63 | def __init__(self): 64 | self.conn = sqlite3.connect("./aprs.db") 65 | self.crs = self.conn.cursor() 66 | def get_stations(self): 67 | self.crs.execute("SELECT DISTINCT call, count(call) FROM call_data GROUP BY call;") 68 | return self.crs.fetchall() 69 | def get_points(self, station): 70 | self.crs.execute("SELECT lat,lon, call FROM call_data WHERE call = ?;", (station,)) 71 | return self.crs.fetchall() 72 | 73 | 74 | class UI(gtk.Window): 75 | def __init__(self): 76 | gtk.Window.__init__(self, gtk.WINDOW_TOPLEVEL) 77 | 78 | db.get_stations() 79 | self.set_default_size(900, 675) 80 | self.connect('destroy', lambda x: gtk.main_quit()) 81 | self.set_title('OpenStreetMap GPS Mapper') 82 | 83 | self.vbox = gtk.VBox(False, 0) 84 | self.add(self.vbox) 85 | 86 | if 0: 87 | self.osm = DummyMapNoGpsPoint() 88 | else: 89 | self.osm = osmgpsmap.GpsMap() 90 | #self.osm.layer_add( 91 | # osmgpsmap.GpsMapOsd( 92 | # show_dpad=True, 93 | # show_zoom=True)) 94 | self.osm.layer_add( 95 | DummyLayer()) 96 | 97 | self.osm.connect('button_release_event', self.map_clicked) 98 | 99 | #connect keyboard shortcuts 100 | self.osm.set_keyboard_shortcut(osmgpsmap.KEY_FULLSCREEN, gtk.gdk.keyval_from_name("F11")) 101 | self.osm.set_keyboard_shortcut(osmgpsmap.KEY_UP, gtk.gdk.keyval_from_name("Up")) 102 | self.osm.set_keyboard_shortcut(osmgpsmap.KEY_DOWN, gtk.gdk.keyval_from_name("Down")) 103 | self.osm.set_keyboard_shortcut(osmgpsmap.KEY_LEFT, gtk.gdk.keyval_from_name("Left")) 104 | self.osm.set_keyboard_shortcut(osmgpsmap.KEY_RIGHT, gtk.gdk.keyval_from_name("Right")) 105 | 106 | #connect to tooltip 107 | self.osm.props.has_tooltip = True 108 | self.osm.connect("query-tooltip", self.on_query_tooltip) 109 | 110 | self.osm.set_center_and_zoom(55.872, -4.291, 16) 111 | 112 | self.latlon_entry = gtk.Entry() 113 | 114 | zoom_in_button = gtk.Button(stock=gtk.STOCK_ZOOM_IN) 115 | zoom_in_button.connect('clicked', self.zoom_in_clicked) 116 | zoom_out_button = gtk.Button(stock=gtk.STOCK_ZOOM_OUT) 117 | zoom_out_button.connect('clicked', self.zoom_out_clicked) 118 | home_button = gtk.Button(stock=gtk.STOCK_HOME) 119 | home_button.connect('clicked', self.home_clicked) 120 | cache_button = gtk.Button('Cache') 121 | cache_button.connect('clicked', self.cache_clicked) 122 | 123 | hbox2 = gtk.HBox(False, 0) 124 | 125 | 126 | 127 | # liststore for stations 128 | lstore = gtk.ListStore(str, int) 129 | s = db.get_stations() 130 | for i in s: 131 | lstore.append([i[0], i[1]]) 132 | treeview = gtk.TreeView(lstore) 133 | rendererText = gtk.CellRendererText() 134 | column = gtk.TreeViewColumn("Station", rendererText, text=0) 135 | column.set_sort_column_id(0) 136 | column.set_min_width(100) 137 | treeview.append_column(column) 138 | 139 | column = gtk.TreeViewColumn("Points", rendererText, text=1) 140 | column.set_sort_column_id(1) 141 | column.set_min_width(50) 142 | treeview.append_column(column) 143 | 144 | treeview.columns_autosize() 145 | treeview.connect("row-activated", self.on_activated) 146 | 147 | sw = gtk.ScrolledWindow() 148 | sw.set_shadow_type(gtk.SHADOW_ETCHED_IN) 149 | sw.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) 150 | sw.add(treeview) 151 | sw.set_size_request(200,-1) 152 | 153 | vbox2 = gtk.VBox(False, 0) 154 | 155 | calendar = gtk.Calendar() 156 | vbox2.pack_start(calendar, False) 157 | vbox2.pack_start(sw) 158 | 159 | 160 | hbox2.pack_start(vbox2, expand=False) 161 | hbox2.pack_start(self.osm) 162 | self.vbox.pack_start(hbox2) 163 | 164 | 165 | 166 | hbox = gtk.HBox(False, 0) 167 | hbox.pack_start(zoom_in_button) 168 | hbox.pack_start(zoom_out_button) 169 | hbox.pack_start(home_button) 170 | hbox.pack_start(cache_button) 171 | 172 | #add ability to test custom map URIs 173 | ex = gtk.Expander("Map Repository URI") 174 | ex.props.use_markup = True 175 | vb = gtk.VBox() 176 | self.repouri_entry = gtk.Entry() 177 | self.repouri_entry.set_text(self.osm.props.repo_uri) 178 | self.image_format_entry = gtk.Entry() 179 | self.image_format_entry.set_text(self.osm.props.image_format) 180 | 181 | lbl = gtk.Label( 182 | """ 183 | Enter an repository URL to fetch map tiles from in the box below. Special metacharacters may be included in this url 184 | 185 | Metacharacters: 186 | \t#X\tMax X location 187 | \t#Y\tMax Y location 188 | \t#Z\tMap zoom (0 = min zoom, fully zoomed out) 189 | \t#S\tInverse zoom (max-zoom - #Z) 190 | \t#Q\tQuadtree encoded tile (qrts) 191 | \t#W\tQuadtree encoded tile (1234) 192 | \t#U\tEncoding not implemeted 193 | \t#R\tRandom integer, 0-4""") 194 | lbl.props.xalign = 0 195 | lbl.props.use_markup = True 196 | lbl.props.wrap = True 197 | 198 | ex.add(vb) 199 | vb.pack_start(lbl, False) 200 | 201 | hb = gtk.HBox() 202 | hb.pack_start(gtk.Label("URI: "), False) 203 | hb.pack_start(self.repouri_entry, True) 204 | vb.pack_start(hb, False) 205 | 206 | hb = gtk.HBox() 207 | hb.pack_start(gtk.Label("Image Format: "), False) 208 | hb.pack_start(self.image_format_entry, True) 209 | vb.pack_start(hb, False) 210 | 211 | gobtn = gtk.Button("Load Map URI") 212 | gobtn.connect("clicked", self.load_map_clicked) 213 | vb.pack_start(gobtn, False) 214 | 215 | self.show_tooltips = False 216 | cb = gtk.CheckButton("Show Location in Tooltips") 217 | cb.props.active = self.show_tooltips 218 | cb.connect("toggled", self.on_show_tooltips_toggled) 219 | self.vbox.pack_end(cb, False) 220 | 221 | cb = gtk.CheckButton("Disable Cache") 222 | cb.props.active = False 223 | cb.connect("toggled", self.disable_cache_toggled) 224 | self.vbox.pack_end(cb, False) 225 | 226 | self.vbox.pack_end(ex, False) 227 | self.vbox.pack_end(self.latlon_entry, False) 228 | self.vbox.pack_end(hbox, False) 229 | 230 | gobject.timeout_add(500, self.print_tiles) 231 | 232 | def disable_cache_toggled(self, btn): 233 | if btn.props.active: 234 | self.osm.props.tile_cache = osmgpsmap.CACHE_DISABLED 235 | else: 236 | self.osm.props.tile_cache = osmgpsmap.CACHE_AUTO 237 | 238 | def on_show_tooltips_toggled(self, btn): 239 | self.show_tooltips = btn.props.active 240 | 241 | def load_map_clicked(self, button): 242 | uri = self.repouri_entry.get_text() 243 | format = self.image_format_entry.get_text() 244 | if uri and format: 245 | if self.osm: 246 | #remove old map 247 | self.vbox.remove(self.osm) 248 | try: 249 | self.osm = osmgpsmap.GpsMap( 250 | repo_uri=uri, 251 | image_format=format 252 | ) 253 | except Exception, e: 254 | print "ERROR:", e 255 | self.osm = osm.GpsMap() 256 | 257 | self.vbox.pack_start(self.osm, True) 258 | self.osm.connect('button_release_event', self.map_clicked) 259 | self.osm.show() 260 | 261 | def print_tiles(self): 262 | if self.osm.props.tiles_queued != 0: 263 | print self.osm.props.tiles_queued, 'tiles queued' 264 | return True 265 | 266 | def zoom_in_clicked(self, button): 267 | self.osm.set_zoom(self.osm.props.zoom + 1) 268 | 269 | def zoom_out_clicked(self, button): 270 | self.osm.set_zoom(self.osm.props.zoom - 1) 271 | 272 | def home_clicked(self, button): 273 | pass 274 | def on_activated(self, widget, row, col): 275 | def plot_line(station, colour): 276 | points = db.get_points(station) 277 | try: 278 | if self.t: 279 | self.osm.track_remove(self.t) 280 | except: 281 | pass 282 | self.t = osmgpsmap.GpsMapTrack() 283 | self.osm.track_add(self.t) 284 | self.t.props.color.red=0 285 | self.t.props.color.blue=1 286 | print "points for %s" % station 287 | for i in points: 288 | print i[0], i[1] 289 | #p = osmgpsmap.point_new_degrees(float(i[0]),float(i[1])) 290 | #self.t.add_point(p) 291 | model = widget.get_model() 292 | plot_line(model[row][0], 0) 293 | 294 | def on_query_tooltip(self, widget, x, y, keyboard_tip, tooltip, data=None): 295 | if keyboard_tip: 296 | return False 297 | 298 | if self.show_tooltips: 299 | p = osmgpsmap.point_new_degrees(0.0, 0.0) 300 | self.osm.convert_screen_to_geographic(x, y, p) 301 | lat,lon = p.get_degrees() 302 | tooltip.set_markup("%+.4f, %+.4f" % p.get_degrees()) 303 | return True 304 | 305 | return False 306 | 307 | def cache_clicked(self, button): 308 | bbox = self.osm.get_bbox() 309 | self.osm.download_maps( 310 | *bbox, 311 | zoom_start=self.osm.props.zoom, 312 | zoom_end=self.osm.props.max_zoom 313 | ) 314 | 315 | def map_clicked(self, osm, event): 316 | lat,lon = self.osm.get_event_location(event).get_degrees() 317 | if event.button == 1: 318 | self.latlon_entry.set_text( 319 | 'Map Centre: latitude %s longitude %s' % ( 320 | self.osm.props.latitude, 321 | self.osm.props.longitude 322 | ) 323 | ) 324 | elif event.button == 2: 325 | self.osm.gps_add(lat, lon, heading=osmgpsmap.INVALID); 326 | elif event.button == 3: 327 | pb = gtk.gdk.pixbuf_new_from_file_at_size ("poi.png", 24,24) 328 | self.osm.image_add(lat,lon,pb) 329 | 330 | if __name__ == "__main__": 331 | 332 | db = Database() 333 | u = UI() 334 | u.show_all() 335 | if os.name == "nt": gtk.gdk.threads_enter() 336 | gtk.main() 337 | if os.name == "nt": gtk.gdk.threads_leave() 338 | 339 | # vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 340 | -------------------------------------------------------------------------------- /src/mapviewer.c: -------------------------------------------------------------------------------- 1 | /* -*- Mode: C; indent-tabs-mode: nil; c-basic-offset: 4; tab-width: 4 -*- */ 2 | /* vim:set et sw=4 ts=4 cino=t0,(0: */ 3 | /* 4 | * main.c 5 | * Copyright (C) John Stowers 2008 6 | * 7 | * This is free software: you can redistribute it and/or modify it 8 | * under the terms of the GNU General Public License 9 | * as published by the Free Software Foundation; version 2. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program; if not, see . 18 | */ 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | 28 | #include 29 | #include 30 | #include 31 | 32 | #include "aprsis.h" 33 | #include "station.h" 34 | #include "mapviewer.h" 35 | #include "callbacks.h" 36 | 37 | OsmGpsMap *map; 38 | GtkEntry *latent; 39 | GtkEntry *lonent; 40 | GtkEntry *rangeent; 41 | GtkWidget *about; 42 | GtkWidget *popup; 43 | GtkComboBox *server; 44 | sqlite3 *db; 45 | int rc; 46 | 47 | GdkPixbuf *g_star_image = NULL; 48 | cairo_surface_t *g_symbol_image = NULL; 49 | cairo_surface_t *g_symbol_image2 = NULL; 50 | OsmGpsMapImage *g_last_image = NULL; 51 | GtkStatusbar *statusbar = NULL; 52 | 53 | GHashTable *stations; 54 | 55 | //function that lets us define the values in the aprs_details 56 | aprs_details *aprs_details_new(double lat,double lon,int range,aprsis_ctx *ctx) { 57 | 58 | aprs_details *details = calloc(1, sizeof(aprs_details)); 59 | 60 | details->lat = lat; 61 | details->lon = lon; 62 | details->range = range; 63 | details->ctx = ctx; 64 | 65 | return details; 66 | } 67 | 68 | static OsmGpsMapSource_t opt_map_provider = OSM_GPS_MAP_SOURCE_OPENSTREETMAP; 69 | static gboolean opt_friendly_cache = FALSE; 70 | static gboolean opt_no_cache = FALSE; 71 | static gboolean opt_debug = FALSE; 72 | static char *opt_cache_base_dir = NULL; 73 | static char *packet_log_file = NULL; 74 | static char *aprsis_server = NULL; 75 | static char *aprsis_port = NULL; 76 | 77 | static guint st_ctx; 78 | 79 | static GOptionEntry entries[] = 80 | { 81 | { "friendly-cache", 'f', 0, G_OPTION_ARG_NONE, &opt_friendly_cache, "Store maps using friendly cache style (source name)", NULL }, 82 | { "no-cache", 'n', 0, G_OPTION_ARG_NONE, &opt_no_cache, "Disable cache", NULL }, 83 | { "cache-basedir", 'b', 0, G_OPTION_ARG_FILENAME, &opt_cache_base_dir, "Cache basedir", NULL }, 84 | { "debug", 'd', 0, G_OPTION_ARG_NONE, &opt_debug, "Enable debugging", NULL }, 85 | { "map", 'm', 0, G_OPTION_ARG_INT, &opt_map_provider, "Map source", "N" }, 86 | { "aprsis-server", 's', 0, G_OPTION_ARG_STRING, &aprsis_server, "APRS-IS server", "HOST" }, 87 | { "aprsis-port", 'p', 0, G_OPTION_ARG_STRING, &aprsis_port, "APRS-IS port number", "PORT" }, 88 | { "packet-log-file", 'l', 0, G_OPTION_ARG_FILENAME, &packet_log_file, "Log network IO to a file", "FILE" }, 89 | { NULL } 90 | }; 91 | 92 | 93 | 94 | static gboolean *aprsmap_clear_status() { 95 | // remove message from the statusbar when the timer runs out 96 | gtk_statusbar_pop(statusbar, st_ctx); 97 | return FALSE; 98 | } 99 | 100 | 101 | void aprsmap_set_status(gchar *msg) { 102 | // put new message on the status bar 103 | gtk_statusbar_pop(statusbar, st_ctx); 104 | gtk_statusbar_push(statusbar, st_ctx, msg); 105 | g_timeout_add_seconds(3, (GSourceFunc)aprsmap_clear_status, NULL); 106 | } 107 | 108 | 109 | static void 110 | usage (GOptionContext *context) 111 | { 112 | int i; 113 | 114 | puts(g_option_context_get_help(context, TRUE, NULL)); 115 | 116 | printf("Valid map sources:\n"); 117 | for(i=OSM_GPS_MAP_SOURCE_NULL; i <= OSM_GPS_MAP_SOURCE_LAST; i++) 118 | { 119 | const char *name = osm_gps_map_source_get_friendly_name(i); 120 | const char *uri = osm_gps_map_source_get_repo_uri(i); 121 | if (uri != NULL) 122 | printf("\t%d:\t%s\n",i,name); 123 | } 124 | } 125 | 126 | int 127 | main (int argc, char **argv) 128 | { 129 | GtkBuilder *builder; 130 | GtkWidget *widget; 131 | GtkAccelGroup *ag; 132 | OsmGpsMapLayer *osd; 133 | const char *repo_uri; 134 | char *cachedir, *cachebasedir; 135 | GError *error = NULL; 136 | GOptionContext *context; 137 | char *zErrMsg = 0; 138 | //create and/or open sqlite3db 139 | rc = sqlite3_open("aprs.db", &db); 140 | if( rc ){ 141 | fprintf(stderr, "Can't open database: %s\n",sqlite3_errmsg(db)); 142 | sqlite3_close(db); 143 | exit(1); 144 | } 145 | //create and/or open table 146 | rc = sqlite3_exec(db, "CREATE TABLE IF NOT EXISTS call_data (call TEXT, object TEXT, course NUMERIC, lat NUMERIC, lon NUMERIC, time NUMERIC)", callback, 0, &zErrMsg); 147 | if( rc!=SQLITE_OK ){ 148 | fprintf(stderr, "SQL error: %s\n", zErrMsg); 149 | sqlite3_free(zErrMsg); 150 | } 151 | 152 | context = g_option_context_new ("- Map browser"); 153 | g_option_context_set_help_enabled(context, FALSE); 154 | g_option_context_add_main_entries (context, entries, NULL); 155 | 156 | if (!g_option_context_parse (context, &argc, &argv, &error)) { 157 | usage(context); 158 | return 1; 159 | } 160 | 161 | if (aprsis_server == NULL) { 162 | aprsis_server = strdup("euro.aprs2.net"); 163 | } 164 | 165 | if (aprsis_port == 0) { 166 | aprsis_port = strdup("14580"); 167 | } 168 | 169 | aprsis_ctx *ctx = aprsis_new(aprsis_server, aprsis_port, "aprsmap", "-1"); 170 | //aprsis_ctx *ctx = aprsis_new("localhost", "14580", "aprsmap", "-1"); 171 | 172 | //set variables properties->lat, properties->lon, properties->range, properties->ctx 173 | aprs_details *properties = aprs_details_new(55.00,-4.00,600,ctx); 174 | 175 | if (packet_log_file != NULL) { 176 | FILE *log = fopen(packet_log_file, "w"); 177 | aprsis_set_log(ctx, log); 178 | } 179 | 180 | aprsis_set_filter(properties->ctx, properties->lat,properties->lon,properties->range); 181 | gtk_init (&argc, &argv); 182 | 183 | // initialise APRS parser 184 | fap_init(); 185 | 186 | // connect to APRS_IS server 187 | start_aprsis(ctx); 188 | 189 | /* Only use the repo_uri to check if the user has supplied a 190 | valid map source ID */ 191 | repo_uri = osm_gps_map_source_get_repo_uri(opt_map_provider); 192 | if ( repo_uri == NULL ) { 193 | usage(context); 194 | return 2; 195 | } 196 | 197 | cachebasedir = osm_gps_map_get_default_cache_directory(); 198 | 199 | if (opt_cache_base_dir && g_file_test(opt_cache_base_dir, G_FILE_TEST_IS_DIR)) { 200 | cachedir = g_strdup(OSM_GPS_MAP_CACHE_AUTO); 201 | cachebasedir = g_strdup(opt_cache_base_dir); 202 | } else if (opt_friendly_cache) { 203 | cachedir = g_strdup(OSM_GPS_MAP_CACHE_FRIENDLY); 204 | } else if (opt_no_cache) { 205 | cachedir = g_strdup(OSM_GPS_MAP_CACHE_DISABLED); 206 | } else { 207 | cachedir = g_strdup(OSM_GPS_MAP_CACHE_AUTO); 208 | } 209 | 210 | if (opt_debug) 211 | gdk_window_set_debug_updates(TRUE); 212 | 213 | 214 | g_debug("Map Cache Dir: %s", cachedir); 215 | g_debug("Map Provider: %s (%d)", osm_gps_map_source_get_friendly_name(opt_map_provider), opt_map_provider); 216 | 217 | map = g_object_new (OSM_TYPE_GPS_MAP, 218 | "map-source",opt_map_provider, 219 | "tile-cache",cachedir, 220 | "tile-cache-base", cachebasedir, 221 | "proxy-uri",g_getenv("http_proxy"), 222 | NULL); 223 | 224 | osd = g_object_new (OSM_TYPE_GPS_MAP_OSD, 225 | "show-scale",TRUE, 226 | "show-coordinates",TRUE, 227 | NULL); 228 | osm_gps_map_layer_add(OSM_GPS_MAP(map), osd); 229 | g_object_unref(G_OBJECT(osd)); 230 | 231 | g_free(cachedir); 232 | g_free(cachebasedir); 233 | 234 | //Enable keyboard navigation 235 | osm_gps_map_set_keyboard_shortcut(map, OSM_GPS_MAP_KEY_FULLSCREEN, GDK_F11); 236 | osm_gps_map_set_keyboard_shortcut(map, OSM_GPS_MAP_KEY_UP, GDK_Up); 237 | osm_gps_map_set_keyboard_shortcut(map, OSM_GPS_MAP_KEY_DOWN, GDK_Down); 238 | osm_gps_map_set_keyboard_shortcut(map, OSM_GPS_MAP_KEY_LEFT, GDK_Left); 239 | osm_gps_map_set_keyboard_shortcut(map, OSM_GPS_MAP_KEY_RIGHT, GDK_Right); 240 | 241 | //Build the UI 242 | g_symbol_image = cairo_image_surface_create_from_png("allicons.png"); //, &error); 243 | g_symbol_image2 = cairo_image_surface_create_from_png("allicon2.png"); //, &error); 244 | 245 | stations = g_hash_table_new(g_str_hash, g_str_equal); 246 | 247 | 248 | builder = gtk_builder_new(); 249 | gtk_builder_add_from_file (builder, "mapviewer.ui", &error); 250 | if (error) 251 | g_error ("ERROR: %s\n", error->message); 252 | 253 | gtk_box_pack_start ( 254 | GTK_BOX(gtk_builder_get_object(builder, "map_box")), 255 | GTK_WIDGET(map), TRUE, TRUE, 0); 256 | 257 | // centre on the latitude and longitude set in the properties menu 258 | osm_gps_map_set_center_and_zoom(map, properties->lat,properties->lon, 5); 259 | 260 | //Connect to signals that need data passed to them 261 | 262 | g_signal_connect ( 263 | gtk_builder_get_object(builder, "home_button"), "clicked", 264 | G_CALLBACK (on_home_clicked_event), properties); 265 | g_signal_connect ( 266 | gtk_builder_get_object(builder, "homemenuitem"), "activate", 267 | G_CALLBACK (on_home_clicked_event), properties); 268 | g_signal_connect ( 269 | gtk_builder_get_object(builder, "sethomemenuitem"), "activate", 270 | G_CALLBACK (on_set_home_activate_event), properties); 271 | g_signal_connect ( 272 | gtk_builder_get_object(builder, "okPrefs"), "clicked", 273 | G_CALLBACK (on_properties_ok_clicked), properties); 274 | g_signal_connect ( 275 | gtk_builder_get_object(builder, "prefs_button"), "clicked", 276 | G_CALLBACK (on_properties_clicked_event), properties); 277 | g_signal_connect (G_OBJECT (map), "button-press-event", 278 | G_CALLBACK (on_button_press_event), (gpointer) map); 279 | 280 | widget = GTK_WIDGET(gtk_builder_get_object(builder, "window1")); 281 | 282 | g_signal_connect (widget, "destroy", 283 | G_CALLBACK (on_close), (gpointer) map); 284 | 285 | //pulls popup data from mapviewer.ui 286 | 287 | popup = GTK_WIDGET(gtk_builder_get_object(builder, "proppop")); 288 | 289 | about = GTK_WIDGET(gtk_builder_get_object(builder, "about")); 290 | 291 | //connect mapviewer.ui values to popup window 292 | gtk_builder_connect_signals(builder, popup); 293 | gtk_builder_connect_signals(builder, about); 294 | 295 | //Setup accelerators. 296 | ag = gtk_accel_group_new(); 297 | gtk_accel_group_connect(ag, GDK_w, GDK_CONTROL_MASK, GTK_ACCEL_MASK, 298 | g_cclosure_new(gtk_main_quit, NULL, NULL)); 299 | gtk_accel_group_connect(ag, GDK_q, GDK_CONTROL_MASK, GTK_ACCEL_MASK, 300 | g_cclosure_new(gtk_main_quit, NULL, NULL)); 301 | gtk_window_add_accel_group(GTK_WINDOW(widget), ag); 302 | 303 | statusbar = GTK_STATUSBAR(gtk_builder_get_object(builder, "statusbar1")); 304 | st_ctx = gtk_statusbar_get_context_id(statusbar, "connect"); 305 | 306 | //Set up GTK_ENTRY boxes in the preferences pop up 307 | latent = GTK_ENTRY(gtk_builder_get_object(builder, "declat")); 308 | lonent = GTK_ENTRY(gtk_builder_get_object(builder, "declon")); 309 | rangeent = GTK_ENTRY(gtk_builder_get_object(builder, "range")); 310 | gtk_entry_set_text(latent, g_strdup_printf("%f",properties->lat)); 311 | gtk_entry_set_text(lonent, g_strdup_printf("%f",properties->lon)); 312 | gtk_entry_set_text(rangeent, g_strdup_printf("%d",properties->range)); 313 | 314 | g_object_unref( G_OBJECT( builder ) ); 315 | 316 | gtk_widget_show_all (widget); 317 | 318 | //g_log_set_handler ("OsmGpsMap", G_LOG_LEVEL_MASK, g_log_default_handler, NULL); 319 | g_log_set_handler ("OsmGpsMap", G_LOG_LEVEL_MESSAGE, g_log_default_handler, NULL); 320 | aprsis_set_filter(properties->ctx, properties->lat,properties->lon,properties->range); 321 | printf("Filter Data Set\n"); 322 | gtk_main (); 323 | 324 | sqlite3_close(db); 325 | 326 | fap_cleanup(); 327 | aprsis_close(ctx); 328 | 329 | return(0); 330 | } 331 | 332 | /* vim: set noexpandtab ai ts=4 sw=4 tw=4: */ 333 | -------------------------------------------------------------------------------- /src/station.c: -------------------------------------------------------------------------------- 1 | // station.c 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include "station.h" 12 | #include "mapviewer.h" 13 | 14 | extern GdkPixbuf *g_star_image; 15 | extern cairo_surface_t *g_symbol_image; 16 | extern cairo_surface_t *g_symbol_image2; 17 | 18 | extern GHashTable *stations; 19 | extern OsmGpsMap *map; 20 | extern int rc; 21 | extern sqlite3 *db; 22 | // workaround for libfap bug 23 | /// The magic constant. 24 | #define PI 3.14159265 25 | /// Degrees to radians. 26 | #define DEG2RAD(x) (x/360*2*PI) 27 | /// Radians to degrees. 28 | #define RAD2DEG(x) (x*(180/PI)) 29 | 30 | char *zErrMsg = 0; 31 | 32 | char *packet_type[] = { 33 | 34 | "LOCATION", 35 | "OBJECT", 36 | "ITEM", 37 | "MICE", 38 | "NMEA", 39 | "WX", 40 | "MESSAGE", 41 | "CAPABILITIES", 42 | "STATUS", 43 | "TELEMETRY", 44 | "TELEMETRY_MESSAGE", 45 | "DX_SPOT", 46 | "EXPERIMENTAL" 47 | 48 | } ; 49 | 50 | static void 51 | convert_alpha (guchar *dest_data, 52 | int dest_stride, 53 | guchar *src_data, 54 | int src_stride, 55 | int src_x, 56 | int src_y, 57 | int width, 58 | int height) 59 | { 60 | int x, y; 61 | 62 | src_data += src_stride * src_y + src_x * 4; 63 | 64 | for (y = 0; y < height; y++) { 65 | guint32 *src = (guint32 *) src_data; 66 | 67 | for (x = 0; x < width; x++) { 68 | guint alpha = src[x] >> 24; 69 | 70 | if (alpha == 0) 71 | { 72 | dest_data[x * 4 + 0] = 0; 73 | dest_data[x * 4 + 1] = 0; 74 | dest_data[x * 4 + 2] = 0; 75 | } 76 | else 77 | { 78 | dest_data[x * 4 + 0] = (((src[x] & 0xff0000) >> 16) * 255 + alpha / 2) / alpha; 79 | dest_data[x * 4 + 1] = (((src[x] & 0x00ff00) >> 8) * 255 + alpha / 2) / alpha; 80 | dest_data[x * 4 + 2] = (((src[x] & 0x0000ff) >> 0) * 255 + alpha / 2) / alpha; 81 | } 82 | dest_data[x * 4 + 3] = alpha; 83 | } 84 | 85 | src_data += src_stride; 86 | dest_data += dest_stride; 87 | } 88 | } 89 | 90 | static void aprsmap_set_icon(fap_packet_t *packet, APRSMapStation *station) { 91 | // get the icon given a packet to get the symbol from 92 | cairo_t *cr; 93 | guint c; 94 | gdouble xo, yo; 95 | gdouble angle; 96 | 97 | if (!station->icon) { 98 | station->icon = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, 22, 22); 99 | } 100 | 101 | cr = cairo_create(station->icon); 102 | 103 | // calculate the symbol image 104 | // this fails on symbols that aren't on the \ or / table 105 | // they default to the / table, probably wrongly ;-) 106 | c = packet->symbol_code-32; 107 | yo = (gdouble)((c*16)%256); 108 | xo = (gdouble)(c &0xf0); 109 | 110 | if (station->course) { 111 | cairo_translate(cr, 8, 8); 112 | if (station->course > 180) { 113 | cairo_scale(cr, -1, 1); 114 | angle = 270.0f - station->course; 115 | } else { 116 | angle = station->course - 90.0f; 117 | } 118 | cairo_rotate(cr, DEG2RAD(angle)); 119 | cairo_translate(cr, -8, -8); 120 | } 121 | 122 | if (packet->symbol_table == '\\') { 123 | cairo_set_source_surface (cr, g_symbol_image2, 1-xo, 1-yo); 124 | 125 | } else { 126 | cairo_set_source_surface (cr, g_symbol_image, 1-xo, 1-yo); 127 | } 128 | 129 | cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE); 130 | cairo_rectangle (cr, 1, 1, 16, 16); 131 | //cairo_clip(cr); 132 | 133 | cairo_fill (cr); 134 | cairo_surface_flush(station->icon); 135 | cairo_destroy(cr); 136 | } 137 | 138 | static void aprsmap_get_label(fap_packet_t *packet, APRSMapStation *station) { 139 | // return the symbol pixbuf 140 | 141 | guint width=90, height=22; 142 | 143 | //gdouble xo, yo; 144 | //guint c; 145 | cairo_t *cr; 146 | cairo_text_extents_t extent; 147 | cairo_surface_t *surface; 148 | //cairo_content_t content; 149 | //GdkPixbuf *dest; 150 | 151 | if (packet->symbol_table && packet->symbol_code) { 152 | surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width, height); 153 | cr = cairo_create(surface); 154 | 155 | // get callsign/name size 156 | cairo_select_font_face(cr, "Sans", 157 | CAIRO_FONT_SLANT_NORMAL, 158 | CAIRO_FONT_WEIGHT_NORMAL); 159 | cairo_set_font_size(cr, 12); 160 | cairo_text_extents(cr, station->callsign, &extent); 161 | 162 | // draw background 163 | cairo_arc(cr, 6.5f, 6.5f, 6, 3.14, 4.71); 164 | cairo_arc(cr, 24+extent.width-6.5f, 6.5f, 6, 4.71, 6.28); 165 | cairo_arc(cr, 24+extent.width-6.5f, height-6.5f, 6, 0, 1.57); 166 | cairo_arc(cr, 6.5f, height-6.5f, 6, 1.57, 3.14); 167 | cairo_close_path(cr); 168 | 169 | cairo_set_source_rgba(cr, 1, 1, 1, .75); 170 | cairo_set_source_rgba(cr, .85, .85, .85, .75); 171 | cairo_fill_preserve(cr); 172 | 173 | //cairo_set_source_rgba(cr, .5, .5, .5, 1); 174 | cairo_set_source_rgba(cr, 1, 1, 1, 1); 175 | cairo_set_line_width(cr, 2.0f); 176 | cairo_stroke(cr); 177 | 178 | // draw the icon 179 | aprsmap_set_icon(packet, station); 180 | cairo_set_source_surface(cr, station->icon, 2, 2); 181 | cairo_paint(cr); 182 | 183 | // draw the callsign 184 | cairo_move_to(cr, 20, height-1-(height-extent.height)/2); 185 | cairo_set_source_rgba(cr, 0, 0, 0, 1); 186 | cairo_show_text(cr, station->callsign); 187 | 188 | // munge it into a pixbuf for OsmGpsMapImage 189 | cairo_surface_flush(surface); 190 | //content = cairo_surface_get_content(surface); 191 | if (!station->pix) station->pix = gdk_pixbuf_new(GDK_COLORSPACE_RGB, TRUE, 8, width, height); 192 | 193 | convert_alpha (gdk_pixbuf_get_pixels (station->pix), 194 | gdk_pixbuf_get_rowstride (station->pix), 195 | cairo_image_surface_get_data (surface), 196 | cairo_image_surface_get_stride (surface), 197 | 0, 0, 198 | width, height); 199 | 200 | cairo_surface_destroy(surface); 201 | cairo_destroy(cr); 202 | } 203 | } 204 | 205 | 206 | /* 207 | static gboolean aprsmap_station_moved(fap_packet_t *packet, APRSMapStation *station) { 208 | // has the station moved? 209 | if (!packet->latitude) return FALSE; // don't know if it's moved; nothing to tell us it has 210 | if (station->fix == APRS_NOFIX) return TRUE; // if it's got a latitude we now have a fix 211 | 212 | // if there was a previous fix and this packet contains a position, compare 213 | if (*(packet->latitude) != station->lat) return TRUE; 214 | if (*(packet->longitude) != station->lon) return TRUE; 215 | return FALSE; 216 | } 217 | */ 218 | static APRSMapStation* get_station(fap_packet_t *packet) { 219 | // return either a new station, or an existing one 220 | char name[10]; 221 | APRSMapStation *station; 222 | 223 | // objects and items are sent from a callsign, but have a name 224 | // which is passed as part of the payload 225 | bzero(&name, 10); 226 | switch (*(packet->type)) { 227 | case fapOBJECT: 228 | case fapITEM: 229 | strncpy(name, packet->object_or_item_name, 9); 230 | break; 231 | default: 232 | strncpy(name, packet->src_callsign, 9); 233 | break; 234 | } 235 | station = g_hash_table_lookup(stations, name); 236 | if (!station) { 237 | // printf("new station %s\n", name); 238 | aprsmap_set_status(g_strdup_printf("New station %s...", name)); 239 | station = g_new0(APRSMapStation, 1); 240 | station->callsign = g_strdup(name); 241 | } 242 | return station; 243 | } 244 | 245 | double gjcp_direction(double lon0, double lat0, double lon1, double lat1) 246 | { 247 | double direction; 248 | 249 | /* Convert degrees into radians. */ 250 | lon0 = DEG2RAD(lon0); 251 | lat0 = DEG2RAD(lat0); 252 | lon1 = DEG2RAD(lon1); 253 | lat1 = DEG2RAD(lat1); 254 | 255 | /* Direction from Aviation Formulary V1.42 by Ed Williams by way of 256 | * http://mathforum.org/library/drmath/view/55417.html */ 257 | direction = atan2(sin(lon1-lon0)*cos(lat1), cos(lat0)*sin(lat1)-sin(lat0)*cos(lat1)*cos(lon1-lon0)); 258 | 259 | if ( direction < 0 ) 260 | { 261 | /* Make direction positive. */ 262 | direction += 2 * PI; 263 | } 264 | return RAD2DEG(direction); 265 | } 266 | 267 | 268 | 269 | static void position_station(APRSMapStation *station, fap_packet_t *packet) { 270 | // deal with position packets 271 | OsmGpsMapPoint pt; 272 | if (station->fix == APRS_VALIDFIX) { 273 | // printf("co-ordinates: %f %f\n", station->lat, station->lon); 274 | if ((station->lat != *(packet->latitude)) || (station->lon != *(packet->longitude))) { 275 | // printf("it moved\n"); 276 | // not all APRS packets contain speed and course 277 | // we can work it out though 278 | station->course = gjcp_direction(station->lon, station->lat, *(packet->longitude), *(packet->latitude)); 279 | station->lat = *(packet->latitude); 280 | station->lon = *(packet->longitude); 281 | char test[80]; 282 | struct tm *ts; 283 | ts = localtime(packet->timestamp); 284 | if (ts == NULL) { 285 | printf("Failed to parse timestamp %ldl: errno is %d\n", (long)packet->timestamp, errno); 286 | return; 287 | } 288 | strftime(test, sizeof(test), "%a %Y-%m-%d %H:%M:%S %Z", ts); 289 | printf("%s\n", test ); 290 | if (station->lat && station->lon && packet->src_callsign) { 291 | write_to_db(station->lat, station->lon, station->course, 292 | packet->src_callsign, packet->object_or_item_name, packet->timestamp); 293 | 294 | printf("NotFail\n"); } 295 | else { 296 | printf("Fail\n"); 297 | } 298 | 299 | // we may need to create a track, then 300 | if (!station->track) { 301 | station->track = osm_gps_map_track_new(); 302 | osm_gps_map_point_set_degrees (&pt, station->lat, station->lon); 303 | osm_gps_map_track_add_point(station->track, &pt); 304 | osm_gps_map_track_add(OSM_GPS_MAP(map), station->track); 305 | } else { 306 | // already got a track 307 | osm_gps_map_image_remove(map, station->image); 308 | osm_gps_map_point_set_degrees (&pt, station->lat, station->lon); 309 | osm_gps_map_track_add_point(station->track, &pt); 310 | aprsmap_get_label(packet, station); 311 | station->image = osm_gps_map_image_add(map, station->lat, station->lon, station->pix); 312 | g_object_set (station->image, "x-align", 0.0f, NULL); 313 | } 314 | } 315 | 316 | } else { 317 | // printf("first position packet received for this station\n"); 318 | if (packet->latitude) { 319 | station->lat = *(packet->latitude); 320 | station->lon = *(packet->longitude); 321 | if (packet->course) station->course = *(packet->course); 322 | station->fix = APRS_VALIDFIX; 323 | aprsmap_get_label(packet, station); 324 | if (station->pix) { 325 | station->image = osm_gps_map_image_add(map,*(packet->latitude), *(packet->longitude), station->pix); 326 | g_object_set (station->image, "x-align", 0.0f, NULL); 327 | } else { 328 | g_error("not really an error, just checking to see if we ever get a posit with no symbol"); 329 | } 330 | } else { 331 | g_message("got a position packet, with no valid position. ignoring."); 332 | } 333 | } 334 | 335 | if (station->lat && station->lon && packet->src_callsign) { 336 | if (packet->timestamp) { 337 | char test[80]; 338 | struct tm *ts; 339 | ts = localtime(packet->timestamp); 340 | strftime(test, sizeof(test), "%a %Y-%m-%d %H:%M:%S %Z", ts); 341 | printf("%s\n", test ); 342 | } 343 | write_to_db(station->lat, station->lon, station->course, packet->src_callsign, 344 | packet->object_or_item_name, packet->timestamp); 345 | 346 | } 347 | } 348 | gboolean process_packet(gchar *msg) { 349 | // process an incoming packet, and call a suitable function 350 | fap_packet_t *packet; 351 | fap_packet_type_t type; 352 | APRSMapStation *station; 353 | char errmsg[256]; 354 | //char name[10]; 355 | 356 | packet = fap_parseaprs(msg, strlen(msg), 0); 357 | if (packet->error_code) { 358 | fap_explain_error(*packet->error_code, errmsg); 359 | g_message("%s", errmsg); 360 | return TRUE; 361 | } 362 | 363 | type = *(packet->type); 364 | printf("%s, %s packet\n", packet->src_callsign, packet_type[type]); 365 | 366 | // see if we have a record of this, or not 367 | station = get_station(packet); 368 | 369 | switch(type) { 370 | case fapOBJECT: 371 | case fapITEM: 372 | case fapLOCATION: 373 | position_station(station, packet); 374 | break; 375 | default: 376 | printf("unhandled\n"); 377 | break; 378 | } 379 | g_hash_table_replace(stations, station->callsign, station); 380 | return 0; 381 | } 382 | void write_to_db(gdouble latitude, gdouble longitude, float course, char *call, char 383 | *object, time_t *timestamp) { 384 | 385 | char zlat[20]; char zlon[20]; char zcourse[10]; char ztime[80]; 386 | //int n, m, o, t,rc; 387 | int rc; 388 | /* 389 | n=sprintf(zlat,"%f",latitude); 390 | m=sprintf(zlon,"%f",longitude); 391 | o=sprintf(zcourse, "%f", course); 392 | t=sprintf(ztime, "%ld", (long)timestamp); 393 | */ 394 | char *zSQL = sqlite3_mprintf("INSERT INTO call_data (call, object, course, lon, lat, time) VALUES (%Q,%Q,%Q,%Q,%Q,%Q)",call,object,zcourse,zlon,zlat,ztime); 395 | rc = sqlite3_exec(db, zSQL, 0, 0, &zErrMsg); 396 | if( rc!=SQLITE_OK ){ 397 | fprintf(stderr, "SQL error: %s\n", zErrMsg); 398 | sqlite3_free(zErrMsg); 399 | } 400 | sqlite3_free(zSQL); 401 | } 402 | /* vim: set noexpandtab ai ts=4 sw=4 tw=4: */ 403 | -------------------------------------------------------------------------------- /doc/LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | Preamble 9 | 10 | The GNU General Public License is a free, copyleft license for 11 | software and other kinds of works. 12 | 13 | The licenses for most software and other practical works are designed 14 | to take away your freedom to share and change the works. By contrast, 15 | the GNU General Public License is intended to guarantee your freedom to 16 | share and change all versions of a program--to make sure it remains free 17 | software for all its users. We, the Free Software Foundation, use the 18 | GNU General Public License for most of our software; it applies also to 19 | any other work released this way by its authors. You can apply it to 20 | your programs, too. 21 | 22 | When we speak of free software, we are referring to freedom, not 23 | price. Our General Public Licenses are designed to make sure that you 24 | have the freedom to distribute copies of free software (and charge for 25 | them if you wish), that you receive source code or can get it if you 26 | want it, that you can change the software or use pieces of it in new 27 | free programs, and that you know you can do these things. 28 | 29 | To protect your rights, we need to prevent others from denying you 30 | these rights or asking you to surrender the rights. Therefore, you have 31 | certain responsibilities if you distribute copies of the software, or if 32 | you modify it: responsibilities to respect the freedom of others. 33 | 34 | For example, if you distribute copies of such a program, whether 35 | gratis or for a fee, you must pass on to the recipients the same 36 | freedoms that you received. You must make sure that they, too, receive 37 | or can get the source code. And you must show them these terms so they 38 | know their rights. 39 | 40 | Developers that use the GNU GPL protect your rights with two steps: 41 | (1) assert copyright on the software, and (2) offer you this License 42 | giving you legal permission to copy, distribute and/or modify it. 43 | 44 | For the developers' and authors' protection, the GPL clearly explains 45 | that there is no warranty for this free software. For both users' and 46 | authors' sake, the GPL requires that modified versions be marked as 47 | changed, so that their problems will not be attributed erroneously to 48 | authors of previous versions. 49 | 50 | Some devices are designed to deny users access to install or run 51 | modified versions of the software inside them, although the manufacturer 52 | can do so. This is fundamentally incompatible with the aim of 53 | protecting users' freedom to change the software. The systematic 54 | pattern of such abuse occurs in the area of products for individuals to 55 | use, which is precisely where it is most unacceptable. Therefore, we 56 | have designed this version of the GPL to prohibit the practice for those 57 | products. If such problems arise substantially in other domains, we 58 | stand ready to extend this provision to those domains in future versions 59 | of the GPL, as needed to protect the freedom of users. 60 | 61 | Finally, every program is threatened constantly by software patents. 62 | States should not allow patents to restrict development and use of 63 | software on general-purpose computers, but in those that do, we wish to 64 | avoid the special danger that patents applied to a free program could 65 | make it effectively proprietary. To prevent this, the GPL assures that 66 | patents cannot be used to render the program non-free. 67 | 68 | The precise terms and conditions for copying, distribution and 69 | modification follow. 70 | 71 | TERMS AND CONDITIONS 72 | 73 | 0. Definitions. 74 | 75 | "This License" refers to version 3 of the GNU General Public License. 76 | 77 | "Copyright" also means copyright-like laws that apply to other kinds of 78 | works, such as semiconductor masks. 79 | 80 | "The Program" refers to any copyrightable work licensed under this 81 | License. Each licensee is addressed as "you". "Licensees" and 82 | "recipients" may be individuals or organizations. 83 | 84 | To "modify" a work means to copy from or adapt all or part of the work 85 | in a fashion requiring copyright permission, other than the making of an 86 | exact copy. The resulting work is called a "modified version" of the 87 | earlier work or a work "based on" the earlier work. 88 | 89 | A "covered work" means either the unmodified Program or a work based 90 | on the Program. 91 | 92 | To "propagate" a work means to do anything with it that, without 93 | permission, would make you directly or secondarily liable for 94 | infringement under applicable copyright law, except executing it on a 95 | computer or modifying a private copy. Propagation includes copying, 96 | distribution (with or without modification), making available to the 97 | public, and in some countries other activities as well. 98 | 99 | To "convey" a work means any kind of propagation that enables other 100 | parties to make or receive copies. Mere interaction with a user through 101 | a computer network, with no transfer of a copy, is not conveying. 102 | 103 | An interactive user interface displays "Appropriate Legal Notices" 104 | to the extent that it includes a convenient and prominently visible 105 | feature that (1) displays an appropriate copyright notice, and (2) 106 | tells the user that there is no warranty for the work (except to the 107 | extent that warranties are provided), that licensees may convey the 108 | work under this License, and how to view a copy of this License. If 109 | the interface presents a list of user commands or options, such as a 110 | menu, a prominent item in the list meets this criterion. 111 | 112 | 1. Source Code. 113 | 114 | The "source code" for a work means the preferred form of the work 115 | for making modifications to it. "Object code" means any non-source 116 | form of a work. 117 | 118 | A "Standard Interface" means an interface that either is an official 119 | standard defined by a recognized standards body, or, in the case of 120 | interfaces specified for a particular programming language, one that 121 | is widely used among developers working in that language. 122 | 123 | The "System Libraries" of an executable work include anything, other 124 | than the work as a whole, that (a) is included in the normal form of 125 | packaging a Major Component, but which is not part of that Major 126 | Component, and (b) serves only to enable use of the work with that 127 | Major Component, or to implement a Standard Interface for which an 128 | implementation is available to the public in source code form. A 129 | "Major Component", in this context, means a major essential component 130 | (kernel, window system, and so on) of the specific operating system 131 | (if any) on which the executable work runs, or a compiler used to 132 | produce the work, or an object code interpreter used to run it. 133 | 134 | The "Corresponding Source" for a work in object code form means all 135 | the source code needed to generate, install, and (for an executable 136 | work) run the object code and to modify the work, including scripts to 137 | control those activities. However, it does not include the work's 138 | System Libraries, or general-purpose tools or generally available free 139 | programs which are used unmodified in performing those activities but 140 | which are not part of the work. For example, Corresponding Source 141 | includes interface definition files associated with source files for 142 | the work, and the source code for shared libraries and dynamically 143 | linked subprograms that the work is specifically designed to require, 144 | such as by intimate data communication or control flow between those 145 | subprograms and other parts of the work. 146 | 147 | The Corresponding Source need not include anything that users 148 | can regenerate automatically from other parts of the Corresponding 149 | Source. 150 | 151 | The Corresponding Source for a work in source code form is that 152 | same work. 153 | 154 | 2. Basic Permissions. 155 | 156 | All rights granted under this License are granted for the term of 157 | copyright on the Program, and are irrevocable provided the stated 158 | conditions are met. This License explicitly affirms your unlimited 159 | permission to run the unmodified Program. The output from running a 160 | covered work is covered by this License only if the output, given its 161 | content, constitutes a covered work. This License acknowledges your 162 | rights of fair use or other equivalent, as provided by copyright law. 163 | 164 | You may make, run and propagate covered works that you do not 165 | convey, without conditions so long as your license otherwise remains 166 | in force. You may convey covered works to others for the sole purpose 167 | of having them make modifications exclusively for you, or provide you 168 | with facilities for running those works, provided that you comply with 169 | the terms of this License in conveying all material for which you do 170 | not control copyright. Those thus making or running the covered works 171 | for you must do so exclusively on your behalf, under your direction 172 | and control, on terms that prohibit them from making any copies of 173 | your copyrighted material outside their relationship with you. 174 | 175 | Conveying under any other circumstances is permitted solely under 176 | the conditions stated below. Sublicensing is not allowed; section 10 177 | makes it unnecessary. 178 | 179 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law. 180 | 181 | No covered work shall be deemed part of an effective technological 182 | measure under any applicable law fulfilling obligations under article 183 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or 184 | similar laws prohibiting or restricting circumvention of such 185 | measures. 186 | 187 | When you convey a covered work, you waive any legal power to forbid 188 | circumvention of technological measures to the extent such circumvention 189 | is effected by exercising rights under this License with respect to 190 | the covered work, and you disclaim any intention to limit operation or 191 | modification of the work as a means of enforcing, against the work's 192 | users, your or third parties' legal rights to forbid circumvention of 193 | technological measures. 194 | 195 | 4. Conveying Verbatim Copies. 196 | 197 | You may convey verbatim copies of the Program's source code as you 198 | receive it, in any medium, provided that you conspicuously and 199 | appropriately publish on each copy an appropriate copyright notice; 200 | keep intact all notices stating that this License and any 201 | non-permissive terms added in accord with section 7 apply to the code; 202 | keep intact all notices of the absence of any warranty; and give all 203 | recipients a copy of this License along with the Program. 204 | 205 | You may charge any price or no price for each copy that you convey, 206 | and you may offer support or warranty protection for a fee. 207 | 208 | 5. Conveying Modified Source Versions. 209 | 210 | You may convey a work based on the Program, or the modifications to 211 | produce it from the Program, in the form of source code under the 212 | terms of section 4, provided that you also meet all of these conditions: 213 | 214 | a) The work must carry prominent notices stating that you modified 215 | it, and giving a relevant date. 216 | 217 | b) The work must carry prominent notices stating that it is 218 | released under this License and any conditions added under section 219 | 7. This requirement modifies the requirement in section 4 to 220 | "keep intact all notices". 221 | 222 | c) You must license the entire work, as a whole, under this 223 | License to anyone who comes into possession of a copy. This 224 | License will therefore apply, along with any applicable section 7 225 | additional terms, to the whole of the work, and all its parts, 226 | regardless of how they are packaged. This License gives no 227 | permission to license the work in any other way, but it does not 228 | invalidate such permission if you have separately received it. 229 | 230 | d) If the work has interactive user interfaces, each must display 231 | Appropriate Legal Notices; however, if the Program has interactive 232 | interfaces that do not display Appropriate Legal Notices, your 233 | work need not make them do so. 234 | 235 | A compilation of a covered work with other separate and independent 236 | works, which are not by their nature extensions of the covered work, 237 | and which are not combined with it such as to form a larger program, 238 | in or on a volume of a storage or distribution medium, is called an 239 | "aggregate" if the compilation and its resulting copyright are not 240 | used to limit the access or legal rights of the compilation's users 241 | beyond what the individual works permit. Inclusion of a covered work 242 | in an aggregate does not cause this License to apply to the other 243 | parts of the aggregate. 244 | 245 | 6. Conveying Non-Source Forms. 246 | 247 | You may convey a covered work in object code form under the terms 248 | of sections 4 and 5, provided that you also convey the 249 | machine-readable Corresponding Source under the terms of this License, 250 | in one of these ways: 251 | 252 | a) Convey the object code in, or embodied in, a physical product 253 | (including a physical distribution medium), accompanied by the 254 | Corresponding Source fixed on a durable physical medium 255 | customarily used for software interchange. 256 | 257 | b) Convey the object code in, or embodied in, a physical product 258 | (including a physical distribution medium), accompanied by a 259 | written offer, valid for at least three years and valid for as 260 | long as you offer spare parts or customer support for that product 261 | model, to give anyone who possesses the object code either (1) a 262 | copy of the Corresponding Source for all the software in the 263 | product that is covered by this License, on a durable physical 264 | medium customarily used for software interchange, for a price no 265 | more than your reasonable cost of physically performing this 266 | conveying of source, or (2) access to copy the 267 | Corresponding Source from a network server at no charge. 268 | 269 | c) Convey individual copies of the object code with a copy of the 270 | written offer to provide the Corresponding Source. This 271 | alternative is allowed only occasionally and noncommercially, and 272 | only if you received the object code with such an offer, in accord 273 | with subsection 6b. 274 | 275 | d) Convey the object code by offering access from a designated 276 | place (gratis or for a charge), and offer equivalent access to the 277 | Corresponding Source in the same way through the same place at no 278 | further charge. You need not require recipients to copy the 279 | Corresponding Source along with the object code. If the place to 280 | copy the object code is a network server, the Corresponding Source 281 | may be on a different server (operated by you or a third party) 282 | that supports equivalent copying facilities, provided you maintain 283 | clear directions next to the object code saying where to find the 284 | Corresponding Source. Regardless of what server hosts the 285 | Corresponding Source, you remain obligated to ensure that it is 286 | available for as long as needed to satisfy these requirements. 287 | 288 | e) Convey the object code using peer-to-peer transmission, provided 289 | you inform other peers where the object code and Corresponding 290 | Source of the work are being offered to the general public at no 291 | charge under subsection 6d. 292 | 293 | A separable portion of the object code, whose source code is excluded 294 | from the Corresponding Source as a System Library, need not be 295 | included in conveying the object code work. 296 | 297 | A "User Product" is either (1) a "consumer product", which means any 298 | tangible personal property which is normally used for personal, family, 299 | or household purposes, or (2) anything designed or sold for incorporation 300 | into a dwelling. In determining whether a product is a consumer product, 301 | doubtful cases shall be resolved in favor of coverage. For a particular 302 | product received by a particular user, "normally used" refers to a 303 | typical or common use of that class of product, regardless of the status 304 | of the particular user or of the way in which the particular user 305 | actually uses, or expects or is expected to use, the product. A product 306 | is a consumer product regardless of whether the product has substantial 307 | commercial, industrial or non-consumer uses, unless such uses represent 308 | the only significant mode of use of the product. 309 | 310 | "Installation Information" for a User Product means any methods, 311 | procedures, authorization keys, or other information required to install 312 | and execute modified versions of a covered work in that User Product from 313 | a modified version of its Corresponding Source. The information must 314 | suffice to ensure that the continued functioning of the modified object 315 | code is in no case prevented or interfered with solely because 316 | modification has been made. 317 | 318 | If you convey an object code work under this section in, or with, or 319 | specifically for use in, a User Product, and the conveying occurs as 320 | part of a transaction in which the right of possession and use of the 321 | User Product is transferred to the recipient in perpetuity or for a 322 | fixed term (regardless of how the transaction is characterized), the 323 | Corresponding Source conveyed under this section must be accompanied 324 | by the Installation Information. But this requirement does not apply 325 | if neither you nor any third party retains the ability to install 326 | modified object code on the User Product (for example, the work has 327 | been installed in ROM). 328 | 329 | The requirement to provide Installation Information does not include a 330 | requirement to continue to provide support service, warranty, or updates 331 | for a work that has been modified or installed by the recipient, or for 332 | the User Product in which it has been modified or installed. Access to a 333 | network may be denied when the modification itself materially and 334 | adversely affects the operation of the network or violates the rules and 335 | protocols for communication across the network. 336 | 337 | Corresponding Source conveyed, and Installation Information provided, 338 | in accord with this section must be in a format that is publicly 339 | documented (and with an implementation available to the public in 340 | source code form), and must require no special password or key for 341 | unpacking, reading or copying. 342 | 343 | 7. Additional Terms. 344 | 345 | "Additional permissions" are terms that supplement the terms of this 346 | License by making exceptions from one or more of its conditions. 347 | Additional permissions that are applicable to the entire Program shall 348 | be treated as though they were included in this License, to the extent 349 | that they are valid under applicable law. If additional permissions 350 | apply only to part of the Program, that part may be used separately 351 | under those permissions, but the entire Program remains governed by 352 | this License without regard to the additional permissions. 353 | 354 | When you convey a copy of a covered work, you may at your option 355 | remove any additional permissions from that copy, or from any part of 356 | it. (Additional permissions may be written to require their own 357 | removal in certain cases when you modify the work.) You may place 358 | additional permissions on material, added by you to a covered work, 359 | for which you have or can give appropriate copyright permission. 360 | 361 | Notwithstanding any other provision of this License, for material you 362 | add to a covered work, you may (if authorized by the copyright holders of 363 | that material) supplement the terms of this License with terms: 364 | 365 | a) Disclaiming warranty or limiting liability differently from the 366 | terms of sections 15 and 16 of this License; or 367 | 368 | b) Requiring preservation of specified reasonable legal notices or 369 | author attributions in that material or in the Appropriate Legal 370 | Notices displayed by works containing it; or 371 | 372 | c) Prohibiting misrepresentation of the origin of that material, or 373 | requiring that modified versions of such material be marked in 374 | reasonable ways as different from the original version; or 375 | 376 | d) Limiting the use for publicity purposes of names of licensors or 377 | authors of the material; or 378 | 379 | e) Declining to grant rights under trademark law for use of some 380 | trade names, trademarks, or service marks; or 381 | 382 | f) Requiring indemnification of licensors and authors of that 383 | material by anyone who conveys the material (or modified versions of 384 | it) with contractual assumptions of liability to the recipient, for 385 | any liability that these contractual assumptions directly impose on 386 | those licensors and authors. 387 | 388 | All other non-permissive additional terms are considered "further 389 | restrictions" within the meaning of section 10. If the Program as you 390 | received it, or any part of it, contains a notice stating that it is 391 | governed by this License along with a term that is a further 392 | restriction, you may remove that term. If a license document contains 393 | a further restriction but permits relicensing or conveying under this 394 | License, you may add to a covered work material governed by the terms 395 | of that license document, provided that the further restriction does 396 | not survive such relicensing or conveying. 397 | 398 | If you add terms to a covered work in accord with this section, you 399 | must place, in the relevant source files, a statement of the 400 | additional terms that apply to those files, or a notice indicating 401 | where to find the applicable terms. 402 | 403 | Additional terms, permissive or non-permissive, may be stated in the 404 | form of a separately written license, or stated as exceptions; 405 | the above requirements apply either way. 406 | 407 | 8. Termination. 408 | 409 | You may not propagate or modify a covered work except as expressly 410 | provided under this License. Any attempt otherwise to propagate or 411 | modify it is void, and will automatically terminate your rights under 412 | this License (including any patent licenses granted under the third 413 | paragraph of section 11). 414 | 415 | However, if you cease all violation of this License, then your 416 | license from a particular copyright holder is reinstated (a) 417 | provisionally, unless and until the copyright holder explicitly and 418 | finally terminates your license, and (b) permanently, if the copyright 419 | holder fails to notify you of the violation by some reasonable means 420 | prior to 60 days after the cessation. 421 | 422 | Moreover, your license from a particular copyright holder is 423 | reinstated permanently if the copyright holder notifies you of the 424 | violation by some reasonable means, this is the first time you have 425 | received notice of violation of this License (for any work) from that 426 | copyright holder, and you cure the violation prior to 30 days after 427 | your receipt of the notice. 428 | 429 | Termination of your rights under this section does not terminate the 430 | licenses of parties who have received copies or rights from you under 431 | this License. If your rights have been terminated and not permanently 432 | reinstated, you do not qualify to receive new licenses for the same 433 | material under section 10. 434 | 435 | 9. Acceptance Not Required for Having Copies. 436 | 437 | You are not required to accept this License in order to receive or 438 | run a copy of the Program. Ancillary propagation of a covered work 439 | occurring solely as a consequence of using peer-to-peer transmission 440 | to receive a copy likewise does not require acceptance. However, 441 | nothing other than this License grants you permission to propagate or 442 | modify any covered work. These actions infringe copyright if you do 443 | not accept this License. Therefore, by modifying or propagating a 444 | covered work, you indicate your acceptance of this License to do so. 445 | 446 | 10. Automatic Licensing of Downstream Recipients. 447 | 448 | Each time you convey a covered work, the recipient automatically 449 | receives a license from the original licensors, to run, modify and 450 | propagate that work, subject to this License. You are not responsible 451 | for enforcing compliance by third parties with this License. 452 | 453 | An "entity transaction" is a transaction transferring control of an 454 | organization, or substantially all assets of one, or subdividing an 455 | organization, or merging organizations. If propagation of a covered 456 | work results from an entity transaction, each party to that 457 | transaction who receives a copy of the work also receives whatever 458 | licenses to the work the party's predecessor in interest had or could 459 | give under the previous paragraph, plus a right to possession of the 460 | Corresponding Source of the work from the predecessor in interest, if 461 | the predecessor has it or can get it with reasonable efforts. 462 | 463 | You may not impose any further restrictions on the exercise of the 464 | rights granted or affirmed under this License. For example, you may 465 | not impose a license fee, royalty, or other charge for exercise of 466 | rights granted under this License, and you may not initiate litigation 467 | (including a cross-claim or counterclaim in a lawsuit) alleging that 468 | any patent claim is infringed by making, using, selling, offering for 469 | sale, or importing the Program or any portion of it. 470 | 471 | 11. Patents. 472 | 473 | A "contributor" is a copyright holder who authorizes use under this 474 | License of the Program or a work on which the Program is based. The 475 | work thus licensed is called the contributor's "contributor version". 476 | 477 | A contributor's "essential patent claims" are all patent claims 478 | owned or controlled by the contributor, whether already acquired or 479 | hereafter acquired, that would be infringed by some manner, permitted 480 | by this License, of making, using, or selling its contributor version, 481 | but do not include claims that would be infringed only as a 482 | consequence of further modification of the contributor version. For 483 | purposes of this definition, "control" includes the right to grant 484 | patent sublicenses in a manner consistent with the requirements of 485 | this License. 486 | 487 | Each contributor grants you a non-exclusive, worldwide, royalty-free 488 | patent license under the contributor's essential patent claims, to 489 | make, use, sell, offer for sale, import and otherwise run, modify and 490 | propagate the contents of its contributor version. 491 | 492 | In the following three paragraphs, a "patent license" is any express 493 | agreement or commitment, however denominated, not to enforce a patent 494 | (such as an express permission to practice a patent or covenant not to 495 | sue for patent infringement). To "grant" such a patent license to a 496 | party means to make such an agreement or commitment not to enforce a 497 | patent against the party. 498 | 499 | If you convey a covered work, knowingly relying on a patent license, 500 | and the Corresponding Source of the work is not available for anyone 501 | to copy, free of charge and under the terms of this License, through a 502 | publicly available network server or other readily accessible means, 503 | then you must either (1) cause the Corresponding Source to be so 504 | available, or (2) arrange to deprive yourself of the benefit of the 505 | patent license for this particular work, or (3) arrange, in a manner 506 | consistent with the requirements of this License, to extend the patent 507 | license to downstream recipients. "Knowingly relying" means you have 508 | actual knowledge that, but for the patent license, your conveying the 509 | covered work in a country, or your recipient's use of the covered work 510 | in a country, would infringe one or more identifiable patents in that 511 | country that you have reason to believe are valid. 512 | 513 | If, pursuant to or in connection with a single transaction or 514 | arrangement, you convey, or propagate by procuring conveyance of, a 515 | covered work, and grant a patent license to some of the parties 516 | receiving the covered work authorizing them to use, propagate, modify 517 | or convey a specific copy of the covered work, then the patent license 518 | you grant is automatically extended to all recipients of the covered 519 | work and works based on it. 520 | 521 | A patent license is "discriminatory" if it does not include within 522 | the scope of its coverage, prohibits the exercise of, or is 523 | conditioned on the non-exercise of one or more of the rights that are 524 | specifically granted under this License. You may not convey a covered 525 | work if you are a party to an arrangement with a third party that is 526 | in the business of distributing software, under which you make payment 527 | to the third party based on the extent of your activity of conveying 528 | the work, and under which the third party grants, to any of the 529 | parties who would receive the covered work from you, a discriminatory 530 | patent license (a) in connection with copies of the covered work 531 | conveyed by you (or copies made from those copies), or (b) primarily 532 | for and in connection with specific products or compilations that 533 | contain the covered work, unless you entered into that arrangement, 534 | or that patent license was granted, prior to 28 March 2007. 535 | 536 | Nothing in this License shall be construed as excluding or limiting 537 | any implied license or other defenses to infringement that may 538 | otherwise be available to you under applicable patent law. 539 | 540 | 12. No Surrender of Others' Freedom. 541 | 542 | If conditions are imposed on you (whether by court order, agreement or 543 | otherwise) that contradict the conditions of this License, they do not 544 | excuse you from the conditions of this License. If you cannot convey a 545 | covered work so as to satisfy simultaneously your obligations under this 546 | License and any other pertinent obligations, then as a consequence you may 547 | not convey it at all. For example, if you agree to terms that obligate you 548 | to collect a royalty for further conveying from those to whom you convey 549 | the Program, the only way you could satisfy both those terms and this 550 | License would be to refrain entirely from conveying the Program. 551 | 552 | 13. Use with the GNU Affero General Public License. 553 | 554 | Notwithstanding any other provision of this License, you have 555 | permission to link or combine any covered work with a work licensed 556 | under version 3 of the GNU Affero General Public License into a single 557 | combined work, and to convey the resulting work. The terms of this 558 | License will continue to apply to the part which is the covered work, 559 | but the special requirements of the GNU Affero General Public License, 560 | section 13, concerning interaction through a network will apply to the 561 | combination as such. 562 | 563 | 14. Revised Versions of this License. 564 | 565 | The Free Software Foundation may publish revised and/or new versions of 566 | the GNU General Public License from time to time. Such new versions will 567 | be similar in spirit to the present version, but may differ in detail to 568 | address new problems or concerns. 569 | 570 | Each version is given a distinguishing version number. If the 571 | Program specifies that a certain numbered version of the GNU General 572 | Public License "or any later version" applies to it, you have the 573 | option of following the terms and conditions either of that numbered 574 | version or of any later version published by the Free Software 575 | Foundation. If the Program does not specify a version number of the 576 | GNU General Public License, you may choose any version ever published 577 | by the Free Software Foundation. 578 | 579 | If the Program specifies that a proxy can decide which future 580 | versions of the GNU General Public License can be used, that proxy's 581 | public statement of acceptance of a version permanently authorizes you 582 | to choose that version for the Program. 583 | 584 | Later license versions may give you additional or different 585 | permissions. However, no additional obligations are imposed on any 586 | author or copyright holder as a result of your choosing to follow a 587 | later version. 588 | 589 | 15. Disclaimer of Warranty. 590 | 591 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY 592 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT 593 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY 594 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, 595 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 596 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM 597 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF 598 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 599 | 600 | 16. Limitation of Liability. 601 | 602 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 603 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS 604 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY 605 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE 606 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF 607 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD 608 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), 609 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF 610 | SUCH DAMAGES. 611 | 612 | 17. Interpretation of Sections 15 and 16. 613 | 614 | If the disclaimer of warranty and limitation of liability provided 615 | above cannot be given local legal effect according to their terms, 616 | reviewing courts shall apply local law that most closely approximates 617 | an absolute waiver of all civil liability in connection with the 618 | Program, unless a warranty or assumption of liability accompanies a 619 | copy of the Program in return for a fee. 620 | 621 | END OF TERMS AND CONDITIONS 622 | 623 | How to Apply These Terms to Your New Programs 624 | 625 | If you develop a new program, and you want it to be of the greatest 626 | possible use to the public, the best way to achieve this is to make it 627 | free software which everyone can redistribute and change under these terms. 628 | 629 | To do so, attach the following notices to the program. It is safest 630 | to attach them to the start of each source file to most effectively 631 | state the exclusion of warranty; and each file should have at least 632 | the "copyright" line and a pointer to where the full notice is found. 633 | 634 | 635 | Copyright (C) 636 | 637 | This program is free software: you can redistribute it and/or modify 638 | it under the terms of the GNU General Public License as published by 639 | the Free Software Foundation, either version 3 of the License, or 640 | (at your option) any later version. 641 | 642 | This program is distributed in the hope that it will be useful, 643 | but WITHOUT ANY WARRANTY; without even the implied warranty of 644 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 645 | GNU General Public License for more details. 646 | 647 | You should have received a copy of the GNU General Public License 648 | along with this program. If not, see . 649 | 650 | Also add information on how to contact you by electronic and paper mail. 651 | 652 | If the program does terminal interaction, make it output a short 653 | notice like this when it starts in an interactive mode: 654 | 655 | Copyright (C) 656 | This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 657 | This is free software, and you are welcome to redistribute it 658 | under certain conditions; type `show c' for details. 659 | 660 | The hypothetical commands `show w' and `show c' should show the appropriate 661 | parts of the General Public License. Of course, your program's commands 662 | might be different; for a GUI interface, you would use an "about box". 663 | 664 | You should also get your employer (if you work as a programmer) or school, 665 | if any, to sign a "copyright disclaimer" for the program, if necessary. 666 | For more information on this, and how to apply and follow the GNU GPL, see 667 | . 668 | 669 | The GNU General Public License does not permit incorporating your program 670 | into proprietary programs. If your program is a subroutine library, you 671 | may consider it more useful to permit linking proprietary applications with 672 | the library. If this is what you want to do, use the GNU Lesser General 673 | Public License instead of this License. But first, please read 674 | . 675 | -------------------------------------------------------------------------------- /src/mapviewer.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | False 6 | 5 7 | dialog 8 | APRSMap 9 | 0.0.01b-rc0.1 10 | Copyright Gordon JC Pearce (MM0YEQ) et al 11 | A simple, silly and prettier aprs mapping program for the rest of us 12 | http://gordonjcp.github.com/aprsmap 13 | Homepage 14 | Something to be decided properly someday. 15 | Gordon JC Pearce MM0YEQ (github.com/gordonjcp) 16 | Michael Maclean (github.com/mgdm) 17 | Dave Hibberd MM3ZRZ (github.com/hibby) 18 | Andrew Elwell (github.com/elwell) 19 | Kyle Gordon 2M1DIQ (github.com/kylegordon) 20 | 21 | 22 | False 23 | vertical 24 | 2 25 | 26 | 27 | False 28 | end 29 | 30 | 31 | False 32 | True 33 | end 34 | 0 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | True 45 | False 46 | gtk-missing-image 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | england.aprs2.net 56 | 57 | 58 | euro.aprs2.net 59 | 60 | 61 | 62 | 63 | False 64 | Preferences 65 | False 66 | 67 | 68 | 69 | True 70 | False 71 | 4 72 | 4 73 | 4 74 | 4 75 | 76 | 77 | True 78 | False 79 | 80 | 81 | True 82 | True 83 | 84 | 85 | True 86 | False 87 | 88 | 89 | True 90 | False 91 | 92 | 93 | True 94 | False 95 | All the relevant location settings 96 | 97 | 98 | True 99 | True 100 | 0 101 | 102 | 103 | 104 | 105 | True 106 | False 107 | 108 | 109 | True 110 | False 111 | Decimal 112 | Latitude 113 | 114 | 115 | True 116 | True 117 | 0 118 | 119 | 120 | 121 | 122 | True 123 | True 124 | 125 | 126 | 127 | True 128 | True 129 | 1 130 | 131 | 132 | 133 | 134 | True 135 | True 136 | 1 137 | 138 | 139 | 140 | 141 | True 142 | False 143 | 144 | 145 | True 146 | False 147 | Decimal 148 | Longitude 149 | 150 | 151 | True 152 | True 153 | 0 154 | 155 | 156 | 157 | 158 | True 159 | True 160 | 161 | 162 | 163 | True 164 | True 165 | 1 166 | 167 | 168 | 169 | 170 | True 171 | True 172 | 2 173 | 174 | 175 | 176 | 177 | True 178 | False 179 | 180 | 181 | True 182 | False 183 | Radius 184 | 185 | 186 | True 187 | True 188 | 0 189 | 190 | 191 | 192 | 193 | True 194 | True 195 | 196 | True 197 | 198 | 199 | True 200 | True 201 | 1 202 | 203 | 204 | 205 | 206 | True 207 | True 208 | 3 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | True 218 | False 219 | Location 220 | 221 | 222 | False 223 | 224 | 225 | 226 | 227 | True 228 | False 229 | 230 | 231 | True 232 | False 233 | 234 | 235 | True 236 | False 237 | APRS Server 238 | 239 | 240 | True 241 | True 242 | 0 243 | 244 | 245 | 246 | 247 | True 248 | False 249 | 250 | 251 | True 252 | False 253 | Login Credentials 254 | 255 | 256 | True 257 | True 258 | 0 259 | 260 | 261 | 262 | 263 | True 264 | True 265 | 266 | 267 | 268 | True 269 | True 270 | 1 271 | 272 | 273 | 274 | 275 | True 276 | True 277 | 278 | 279 | 280 | True 281 | True 282 | 2 283 | 284 | 285 | 286 | 287 | True 288 | True 289 | 2 290 | 291 | 292 | 293 | 294 | True 295 | False 296 | liststore1 297 | 298 | 299 | True 300 | True 301 | 2 302 | 303 | 304 | 305 | 306 | 307 | 308 | 1 309 | 310 | 311 | 312 | 313 | True 314 | False 315 | APRS-IS 316 | 317 | 318 | 1 319 | False 320 | 321 | 322 | 323 | 324 | 325 | 326 | 327 | 328 | 329 | 330 | True 331 | True 332 | 0 333 | 334 | 335 | 336 | 337 | True 338 | False 339 | True 340 | 341 | 342 | gtk-ok 343 | True 344 | True 345 | True 346 | False 347 | True 348 | 349 | 350 | True 351 | True 352 | 0 353 | 354 | 355 | 356 | 357 | gtk-close 358 | True 359 | True 360 | True 361 | False 362 | True 363 | 364 | 365 | 366 | True 367 | True 368 | 1 369 | 370 | 371 | 372 | 373 | True 374 | True 375 | 1 376 | 377 | 378 | 379 | 380 | 381 | 382 | 383 | 384 | False 385 | 800 386 | 600 387 | 388 | 389 | 390 | True 391 | False 392 | 4 393 | 4 394 | 4 395 | 4 396 | 397 | 398 | True 399 | False 400 | 4 401 | 402 | 403 | True 404 | False 405 | 406 | 407 | True 408 | False 409 | False 410 | _File 411 | True 412 | 413 | 414 | True 415 | False 416 | 417 | 418 | gtk-new 419 | True 420 | False 421 | False 422 | True 423 | True 424 | 425 | 426 | 427 | 428 | gtk-open 429 | True 430 | False 431 | False 432 | True 433 | True 434 | 435 | 436 | 437 | 438 | gtk-save 439 | True 440 | False 441 | False 442 | True 443 | True 444 | 445 | 446 | 447 | 448 | gtk-save-as 449 | True 450 | False 451 | False 452 | True 453 | True 454 | 455 | 456 | 457 | 458 | True 459 | False 460 | False 461 | 462 | 463 | 464 | 465 | gtk-quit 466 | True 467 | False 468 | False 469 | True 470 | True 471 | 472 | 473 | 474 | 475 | 476 | 477 | 478 | 479 | 480 | True 481 | False 482 | False 483 | _Location 484 | True 485 | 486 | 487 | True 488 | False 489 | 490 | 491 | gtk-home 492 | True 493 | False 494 | False 495 | True 496 | True 497 | 498 | 499 | 500 | 501 | Set Home Here 502 | True 503 | False 504 | False 505 | image1 506 | False 507 | 508 | 509 | 510 | 511 | True 512 | False 513 | False 514 | 515 | 516 | 517 | 518 | gtk-properties 519 | True 520 | False 521 | False 522 | True 523 | True 524 | 525 | 526 | 527 | 528 | 529 | 530 | 531 | 532 | 533 | True 534 | False 535 | False 536 | _View 537 | True 538 | 539 | 540 | 541 | 542 | True 543 | False 544 | False 545 | _Help 546 | True 547 | 548 | 549 | True 550 | False 551 | 552 | 553 | gtk-about 554 | True 555 | False 556 | False 557 | True 558 | True 559 | 560 | 561 | 562 | 563 | 564 | 565 | 566 | 567 | 568 | False 569 | True 570 | 0 571 | 572 | 573 | 574 | 575 | True 576 | False 577 | True 578 | 579 | 580 | gtk-zoom-in 581 | True 582 | True 583 | True 584 | False 585 | True 586 | 587 | 588 | 589 | True 590 | True 591 | 0 592 | 593 | 594 | 595 | 596 | gtk-zoom-out 597 | True 598 | True 599 | True 600 | False 601 | True 602 | 603 | 604 | 605 | True 606 | True 607 | 1 608 | 609 | 610 | 611 | 612 | gtk-home 613 | True 614 | True 615 | True 616 | False 617 | True 618 | 619 | 620 | True 621 | True 622 | 2 623 | 624 | 625 | 626 | 627 | gtk-preferences 628 | True 629 | True 630 | True 631 | False 632 | True 633 | 634 | 635 | True 636 | True 637 | 3 638 | 639 | 640 | 641 | 642 | False 643 | True 644 | 1 645 | 646 | 647 | 648 | 649 | True 650 | False 651 | 652 | 653 | 654 | 655 | 656 | True 657 | True 658 | 1 659 | 660 | 661 | 662 | 663 | True 664 | False 665 | 2 666 | 667 | 668 | False 669 | True 670 | 3 671 | 672 | 673 | 674 | 675 | 676 | 677 | 678 | 679 | 680 | 681 | 682 | 683 | 684 | --------------------------------------------------------------------------------