Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
path: root/aprs.py
diff options
context:
space:
mode:
authorJack Zielke <kg4gjy@takeovertheworld.org>2009-05-26 05:05:34 (GMT)
committer Jack Zielke <kg4gjy@takeovertheworld.org>2009-05-26 05:05:34 (GMT)
commit9ada452201520759bd6ec5e46a5064f5c63e638f (patch)
tree2779e194d10d26b815f9ba4bba99c0d96bce3144 /aprs.py
version 1 - no journal, no messaging
Diffstat (limited to 'aprs.py')
-rwxr-xr-xaprs.py928
1 files changed, 928 insertions, 0 deletions
diff --git a/aprs.py b/aprs.py
new file mode 100755
index 0000000..0ec7ed5
--- /dev/null
+++ b/aprs.py
@@ -0,0 +1,928 @@
+# Copyright (C) 2008, Jack Zielke <jack@linuxcoffee.com>
+# Copyright (C) 2008, One Laptop Per Child
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+import gtk
+import pango
+import time
+import socket
+import random
+import gobject
+
+from gettext import gettext as _
+
+from sugar import profile
+from sugar.activity import activity
+from sugar.bundle.activitybundle import ActivityBundle
+from sugar.graphics.toolbutton import ToolButton
+
+HOST = 'rotate.aprs2.net'
+#HOST = '192.168.50.6'
+PORT = 14580
+RECV_BUFFER = 4096
+
+bundle = ActivityBundle(activity.get_bundle_path())
+VERSION = bundle.get_activity_version()
+del bundle
+
+class APRSActivity(activity.Activity):
+ def __init__(self, handle):
+ activity.Activity.__init__(self, handle)
+ self.set_title(_('APRS Activity'))
+
+ self.connect('destroy', self.onDestroy)
+
+ self.sock = None
+ self.location = "home"
+ self.site = None
+ self.sent_acks = {}
+ self.timers = []
+ self.validating = False
+
+ titlefont = pango.FontDescription('Sans bold 8')
+ smallfont = pango.FontDescription('Sans 6')
+ verysmallfont = pango.FontDescription('Sans 3')
+ firstName = profile.get_nick_name().split(None, 1)[0].capitalize()
+
+ toolbox = activity.ActivityToolbox(self)
+ self.set_toolbox(toolbox)
+
+ activity_toolbar = toolbox.get_activity_toolbar()
+ activity_toolbar.share.props.visible = False
+ activity_toolbar.keep.props.visible = False
+
+ toolbox.show()
+
+ win = gtk.HBox(False, 10)
+ self.set_canvas(win)
+
+ leftwin = gtk.VBox(False, 10)
+
+ # Top 'about' box
+ aboutbox = gtk.VBox(False, 10)
+ aboutbox.set_border_width(10)
+
+ topaboutbox = gtk.VBox(False, 0)
+
+ titlebox = gtk.HBox(False, 0)
+ titlelabel = gtk.Label("APRS-XO:")
+ titlelabel.set_alignment(0, 0)
+ titlelabel.modify_font(titlefont)
+ titlebox.pack_start(titlelabel, False, False, 0)
+ titlelabel.show()
+ aboutlabel1 = gtk.Label("This amateur radio program will update your position")
+ aboutlabel1.set_alignment(0, 0.8)
+ titlebox.pack_start(aboutlabel1, False, False, 0)
+ aboutlabel1.show()
+ topaboutbox.pack_start(titlebox, False, False, 0)
+ titlebox.show()
+ aboutlabel2 = gtk.Label("& status on all of the global APRS web pages once every 10 minutes.")
+ aboutlabel2.set_alignment(0, 0)
+ topaboutbox.pack_start(aboutlabel2, False, False, 0)
+ aboutlabel2.show()
+ aboutbox.pack_start(topaboutbox, False, False, 0)
+ topaboutbox.show()
+
+ sitebox = gtk.HBox(False, 10)
+ sitelabel = gtk.Label("Select an APRS site:")
+ sitelabel.set_alignment(0, 0.4)
+ sitebox.pack_start(sitelabel, False, False, 0)
+ sitelabel.show()
+
+ findubutton = gtk.Button()
+ findubutton.set_label("FINDU.COM")
+ findubutton.connect("clicked", self.set_site, "http://www.findu.com/cgi-bin/symbol.cgi?icon=XA&limit=200")
+ sitebox.pack_start(findubutton, False, False, 0)
+ findubutton.show()
+
+ aprsworldbutton = gtk.Button()
+ aprsworldbutton.set_label("APRSworld")
+ aprsworldbutton.connect("clicked", self.set_site, "http://aprsworld.net/")
+ sitebox.pack_start(aprsworldbutton, False, False, 0)
+ aprsworldbutton.show()
+
+ otherbutton = gtk.Button()
+ otherbutton.set_label("About")
+ otherbutton.connect("clicked", self.set_site, "http://aprs.org/")
+ sitebox.pack_start(otherbutton, False, False, 0)
+ otherbutton.show()
+
+ aboutbox.pack_start(sitebox, False, False, 0)
+ sitebox.show()
+
+ infobox = gtk.VBox(False, 4)
+ # more info radio box?
+
+ infolabel = gtk.Label("Without a ham license you can only communicate with other XO's on the Internet.\nWith a ham license you can communicate worldwide with wireless and radio.")
+ infolabel.set_alignment(0, 0)
+ infolabel.modify_font(smallfont)
+ infobox.pack_start(infolabel, False, False, 0)
+ infolabel.show()
+
+ aboutbox.pack_start(infobox, False, False, 0)
+ infobox.show()
+
+ leftwin.pack_start(aboutbox, False, False, 0)
+ aboutbox.show()
+
+ separator = gtk.HSeparator()
+ leftwin.pack_start(separator, False, False, 0)
+ separator.show()
+
+ # identifiers box
+ identbox = gtk.HBox(False, 10)
+
+ leftidentbox = gtk.VBox(False, 10)
+ leftidentbox.set_border_width(10)
+ identlabel = gtk.Label("Identifiers")
+ identlabel.set_alignment(0, 0)
+ identlabel.modify_font(titlefont)
+ leftidentbox.pack_start(identlabel, False, False, 0)
+ identlabel.show()
+
+ spacerlabel = gtk.Label(" ")
+ spacerlabel.modify_font(verysmallfont)
+ leftidentbox.pack_start(spacerlabel, False, False, 0)
+ spacerlabel.show()
+
+ bottomleftidentbox = gtk.HBox(False, 0)
+
+ calllabel1 = gtk.Label("Callsign: ")
+ calllabel1.set_alignment(0, 0.2)
+ bottomleftidentbox.pack_start(calllabel1, False, False, 0)
+ calllabel1.show()
+
+ callbox = gtk.VBox(False, 4)
+ self.calltext = gtk.Entry()
+ self.calltext.set_max_length(9)
+ self.calltext.set_width_chars(6)
+ self.calltext.connect("changed", self.disable_beacon)
+ callbox.pack_start(self.calltext, False, False, 0)
+ self.calltext.show()
+
+ calllabel2 = gtk.Label("If a ham radio operator")
+ calllabel2.set_alignment(0, 0)
+ calllabel2.modify_font(smallfont)
+ callbox.pack_start(calllabel2, False, False, 0)
+ calllabel2.show()
+
+ bottomleftidentbox.pack_start(callbox, False, False, 0)
+ callbox.show()
+
+ leftidentbox.pack_start(bottomleftidentbox, False, False, 0)
+ bottomleftidentbox.show()
+
+ identbox.pack_start(leftidentbox, False, False, 0)
+ leftidentbox.show()
+
+ rightidentbox = gtk.VBox(False, 10)
+ rightidentbox.set_border_width(10)
+
+ toprightidentbox = gtk.HBox(False, 4)
+ namelabel = gtk.Label("First Name: ")
+ namelabel.set_alignment(0, 0.5)
+ toprightidentbox.pack_start(namelabel, False, False, 0)
+ namelabel.show()
+
+ self.nametext = gtk.Entry()
+ self.nametext.set_max_length(36)
+ self.nametext.set_width_chars(15)
+ self.nametext.set_text(firstName)
+ self.nametext.connect("changed", self.disable_beacon)
+ toprightidentbox.pack_start(self.nametext, False, False, 0)
+ self.nametext.show()
+ rightidentbox.pack_start(toprightidentbox, False, False, 0)
+ toprightidentbox.show()
+
+ bottomrightidentbox = gtk.HBox(False, 0)
+
+ ziplabel1 = gtk.Label("Zip Code: ")
+ ziplabel1.set_alignment(0, 0.5)
+ bottomrightidentbox.pack_start(ziplabel1, False, False, 0)
+ ziplabel1.show()
+
+ self.ziptext = gtk.Entry()
+ self.ziptext.set_max_length(5)
+ self.ziptext.set_width_chars(5)
+ bottomrightidentbox.pack_start(self.ziptext, False, False, 0)
+ self.ziptext.show()
+
+ rightidentbox.pack_start(bottomrightidentbox, False, False, 0)
+ bottomrightidentbox.show()
+
+ ziplabel2 = gtk.Label("otherwise your call will be XZZZZZ")
+ ziplabel2.set_alignment(0, 0)
+ ziplabel2.modify_font(smallfont)
+ rightidentbox.pack_start(ziplabel2, False, False, 0)
+ ziplabel2.show()
+
+ identbox.pack_start(rightidentbox, False, False, 0)
+ rightidentbox.show()
+
+ leftwin.pack_start(identbox, False, False, 0)
+ identbox.show()
+
+ separator = gtk.HSeparator()
+ leftwin.pack_start(separator, False, False, 0)
+ separator.show()
+
+ # station box
+ stationbox = gtk.VBox(False, 10)
+ stationbox.set_border_width(10)
+
+ topstationbox = gtk.HBox(False, 10)
+
+ stationlabel = gtk.Label("Station Text")
+ stationlabel.set_alignment(0, 0.5)
+ stationlabel.modify_font(titlefont)
+ topstationbox.pack_start(stationlabel, False, False, 0)
+ stationlabel.show()
+
+ self.stationtext = gtk.Entry()
+ self.stationtext.set_max_length(36)
+ self.stationtext.set_width_chars(36)
+ self.stationtext.connect("changed", self.disable_beacon)
+ topstationbox.pack_start(self.stationtext, False, False, 0)
+ self.stationtext.show()
+
+ stationbox.pack_start(topstationbox, False, False, 0)
+ topstationbox.show()
+
+ stationhelp = gtk.Label("Enter up to 36 characters, most important first since some displays\ncan only see the first 20 or 28.")
+ stationhelp.set_alignment(0, 0)
+ stationbox.pack_start(stationhelp, False, False, 0)
+ stationhelp.show()
+
+ leftwin.pack_start(stationbox, False, False, 0)
+ stationbox.show()
+
+ separator = gtk.HSeparator()
+ leftwin.pack_start(separator, False, False, 0)
+ separator.show()
+
+ # position box
+ positbox = gtk.VBox(False, 10)
+ positbox.set_border_width(10)
+
+ toppositbox = gtk.HBox(False, 30)
+
+ topleftpositbox = gtk.VBox(False, 4)
+
+ positlabel1 = gtk.Label("Position Data")
+ positlabel1.set_alignment(0, 0)
+ positlabel1.modify_font(titlefont)
+ topleftpositbox.pack_start(positlabel1, False, False, 0)
+ positlabel1.show()
+
+ latpositbox = gtk.HBox(False, 4)
+ latlabel1 = gtk.Label("Latitude: ")
+ latlabel1.set_alignment(0, 0.5)
+ latpositbox.pack_start(latlabel1, False, False, 0)
+ latlabel1.show()
+
+ self.latDDtext = gtk.Entry()
+ self.latDDtext.set_max_length(2)
+ self.latDDtext.set_width_chars(4)
+ self.latDDtext.set_text("DD")
+ self.latDDtext.select_region(1,2)
+ self.latDDtext.connect("changed", self.disable_beacon)
+ latpositbox.pack_start(self.latDDtext, False, False, 0)
+ self.latDDtext.show()
+
+ self.latMMtext = gtk.Entry()
+ self.latMMtext.set_max_length(2)
+ self.latMMtext.set_width_chars(3)
+ self.latMMtext.set_text("MM")
+ self.latMMtext.select_region(1,2)
+ self.latMMtext.connect("changed", self.disable_beacon)
+ latpositbox.pack_start(self.latMMtext, False, False, 0)
+ self.latMMtext.show()
+
+ latlabel2 = gtk.Label(".")
+ latlabel2.set_alignment(0, 1)
+ latpositbox.pack_start(latlabel2, False, False, 0)
+ latlabel2.show()
+
+ self.latmmtext = gtk.Entry()
+ self.latmmtext.set_max_length(2)
+ self.latmmtext.set_width_chars(3)
+ self.latmmtext.set_text("mm")
+ self.latmmtext.select_region(1,2)
+ self.latmmtext.connect("changed", self.disable_beacon)
+ latpositbox.pack_start(self.latmmtext, False, False, 0)
+ self.latmmtext.show()
+
+ self.latcombo = gtk.combo_box_new_text()
+ self.latcombo.append_text("N")
+ self.latcombo.append_text("S")
+ self.latcombo.set_active(0)
+ latpositbox.pack_start(self.latcombo, False, False, 0)
+ self.latcombo.show()
+
+ topleftpositbox.pack_start(latpositbox, False, False, 0)
+ latpositbox.show()
+
+ lonpositbox = gtk.HBox(False, 4)
+ lonlabel1 = gtk.Label("Longitude: ")
+ lonlabel1.set_alignment(0, 0.5)
+ lonpositbox.pack_start(lonlabel1, False, False, 0)
+ lonlabel1.show()
+
+ self.lonDDDtext = gtk.Entry()
+ self.lonDDDtext.set_max_length(3)
+ self.lonDDDtext.set_width_chars(4)
+ self.lonDDDtext.set_text("DDD")
+ self.lonDDDtext.select_region(1,2)
+ self.lonDDDtext.connect("changed", self.disable_beacon)
+ lonpositbox.pack_start(self.lonDDDtext, False, False, 0)
+ self.lonDDDtext.show()
+
+ self.lonMMtext = gtk.Entry()
+ self.lonMMtext.set_max_length(2)
+ self.lonMMtext.set_width_chars(3)
+ self.lonMMtext.set_text("MM")
+ self.lonMMtext.select_region(1,2)
+ self.lonMMtext.connect("changed", self.disable_beacon)
+ lonpositbox.pack_start(self.lonMMtext, False, False, 0)
+ self.lonMMtext.show()
+
+ lonlabel2 = gtk.Label(".")
+ lonlabel2.set_alignment(0, 1)
+ lonpositbox.pack_start(lonlabel2, False, False, 0)
+ lonlabel2.show()
+
+ self.lonmmtext = gtk.Entry()
+ self.lonmmtext.set_max_length(2)
+ self.lonmmtext.set_width_chars(3)
+ self.lonmmtext.set_text("mm")
+ self.lonmmtext.select_region(1,2)
+ self.lonmmtext.connect("changed", self.disable_beacon)
+ lonpositbox.pack_start(self.lonmmtext, False, False, 0)
+ self.lonmmtext.show()
+
+ self.loncombo = gtk.combo_box_new_text()
+ self.loncombo.append_text("W")
+ self.loncombo.append_text("E")
+ self.loncombo.set_active(0)
+ lonpositbox.pack_start(self.loncombo, False, False, 0)
+ self.loncombo.show()
+
+ topleftpositbox.pack_start(lonpositbox, False, False, 0)
+ lonpositbox.show()
+
+ toppositbox.pack_start(topleftpositbox, False, False, 0)
+ topleftpositbox.show()
+
+ toprightpositbox = gtk.VBox(False, 4)
+
+ loclabel = gtk.Label("Location Type:")
+ loclabel.set_alignment(0, 0)
+ toprightpositbox.pack_start(loclabel, False, False, 0)
+ loclabel.show()
+
+ locbox = gtk.HBox(False, 4)
+
+ leftlocbox = gtk.VBox(False, 4)
+
+ locbutton = gtk.RadioButton(None, "home")
+ locbutton.connect("toggled", self.set_location, "home")
+ locbutton.set_active(True)
+ leftlocbox.pack_start(locbutton, False, False, 0)
+ locbutton.show()
+
+ locbutton = gtk.RadioButton(locbutton, "work")
+ locbutton.connect("toggled", self.set_location, "work")
+ leftlocbox.pack_start(locbutton, False, False, 0)
+ locbutton.show()
+
+ locbutton = gtk.RadioButton(locbutton, "school")
+ locbutton.connect("toggled", self.set_location, "school")
+ leftlocbox.pack_start(locbutton, False, False, 0)
+ locbutton.show()
+
+ locbox.pack_start(leftlocbox, False, False, 0)
+ leftlocbox.show()
+
+ rightlocbox = gtk.VBox(False, 4)
+
+ locbutton = gtk.RadioButton(locbutton, "club")
+ locbutton.connect("toggled", self.set_location, "club")
+ rightlocbox.pack_start(locbutton, False, False, 0)
+ locbutton.show()
+
+ locbutton = gtk.RadioButton(locbutton, "visiting")
+ locbutton.connect("toggled", self.set_location, "visiting")
+ rightlocbox.pack_start(locbutton, False, False, 0)
+ locbutton.show()
+
+ locbutton = gtk.RadioButton(locbutton, "other")
+ locbutton.connect("toggled", self.set_location, "other")
+ rightlocbox.pack_start(locbutton, False, False, 0)
+ locbutton.show()
+
+ locbox.pack_start(rightlocbox, False, False, 0)
+ rightlocbox.show()
+
+ toprightpositbox.pack_start(locbox, False, False, 0)
+ locbox.show()
+
+ toppositbox.pack_start(toprightpositbox, False, False, 0)
+ toprightpositbox.show()
+
+ positbox.pack_start(toppositbox, False, False, 0)
+ toppositbox.show()
+
+ positlabel2 = gtk.Label("If you do not know your LAT/LONG then your\nzip code will be used to place you on the map.")
+ positlabel2.set_alignment(0, 0)
+ positbox.pack_start(positlabel2, False, False, 0)
+ positlabel2.show()
+
+ leftwin.pack_start(positbox, False, False, 0)
+ positbox.show()
+
+ separator = gtk.HSeparator()
+ leftwin.pack_start(separator, False, False, 0)
+ separator.show()
+
+ # defined here so clear and connect buttons have access
+ statusview = gtk.TextView()
+ self.statusbuffer = statusview.get_buffer()
+
+ connectbutton = gtk.Button()
+ connectbutton.set_label("Connect")
+ connectbutton.modify_bg(gtk.STATE_NORMAL, gtk.gdk.color_parse("#00b20d"))
+ connectbutton.connect("clicked", self.connect_aprs)
+ leftwin.pack_start(connectbutton, False, False, 12)
+ connectbutton.show()
+
+ win.pack_start(leftwin, False, False, 0)
+ leftwin.show()
+
+ rightwin = gtk.VBox(False, 4)
+
+ rightwintopbox = gtk.HBox(False, 4)
+
+ clearbutton = gtk.Button()
+ clearbutton.set_label(" Clear ")
+ clearbutton.connect("clicked", self.clear_status)
+ rightwintopbox.pack_start(clearbutton, False, False, 20)
+ clearbutton.show()
+
+ self.beaconbutton = gtk.CheckButton("Beacon every 10 minutes")
+ self.beaconbutton.set_active(True)
+ self.beaconbutton.connect("toggled", self.enable_beacon, "beacon")
+ rightwintopbox.pack_start(self.beaconbutton, False, False, 20)
+ self.beaconbutton.show()
+
+ rightwin.pack_start(rightwintopbox, False, False, 0)
+ rightwintopbox.show()
+
+ statusview.set_editable(False)
+ statusview.set_cursor_visible(True)
+ statusview.set_wrap_mode(gtk.WRAP_CHAR)
+ statusview.set_justification(gtk.JUSTIFY_LEFT)
+ statusview.modify_font(smallfont)
+
+ self.statusbuffer.set_text("Welcome to APRS-XO.\n\nThis program sends your position information to a\nserver that will then display your location on\na webpage. This program requires an active\nInternet connection to work.\n\nSelect an APRS Site\nSelecting a button will copy the URI to the\nclipboard.\n\nIndentifiers\nIf you are not a ham radio operator enter your\nfirst name and zip code.\n\nIf you are a ham radio operator enter your first\nname, call sign, and position data. If position\nis unknown, leave the call sign field blank and\nenter your 5 digit zip code.\n\nStation Text\nData in the Station Text field will appear\nafter your location information on the website.\n\nBeacon\nWhen selected, sends location data every 10\nminutes. The beacon will automatically stop\nwhen you edit personal information. Must be\nmanually reselected after edit completion.\n\nAPRS Copyright (c) Bob Bruninga WB4APR\n\n")
+
+ self.statuswindow = gtk.ScrolledWindow()
+ self.statuswindow.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_ALWAYS)
+ self.statuswindow.add_with_viewport(statusview)
+ statusview.show()
+ rightwin.pack_start(self.statuswindow, True, True, 0)
+ self.statuswindow.show()
+
+ # when messaging is enabled, put a textview for messages above the status textview
+ # move the messagebox between them.
+ messagebox = gtk.HBox(False, 4)
+
+ self.messagecombo = gtk.combo_box_entry_new_text()
+ self.messagecombo.append_text("ALL")
+ self.messagecombo.append_text("BEACON")
+ self.messagecombo.append_text("CQ")
+ self.messagecombo.append_text("QST")
+ self.messagecombo.set_active(-1)
+ self.messagedest = self.messagecombo.get_child()
+ self.messagedest.set_max_length(9)
+ self.messagedest.set_width_chars(5)
+ self.messagedest.modify_font(smallfont)
+ messagebox.pack_start(self.messagecombo, False, False, 0)
+ self.messagecombo.show()
+
+ self.messagetext = gtk.Entry()
+ self.messagetext.set_max_length(67)
+ self.messagetext.set_width_chars(23)
+ self.messagetext.modify_font(smallfont)
+ self.messagetext.connect("activate", self.send_message, self.messagetext)
+ messagebox.pack_start(self.messagetext, False, False, 0)
+ self.messagetext.show()
+
+ messagebutton = gtk.Button()
+ messagebutton.set_label("Send")
+ messagebutton.connect("clicked", self.send_message, "message")
+ messagebox.pack_start(messagebutton, False, False, 0)
+ messagebutton.show()
+
+ rightwin.pack_start(messagebox, False, False, 0)
+ messagebox.show()
+
+ self.rawbox = gtk.HBox(False, 6)
+
+ self.rawtext = gtk.Entry()
+ self.rawtext.set_max_length(128)
+ self.rawtext.set_width_chars(34)
+ self.rawtext.modify_font(smallfont)
+ self.rawtext.connect("activate", self.raw_send)
+ self.rawbox.pack_start(self.rawtext, False, False, 0)
+ self.rawtext.show()
+
+ rawbutton = gtk.Button()
+ rawbutton.set_label("Send")
+ rawbutton.connect("clicked", self.raw_send)
+ self.rawbox.pack_start(rawbutton, False, False, 0)
+ rawbutton.show()
+
+ rightwin.pack_start(self.rawbox, False, False, 0)
+ # only show if aprsd says you are verified
+ #rawbox.show()
+
+ win.pack_start(rightwin, False, False, 0)
+ rightwin.show()
+
+ win.show()
+
+ def clear_status(self, button):
+ self.statusbuffer.delete(self.statusbuffer.get_start_iter(), self.statusbuffer.get_end_iter())
+
+ def connect_aprs(self, button):
+ if (self.validate_data() == False):
+ return False
+ if (self.sock == None):
+ self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+ self.status_write("Connecting ")
+ iplist = socket.gethostbyname_ex(HOST)[2]
+ server = random.choice(iplist)
+ self.status_write("to %s\n" % server)
+ try:
+ self.sock.connect((server, PORT))
+ except socket.error, msg:
+ self.status_write(msg[1])
+ self.sock = None
+ return
+
+ self.status_write("Connected\n")
+ button.set_label("Disconnect")
+ button.modify_bg(gtk.STATE_NORMAL, gtk.gdk.color_parse("#e6000a"))
+
+ response = self.sock.recv(RECV_BUFFER)
+ self.status_write("%s" % response)
+ if (response.find("javAPRSSrvr") == -1):
+ self.status_write("invalid response.")
+ self.disconnect_aprs(button)
+
+ # hidden feature
+ # if you have callsign, zip code and position - send zip as password
+ if (self.calltext.get_text() != "" and self.ziptext.get_text() != "" and self.latDDtext.get_text() != "DD"):
+ sendme = "user %s pass %s vers aprs_xo %d\n" % (self.calltext.get_text(), self.ziptext.get_text(), VERSION)
+ else:
+ sendme = "user %s vers aprs_xo %d\n" % (self.calltext.get_text(), VERSION)
+ self.sock.sendall(sendme)
+ self.status_write("%s" % sendme)
+
+ response = self.sock.recv(RECV_BUFFER)
+ self.status_write("%s" % response)
+ if (response.find("# logresp") == -1):
+ self.status_write("invalid response.")
+ self.disconnect_aprs(button)
+ if (response.find("unverified") == -1):
+ self.rawbox.show()
+
+ self.input_watch = gobject.io_add_watch(self.sock, gobject.IO_IN, self.recv_data)
+
+ # send banner
+ sendme = "%s>APRS-XO v%s\n" % (self.calltext.get_text(), VERSION)
+ self.sock.sendall(sendme)
+
+ self.send_beacon()
+ self.output_watch = gobject.timeout_add(10 * 60 * 1000, self.send_beacon)
+ else:
+ self.disconnect_aprs(button)
+ gobject.source_remove(self.input_watch)
+ gobject.source_remove(self.output_watch)
+
+ def disconnect(self):
+ # for test server only
+ if(HOST == "192.168.50.6"):
+ self.sock.sendall("q")
+
+ self.sock.close()
+ self.sock = None
+
+ def disconnect_aprs(self, button):
+ self.disconnect()
+
+ self.status_write("Disconnected\n")
+ button.set_label("Connect")
+ button.modify_bg(gtk.STATE_NORMAL, gtk.gdk.color_parse("#00b20d"))
+ self.rawbox.hide()
+
+ def recv_data(self, sock, condition):
+ while 1:
+ try:
+ recv_data = sock.recv(RECV_BUFFER)
+ except:
+ self.status_write("Server closed connection.\n")
+ return False
+ if not recv_data:
+ self.status_write("Server closed connection.\n")
+ return False
+ else:
+ self.status_write(recv_data)
+ self.msg_check(recv_data)
+ return True
+
+ def send_beacon(self):
+ if (self.beaconbutton.get_active()):
+ beacon = "%s>APOLPC:=%s%s.%s%sX%s%s.%s%sA%s's XO at %s. %s\n" % (self.calltext.get_text(), self.latDDtext.get_text(), self.latMMtext.get_text(), self.latmmtext.get_text(), self.latcombo.get_active_text(), self.lonDDDtext.get_text(), self.lonMMtext.get_text(), self.lonmmtext.get_text(), self.loncombo.get_active_text(), self.nametext.get_text(), self.location, self.stationtext.get_text())
+ try:
+ self.sock.sendall(beacon)
+ except:
+ self.status_write("\nProblem sending beacon - STOPPED")
+ return False
+ self.status_write("%s" % beacon)
+ return True
+ else:
+ return True
+
+ def send_data(self, msg):
+ text = "%s>APOLPC:%s\n" % (self.calltext.get_text(), msg)
+ try:
+ self.sock.sendall(text)
+ except:
+ self.status_write("Problem sending message\n")
+ self.sock = None
+ return False
+ self.status_write("%s" % text)
+ return True
+
+ def send_ack(self, msg):
+ self.send_data(msg)
+ return False
+
+ def status_write(self, text):
+ self.statusbuffer.insert(self.statusbuffer.get_end_iter(), text)
+ adjustment = self.statuswindow.get_vadjustment()
+ adjustment.set_value(adjustment.upper)
+
+ def set_location(self, widget, data=None):
+ self.location = data
+
+ def set_site(self, widget, data=None):
+ self.site = data
+ self.clipboard()
+
+ def validate_data(self):
+ stop_here = False
+ self.validating = True
+ beaconchecked = self.beaconbutton.get_active()
+
+ if (self.nametext.get_text() == ""):
+ self.status_write("First Name is required\n")
+ stop_here = True
+
+ if (self.calltext.get_text() == "" and self.ziptext.get_text() == ""):
+ self.status_write("A callsign or zip code must be provided.\n")
+ self.validating = False
+ return False
+
+ if (stop_here):
+ self.validating = False
+ return False
+
+ # convert zip to position
+ if (self.calltext.get_text() == "" and self.ziptext.get_text() != "" and self.latDDtext.get_text() == "DD"):
+ self.status_write("Attempting to set location from zip code\n")
+ self.ziptext.set_text(self.ziptext.get_text().zfill(5))
+ try:
+ zipsock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+ except socket.error, msg:
+ self.status_write("[ERROR] %s\n" % msg[1])
+ self.validating = False
+ return False
+ try:
+ zipsock.connect(('geocoder.us', 80))
+ except socket.error, msg:
+ self.status_write("[ERROR] %s\n" % msg[1])
+ self.validating = False
+ return False
+ try:
+ zipsock.sendall("GET /service/csv/geocode?zip=%s\n" % self.ziptext.get_text())
+ except socket.error, msg:
+ self.status_write("[ERROR] %s\n" % msg[1])
+ self.validating = False
+ return False
+ zipsock.shutdown(1)
+ try:
+ response = zipsock.recv(RECV_BUFFER)
+ except socket.error, msg:
+ self.status_write("[ERROR] %s\n" % msg[1])
+ self.validating = False
+ return False
+ A = random.randrange(0,9)
+ O = random.randrange(0,9)
+ self.calltext.set_text("X%s-%d%d" % (self.ziptext.get_text(), A, O))
+ try:
+ (lat, lon, junk) = response.split(',', 2)
+ except:
+ self.status_write("%s\n" % response)
+ lat = "0"
+ lon = "0"
+ lat = float(lat.strip())
+ lon = float(lon.strip())
+ if (lat < 0):
+ self.latcombo.set_active(1) # S
+ else:
+ self.latcombo.set_active(0) # N
+ lat = abs(lat)
+ if (lon < 0):
+ self.loncombo.set_active(0) # W
+ else:
+ self.loncombo.set_active(1) # E
+ lon = abs(lon)
+ self.latDDtext.set_text("%02d" % int(lat))
+ self.latMMtext.set_text("%02d" % int((lat - int(lat)) * 60 + 0.5))
+ self.latmmtext.set_text("%d " % A)
+ self.lonDDDtext.set_text("%03d" % int(lon))
+ self.lonMMtext.set_text("%02d" % int((lon - int(lon)) * 60 + 0.5))
+ self.lonmmtext.set_text("%d " % O)
+ self.ziptext.set_text("")
+ # end set loc by zip code
+
+ if (self.latDDtext.get_text() == "DD" or self.latDDtext.get_text() == ""):
+ self.status_write("Latitude Degrees are required.\n")
+ stop_here = True
+ if (self.latMMtext.get_text() == "MM" or self.latMMtext.get_text() == ""):
+ self.status_write("Latitude Minutes are required.\n")
+ stop_here = True
+ if (self.lonDDDtext.get_text() == "DDD" or self.lonDDDtext.get_text() == ""):
+ self.status_write("Longitude Degrees are required.\n")
+ stop_here = True
+ if (self.lonMMtext.get_text() == "MM" or self.lonMMtext.get_text() == ""):
+ self.status_write("Longitude Minutes are required.\n")
+ stop_here = True
+
+ if (stop_here):
+ self.status_write("Latitude and Longitude must be complete.\nFor position ambiguity omit decimal Minutes (mm).\nIf you do not know your lat/lon, leave the letters\n(DD, MM, etc) and provide your zip code.\n")
+ self.validating = False
+ return False
+
+ if (not self.latDDtext.get_text().isdigit()):
+ self.status_write("Latitude Degrees must be a number.\n")
+ stop_here = True
+ if (not self.latMMtext.get_text().isdigit()):
+ self.status_write("Latitude Minutes must be a number.\n")
+ stop_here = True
+ if (not self.lonDDDtext.get_text().isdigit()):
+ self.status_write("Longitude Degrees must be a number.\n")
+ stop_here = True
+ if (not self.lonMMtext.get_text().isdigit()):
+ self.status_write("Longitude Minutes must be a number.\n")
+ stop_here = True
+
+ if (stop_here):
+ self.status_write("Invalid Position.\n")
+ self.validating = False
+ return False
+
+ if (int(self.latDDtext.get_text()) < 0 or int(self.latDDtext.get_text()) > 90):
+ self.status_write("Latitude Degrees must be between 0 and 90.\n")
+ stop_here = True
+ if (int(self.latMMtext.get_text()) < 0 or int(self.latMMtext.get_text()) > 60):
+ self.status_write("Latitude Minutes must be between 0 and 60.\n")
+ stop_here = True
+ if (int(self.lonDDDtext.get_text()) < 0 or int(self.lonDDDtext.get_text()) > 180):
+ self.status_write("Longitude Degrees must be between 0 and 180.\n")
+ stop_here = True
+ if (int(self.lonMMtext.get_text()) < 0 or int(self.lonMMtext.get_text()) > 60):
+ self.status_write("Longitude Minutes must be between 0 and 60.\n")
+ stop_here = True
+
+ if (stop_here):
+ self.status_write("Invalid Position.\n")
+ self.validating = False
+ return False
+
+ # clean up entries
+ self.latDDtext.set_text(self.latDDtext.get_text().zfill(2))
+ self.latMMtext.set_text(self.latMMtext.get_text().zfill(2))
+ self.lonDDDtext.set_text(self.lonDDDtext.get_text().zfill(3))
+ self.lonMMtext.set_text(self.lonMMtext.get_text().zfill(2))
+ self.latmmtext.set_text(self.latmmtext.get_text().ljust(2))
+ self.lonmmtext.set_text(self.lonmmtext.get_text().ljust(2))
+ if (self.latmmtext.get_text() == "mm"):
+ self.latmmtext.set_text(" ")
+ if (self.lonmmtext.get_text() == "mm"):
+ self.lonmmtext.set_text(" ")
+ self.calltext.set_text(self.calltext.get_text().upper())
+ self.beaconbutton.set_active(beaconchecked)
+ self.validating = False
+
+ def onDestroy(self, junk=None ):
+ if (self.sock != None):
+ self.disconnect()
+
+# def write_file(self, filename):
+# # does not appear to run so commented out for now
+# self.metadata['callsign'] = self.calltext.get_text()
+# self.metadata['latDD'] = self.latDDtext.get_text()
+# self.metadata['latMM'] = self.latMMtext.get_text()
+# self.metadata['latmm'] = self.latmmtext.get_text()
+# self.metadata['lat'] = self.latcombo.get_active()
+# self.metadata['lonDDD'] = self.lonDDDtext.get_text()
+# self.metadata['lonMM'] = self.lonMMtext.get_text()
+# self.metadata['lonmm'] = self.lonmmtext.get_text()
+# self.metadata['lon'] = self.loncombo.get_active()
+# self.metadata['nick_name'] = self.nametext.get_text()
+# self.metadata['location'] = self.location
+# self.metadata['stationtext'] = self.stationtext.get_text()
+# self.metadata['site'] = self.site
+# self.metadata['zip'] = self.ziptext.get_text()
+# self.metadata['beacon'] = self.beaconbutton.get_active()
+# # should save list of callsigns used in outgoing messages
+
+# def read_file(self, filename):
+# # does not appear to run so commented out for now
+# self.calltext.set_text(self.metadata.get('callsign', ""))
+
+ def msg_check(self, data):
+ # a quick "does it look like a message?" check
+ firstcheck = data.find("::")
+ secondcheck = data.find("{")
+ if (firstcheck != -1 and secondcheck != -1):
+ # great, it looks kinda like a message, is it to me?
+ tocall = data[firstcheck+2:firstcheck+11]
+ if (tocall.strip().upper() == self.calltext.get_text()):
+ sequence_end = data.find("}")
+ if (sequence_end == -1):
+ sequence = data[secondcheck+1:-1]
+ else:
+ sequence = data[secondcheck+1:sequence_end]
+ fromcall = data[:data.find(">")]
+ message = ":%s:ack%s" % (fromcall, sequence)
+ self.send_data(message)
+ id = "%s-%s" % (fromcall, sequence)
+ if (id in self.sent_acks):
+ self.timers.append(gobject.timeout_add(30 * 1000, self.send_ack, message))
+ self.timers.append(gobject.timeout_add(60 * 1000, self.send_ack, message))
+ self.timers.append(gobject.timeout_add(120 * 1000, self.send_ack, message))
+
+ self.sent_acks[id] = time.time() # to help a cleanup thread later
+ self.sent_acks[fromcall] = sequence # to help with reply acks later
+
+ def disable_beacon(self, widget, data=None):
+ if (self.sock != None):
+ self.beaconbutton.set_active(False)
+
+ def enable_beacon(self, widget, data=None):
+ if (not self.validating and widget.get_active()):
+ self.validate_data()
+
+ def raw_send(self, widget, data=None):
+ msg = "%s\n" % self.rawtext.get_text()
+ try:
+ self.sock.sendall(msg)
+ except:
+ self.status_write("Problem sending message\n")
+ self.sock = None
+ return False
+ self.status_write(msg)
+ self.rawtext.set_text("")
+ return True
+
+ def send_message(self, widget, data=None):
+ self.status_write("This version of APRS-XO can not send messages.\n")
+ self.messagetext.set_text("")
+
+ def clipboard(self):
+ clipboard = gtk.clipboard_get()
+ target = [("text/uri-list", 0, 0)]
+ clipboard.set_with_data(target, self.clipboard_get, self.clipboard_clear, (self.site))
+
+ def clipboard_get(self, clipboard, selection, info, data):
+ selection.set_uris([data])
+
+ def clipboard_clear(self, clipboard, data):
+ pass