# 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
MAXLINES = 50
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')
mediumfont = pango.FontDescription('Sans 6.5')
smallfont = pango.FontDescription('Sans 6')
verysmallfont = pango.FontDescription('Sans 4')
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, 8)
aboutbox.set_border_width(8)
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)
aboutlabel1.modify_font(mediumfont)
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)
aboutlabel2.modify_font(mediumfont)
topaboutbox.pack_start(aboutlabel2, False, False, 0)
aboutlabel2.show()
aboutbox.pack_start(topaboutbox, False, False, 0)
topaboutbox.show()
sitebox = gtk.HBox(False, 8)
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 = gtk.Label("With a ham license you can communicate worldwide with wireless and radio.\nWithout a ham license you can only communicate with other XO's on the\nInternet.")
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, 4)
identbox.set_border_width(8)
leftidentbox = gtk.VBox(False, 4)
# leftidentbox.set_border_width(8)
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, 4)
# rightidentbox.set_border_width(8)
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, 8)
stationbox.set_border_width(8)
# topstationbox = gtk.HBox(False, 8)
stationlabel = gtk.Label("Station Text")
stationlabel.set_alignment(0, 0)
stationlabel.modify_font(titlefont)
# topstationbox.pack_start(stationlabel, False, False, 0)
stationbox.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)
stationbox.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\ndisplays can only see the first 20 or 28.")
stationhelp.set_alignment(0, 0)
# stationhelp.modify_font(mediumfont)
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, 8)
positbox.set_border_width(8)
toppositbox = gtk.HBox(False, 0)
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, 0)
loclabel = gtk.Label("Location Type:")
loclabel.set_alignment(0, 0)
toprightpositbox.pack_start(loclabel, False, False, 0)
loclabel.show()
locbox = gtk.HBox(False, 0)
leftlocbox = gtk.VBox(False, 0)
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, 0)
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 zip code will be used to\nplace you on the map.")
positlabel2.set_alignment(0, 0)
positlabel2.modify_font(mediumfont)
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()
messageview = gtk.TextView()
self.messagebuffer = messageview.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)
rightwin.set_border_width(4)
rightwintopbox = gtk.HBox(False, 4)
clearbutton = gtk.Button()
clearbutton.set_label(" Clear ")
clearbutton.connect("clicked", self.clear_message)
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()
messageview.set_editable(False)
messageview.set_cursor_visible(True)
messageview.set_wrap_mode(gtk.WRAP_CHAR)
messageview.set_justification(gtk.JUSTIFY_LEFT)
messageview.modify_font(smallfont)
# self.messagebuffer.set_text("Welcome to APRS-XO.\n\nThis program sends your position information to a server\nthat will then display your location on a webpage. This\nprogram requires an active Internet connection to work.\n\nSelect an APRS Site\nSelecting a button will copy the URI to the clipboard.\n\nIndentifiers\nIf you are not a ham radio operator enter your first name\nand zip code.\n\nIf you are a ham radio operator enter your first name,\ncallsign, and position data. If position is unknown, leave\nthe callsign field blank and enter your 5 digit zip code.\n\nStation Text\nData in the Station Text field will appear after your location\ninformation on the website.\n\nBeacon\nWhen selected, sends location data every 10 minutes. The\nbeacon will automatically stop when you edit personal\ninformation. Must be manually reselected after edit\ncompletion.\n\nAPRS Copyright (c) Bob Bruninga WB4APR\n\n")
self.messagebuffer.set_text("Welcome to APRS-XO.\n\nThis program sends your position information to a server\nthat will then display your location on a webpage. This\nprogram requires an active Internet connection to work.\n\nSelect an APRS Site\nSelecting a button will copy the URI to the clipboard.\n\nIndentifiers\nIf you are not a ham radio operator enter your first name\nand zip code.\n\nIf you are a ham radio operator enter your first name,\ncallsign, and position data. If position is unknown, leave\nthe callsign field blank and enter your 5 digit zip code.")
self.messagewindow = gtk.ScrolledWindow()
self.messagewindow.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_ALWAYS)
self.messagewindow.add_with_viewport(messageview)
messageview.show()
rightwin.pack_start(self.messagewindow, True, True, 0)
self.messagewindow.show()
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(27)
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()
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("Station Text\nData in the Station Text field will appear after your location\ninformation on the website.\n\nBeacon\nWhen selected, sends location data every 10 minutes. The\nbeacon will automatically stop when you edit personal\ninformation. Must be manually reselected after edit\ncompletion.\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()
self.rawbox = gtk.HBox(False, 6)
self.rawtext = gtk.Entry()
self.rawtext.set_max_length(128)
self.rawtext.set_width_chars(38)
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
# changed to always available
self.rawbox.show()
win.pack_start(rightwin, False, False, 0)
rightwin.show()
win.show()
def clear_status(self, button=None):
# self.statusbuffer.delete(self.statusbuffer.get_start_iter(), self.statusbuffer.get_end_iter())
self.statusbuffer.set_text("")
def clear_message(self, button=None):
# self.messagebuffer.delete(self.messagebuffer.get_start_iter(), self.messagebuffer.get_end_iter())
self.messagebuffer.set_text("")
def connect_aprs(self, button):
if (self.sock == None):
self.clear_message()
self.clear_status()
if (self.validate_data() == False):
return False
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.\n")
self.disconnect_aprs(button)
if (response.find("Port Full") != -1):
self.status_write("Port Full.\n")
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.\n")
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)
# for now just cut the line at the first ":"
cuthere = recv_data.find(":")
self.status_write("%s\n" % recv_data[:cuthere+1])
# some incoming lines end in \n and some do not
if (recv_data[-1:] == "\n"):
self.status_write("%s\n" % recv_data[cuthere+1:])
else:
self.status_write("%s\n\n" % recv_data[cuthere+1:])
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\n" % 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)
statuslines = self.statusbuffer.get_line_count()
if (statuslines > MAXLINES):
deletehere = self.statusbuffer.get_iter_at_line(statuslines - MAXLINES)
self.statusbuffer.delete(self.statusbuffer.get_start_iter(), deletehere)
adjustment = self.statuswindow.get_vadjustment()
adjustment.set_value(adjustment.upper)
def message_write(self, text):
self.messagebuffer.insert(self.messagebuffer.get_end_iter(), text)
adjustment = self.messagewindow.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
# this check will only find messages that require acks
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 = data[firstcheck+12:secondcheck]
self.message_write("%s> %s" % (fromcall, message))
ackmessage = ":%s:ack%s" % (fromcall, sequence)
self.send_data(ackmessage)
id = "%s-%s" % (fromcall, sequence)
if (id in self.sent_acks):
self.timers.append(gobject.timeout_add(30 * 1000, self.send_ack, ackmessage))
self.timers.append(gobject.timeout_add(60 * 1000, self.send_ack, ackmessage))
self.timers.append(gobject.timeout_add(120 * 1000, self.send_ack, ackmessage))
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