diff --git a/src/apex/aprs/__init__.py b/src/apex/aprs/__init__.py
index bbc63b0d612975b0af0696e35cc66d35338cc3b9..61067efe7f1e3fc52831f88a3ddcfa703eca5172 100644
--- a/src/apex/aprs/__init__.py
+++ b/src/apex/aprs/__init__.py
@@ -23,7 +23,8 @@ from __future__ import print_function
 import logging
 
 from .aprs import Aprs  # noqa: F401
-from .aprs_internet_service import AprsInternetService  # noqa: F401
+from .igate import IGate  # noqa: F401
+from .igate import ReconnectingPacketBuffer  # noqa: F401
 
 __author__ = 'Jeffrey Phillips Freeman (WI2ARD)'
 __maintainer__ = 'Jeffrey Phillips Freeman (WI2ARD)'
diff --git a/src/apex/aprs/aprs.py b/src/apex/aprs/aprs.py
index b54c780973f9b41ce1dad725ca80d7d01645f338..ca6f966ab86505c614838c415f690013e988b3ca 100644
--- a/src/apex/aprs/aprs.py
+++ b/src/apex/aprs/aprs.py
@@ -178,7 +178,13 @@ class Aprs(object):
             ssid = 0
         return {'callsign': call_sign, 'ssid': int(ssid)}
 
-    def write(self, frame, port=0):
+    def connect(self, *args, **kwargs):
+        self.data_stream.connect(*args, **kwargs)
+
+    def close(self, *args, **kwargs):
+        self.data_stream.close(*args, **kwargs)
+
+    def write(self, frame, *args, **kwargs):
         """Writes APRS-encoded frame to KISS device.
 
         :param frame: APRS frame to write to KISS device.
@@ -186,13 +192,13 @@ class Aprs(object):
         """
         with self.lock:
             encoded_frame = Aprs.__encode_frame(frame)
-            self.data_stream.write(encoded_frame, port)
+            self.data_stream.write(encoded_frame, *args, **kwargs)
 
-    def read(self):
+    def read(self, *args, **kwargs):
         """Reads APRS-encoded frame from KISS device.
         """
         with self.lock:
-            frame = self.data_stream.read()
+            frame = self.data_stream.read(*args, **kwargs)
             if frame is not None and len(frame):
                 return Aprs.__decode_frame(frame)
             else:
diff --git a/src/apex/aprs/aprs_internet_service.py b/src/apex/aprs/aprs_internet_service.py
deleted file mode 100644
index ff362d53fb03a3a854055c7631b4f7439d18cb4f..0000000000000000000000000000000000000000
--- a/src/apex/aprs/aprs_internet_service.py
+++ /dev/null
@@ -1,149 +0,0 @@
-#!/usr/bin/env python
-# -*- coding: utf-8 -*-
-
-"""APRS Internet Service Class Definitions"""
-
-# These imports are for python3 compatability inside python2
-from __future__ import absolute_import
-from __future__ import division
-from __future__ import print_function
-
-import logging
-import socket
-import requests
-
-from apex.aprs import constants as aprs_constants
-from apex.aprs import util as aprs_util
-
-__author__ = 'Jeffrey Phillips Freeman (WI2ARD)'
-__maintainer__ = 'Jeffrey Phillips Freeman (WI2ARD)'
-__email__ = 'jeffrey.freeman@syncleus.com'
-__license__ = 'Apache License, Version 2.0'
-__copyright__ = 'Copyright 2016, Syncleus, Inc. and contributors'
-__credits__ = []
-
-
-class AprsInternetService(object):
-
-    """APRS Object."""
-
-    logger = logging.getLogger(__name__)
-    logger.setLevel(aprs_constants.LOG_LEVEL)
-    console_handler = logging.StreamHandler()
-    console_handler.setLevel(aprs_constants.LOG_LEVEL)
-    console_handler.setFormatter(aprs_constants.LOG_FORMAT)
-    logger.addHandler(console_handler)
-    logger.propagate = False
-
-    def __init__(self, user, password='-1', input_url=None):
-        self.user = user
-        self._url = input_url or aprs_constants.APRSIS_URL
-        self._auth = ' '.join(
-            ['user', user, 'pass', password, 'vers', 'APRS Python Module'])
-        self.aprsis_sock = None
-
-    def connect(self, server=None, port=None, aprs_filter=None):
-        """
-        Connects & logs in to APRS-IS.
-
-        :param server: Optional alternative APRS-IS server.
-        :param port: Optional APRS-IS port.
-        :param filter: Optional filter to use.
-        :type server: str
-        :type port: int
-        :type filte: str
-        """
-        server = server or aprs_constants.APRSIS_SERVER
-        port = port or aprs_constants.APRSIS_FILTER_PORT
-        aprs_filter = aprs_filter or '/'.join(['p', self.user])
-
-        self.full_auth = ' '.join([self._auth, 'filter', aprs_filter])
-
-        self.server = server
-        self.port = port
-        self.aprsis_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
-        self.aprsis_sock.connect((server, port))
-        self.logger.info('Connected to server=%s port=%s', server, port)
-        self.logger.debug('Sending full_auth=%s', self.full_auth)
-        self.aprsis_sock.sendall((self.full_auth + '\n\r').encode('ascii'))
-
-    def send(self, frame, headers=None, protocol='TCP'):
-        """
-        Sends message to APRS-IS.
-
-        :param message: Message to send to APRS-IS.
-        :param headers: Optional HTTP headers to post.
-        :param protocol: Protocol to use: One of TCP, HTTP or UDP.
-        :type message: str
-        :type headers: dict
-
-        :return: True on success, False otherwise.
-        :rtype: bool
-        """
-        self.logger.debug(
-            'message=%s headers=%s protocol=%s', str(frame), headers, protocol)
-
-        if 'TCP' in protocol:
-            self.logger.debug('sending message=%s', str(frame))
-            # TODO: simplify this
-            message = bytearray()
-            for frame_chr in aprs_util.format_aprs_frame(frame):
-                message.append(ord(frame_chr))
-            message_sent = False
-            while not message_sent:
-                self.aprsis_sock.sendall(message)
-                message_sent = True
-            return True
-        elif 'HTTP' in protocol:
-            content = '\n'.join([self._auth, aprs_util.format_aprs_frame(frame)])
-            headers = headers or aprs_constants.APRSIS_HTTP_HEADERS
-            result = requests.post(self._url, data=content, headers=headers)
-            return 204 == result.status_code
-        elif 'UDP' in protocol:
-            content = '\n'.join([self._auth, aprs_util.format_aprs_frame(frame)])
-            sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
-            sock.sendto(
-                content,
-                (aprs_constants.APRSIS_SERVER, aprs_constants.APRSIS_RX_PORT)
-            )
-            return True
-
-    def receive(self, callback=None):
-        """
-        Receives from APRS-IS.
-
-        :param callback: Optional callback to deliver data to.
-        :type callback: func
-        """
-        recvd_data = ''
-
-        try:
-            while 1:
-                recv_data = self.aprsis_sock.recv(aprs_constants.RECV_BUFFER)
-
-                if not recv_data:
-                    break
-
-                recvd_data += recv_data
-
-                self.logger.debug('recv_data=%s', recv_data.strip())
-
-                if recvd_data.endswith('\r\n'):
-                    lines = recvd_data.strip().split('\r\n')
-                    recvd_data = ''
-                else:
-                    lines = recvd_data.split('\r\n')
-                    recvd_data = str(lines.pop(-1))
-
-                for line in lines:
-                    if line.startswith('#'):
-                        if 'logresp' in line:
-                            self.logger.debug('logresp=%s', line)
-                    else:
-                        self.logger.debug('line=%s', line)
-                        if callback:
-                            callback(line)
-
-        except socket.error as sock_err:
-            self.logger.error(sock_err)
-            raise
diff --git a/src/apex/aprs/igate.py b/src/apex/aprs/igate.py
new file mode 100644
index 0000000000000000000000000000000000000000..14b12d4c58d17ff66c5d910eb51eff05b71f205f
--- /dev/null
+++ b/src/apex/aprs/igate.py
@@ -0,0 +1,288 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+"""APRS Internet Service Class Definitions"""
+
+# These imports are for python3 compatability inside python2
+from __future__ import absolute_import
+from __future__ import division
+from __future__ import print_function
+
+import logging
+import select
+import socket
+import threading
+import time
+import cachetools
+import requests
+
+from apex.aprs import constants as aprs_constants
+from apex.aprs import util as aprs_util
+
+__author__ = 'Jeffrey Phillips Freeman (WI2ARD)'
+__maintainer__ = 'Jeffrey Phillips Freeman (WI2ARD)'
+__email__ = 'jeffrey.freeman@syncleus.com'
+__license__ = 'Apache License, Version 2.0'
+__copyright__ = 'Copyright 2016, Syncleus, Inc. and contributors'
+__credits__ = []
+
+
+class ReconnectingPacketBuffer(object):
+
+    STARTING_WAIT_TIME = 2
+    MAX_WAIT_TIME = 300
+    WAIT_TIME_MULTIPLIER = 2
+    MAX_INDEX = 1000000
+
+    def __init__(self, packet_layer):
+        self.packet_layer = packet_layer
+        self.to_packet_layer = cachetools.TTLCache(10, 30)
+        self.current_index = 0
+        self.from_packet_layer = cachetools.TTLCache(10, 30)
+        self.connect_thread = None
+        self.lock = threading.Lock()
+        self.running = False
+        self.reconnect_wait_time = self.STARTING_WAIT_TIME
+        self.last_connect_attempt = None
+        self.connect_args = None
+        self.connect_kwargs = None
+        self.connected = False
+
+    def __next_index(self):
+        if self.current_index > self.MAX_INDEX:
+            self.current_index = 0
+        return ++self.current_index
+
+    def __increment_wait_time(self):
+        self.reconnect_wait_time *= self.WAIT_TIME_MULTIPLIER
+        if self.reconnect_wait_time > self.MAX_WAIT_TIME:
+            self.reconnect_wait_time = self.MAX_WAIT_TIME
+
+    def __reset_wait_time(self):
+        self.reconnect_wait_time = self.STARTING_WAIT_TIME
+
+    def __run(self):
+        while self.running:
+            if not self.connected:
+                if not self.last_connect_attempt or time.time() - self.last_connect_attempt > self.reconnect_wait_time:
+                    try:
+                        self.last_connect_attempt = time.time()
+                        self.packet_layer.connect(*self.connect_args, **self.connect_kwargs)
+                        self.connected = True
+                    except IOError:
+                        try:
+                            self.packet_layer.close()
+                        except IOError:
+                            pass
+                        self.__increment_wait_time()
+                else:
+                    time.sleep(1)
+            else:
+                io_occured = False
+
+                # lets attempt to read in a packet
+                try:
+                    read_packet = self.packet_layer.read()
+                    self.__reset_wait_time()
+                    if read_packet:
+                        with self.lock:
+                                self.from_packet_layer[self.__next_index()] = read_packet
+                        io_occured = True
+                except IOError:
+                    try:
+                        self.packet_layer.close()
+                    except IOError:
+                        pass
+                    self.connected = False
+                    continue
+
+                # lets try to write a packet, if any are waiting.
+                write_packet = None
+                with self.lock:
+                    if self.to_packet_layer:
+                        write_packet = self.to_packet_layer.popitem()[1]
+                if write_packet:
+                    try:
+                        self.packet_layer.write(write_packet)
+                        io_occured = True
+                        self.__reset_wait_time()
+                    except IOError:
+                        self.to_packet_layer[self.__next_index()] = write_packet
+                        try:
+                            self.packet_layer.close()
+                        except IOError:
+                            pass
+                        self.connected = False
+                        continue
+
+                if not io_occured:
+                    time.sleep(1)
+        try:
+            self.packet_layer.close()
+        except IOError:
+            pass
+
+    def connect(self, *args, **kwargs):
+        with self.lock:
+            if self.connect_thread:
+                raise RuntimeError('already connected')
+
+            self.running = True
+            self.connect_args = args
+            self.connect_kwargs = kwargs
+            self.connect_thread = threading.Thread(target=self.__run)
+            self.connect_thread.start()
+
+    def close(self):
+        with self.lock:
+            if not self.connect_thread:
+                raise RuntimeError('not connected')
+
+            self.running = False
+            self.connect_thread.join()
+            self.connect_thread = None
+
+    def read(self):
+        with self.lock:
+            if self.from_packet_layer:
+                return self.from_packet_layer.popitem()[1]
+        return None
+
+    def write(self, packet):
+        with self.lock:
+            self.to_packet_layer[self.__next_index()] = packet
+
+
+class IGate(object):
+
+    """APRS Object."""
+
+    logger = logging.getLogger(__name__)
+    logger.setLevel(aprs_constants.LOG_LEVEL)
+    console_handler = logging.StreamHandler()
+    console_handler.setLevel(aprs_constants.LOG_LEVEL)
+    console_handler.setFormatter(aprs_constants.LOG_FORMAT)
+    logger.addHandler(console_handler)
+    logger.propagate = False
+
+    def __init__(self, user, password='-1', input_url=None):
+        self.user = user
+        self._url = input_url or aprs_constants.APRSIS_URL
+        self._auth = ' '.join(
+            ['user', user, 'pass', password, 'vers', 'APRS Python Module'])
+        self.aprsis_sock = None
+        self.data_buffer = ''
+        self.packet_buffer = []
+
+    def __reset_buffer(self):
+        self.data_buffer = ''
+        self.packet_buffer = []
+
+    def connect(self, server=None, port=None, aprs_filter=None):
+        """
+        Connects & logs in to APRS-IS.
+
+        :param server: Optional alternative APRS-IS server.
+        :param port: Optional APRS-IS port.
+        :param filter: Optional filter to use.
+        :type server: str
+        :type port: int
+        :type filte: str
+        """
+        if not self.aprsis_sock:
+            self.__reset_buffer()
+
+            server = server or aprs_constants.APRSIS_SERVER
+            port = port or aprs_constants.APRSIS_FILTER_PORT
+            aprs_filter = aprs_filter or '/'.join(['p', self.user])
+
+            self.full_auth = ' '.join([self._auth, 'filter', aprs_filter])
+
+            self.server = server
+            self.port = port
+            self.aprsis_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+            self.aprsis_sock.connect((server, port))
+            self.logger.info('Connected to server=%s port=%s', server, port)
+            self.logger.debug('Sending full_auth=%s', self.full_auth)
+            self.aprsis_sock.sendall((self.full_auth + '\n\r').encode('ascii'))
+
+    def close(self):
+        if self.aprsis_sock:
+            self.aprsis_sock.close()
+            self.__reset_buffer()
+            self.aprsis_sock = None
+
+    def write(self, frame_decoded, headers=None, protocol='TCP'):
+        """
+        Sends message to APRS-IS.
+
+        :param message: Message to send to APRS-IS.
+        :param headers: Optional HTTP headers to post.
+        :param protocol: Protocol to use: One of TCP, HTTP or UDP.
+        :type message: str
+        :type headers: dict
+
+        :return: True on success, False otherwise.
+        :rtype: bool
+        """
+
+        frame = aprs_util.encode_frame(frame_decoded)
+        if 'TCP' in protocol:
+            self.aprsis_sock.sendall(frame)
+            return True
+        elif 'HTTP' in protocol:
+            content = '\n'.join([self._auth, frame])
+            headers = headers or aprs_constants.APRSIS_HTTP_HEADERS
+            result = requests.post(self._url, data=content, headers=headers)
+            return 204 == result.status_code
+        elif 'UDP' in protocol:
+            content = '\n'.join([self._auth, frame])
+            sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
+            sock.sendto(
+                content,
+                (aprs_constants.APRSIS_SERVER, aprs_constants.APRSIS_RX_PORT)
+            )
+            return True
+
+    def read(self, filter_logresp=True):
+        """
+        Receives from APRS-IS.
+
+        :param callback: Optional callback to deliver data to.
+        :type callback: func
+        """
+        # check if there is any data waiting
+        read_more = True
+        while read_more:
+            selected = select.select([self.aprsis_sock], [], [], 0)
+            if len(selected[0]) > 0:
+                recvd_data = self.aprsis_sock.recv(aprs_constants.RECV_BUFFER)
+                if not recvd_data:
+                    self.data_buffer += recvd_data
+                else:
+                    read_more = False
+            else:
+                read_more = False
+
+        # check for any complete packets and move them to the packet buffer
+        if '\r\n' in self.data_buffer:
+            partial = True
+            if self.data_buffer.endswith('\r\n'):
+                partial = False
+            packets = recvd_data.split('\r\n')
+            if partial:
+                self.data_buffer = str(packets.pop(-1))
+            else:
+                self.data_buffer = ''
+            for packet in packets:
+                self.packet_buffer += [str(packet)]
+
+        # return the next packet that matches the filter
+        while len(self.packet_buffer):
+            packet = self.packet_buffer.pop(0)
+            if filter_logresp and packet.startswith('#') and 'logresp' in packet:
+                pass
+            else:
+                return aprs_util.decode_frame(packet)
+
+        return None
diff --git a/src/apex/aprs/util.py b/src/apex/aprs/util.py
index 1fdd4f1ac3031cc0da22cce70dfd65be7704a61e..ece5b8a70498b02a2a410dde613a8fe1e06c3f21 100755
--- a/src/apex/aprs/util.py
+++ b/src/apex/aprs/util.py
@@ -70,7 +70,7 @@ def dec2dm_lng(dec):
     return ''.join([str(abs_deg), '%.2f' % dec_min[1], suffix])
 
 
-def decode_aprs_ascii_frame(ascii_frame):
+def decode_frame(frame):
     """
     Breaks an ASCII APRS Frame down to it's constituent parts.
 
@@ -80,11 +80,11 @@ def decode_aprs_ascii_frame(ascii_frame):
     :returns: Dictionary of APRS Frame parts: source, destination, path, text.
     :rtype: dict
     """
-    logging.debug('frame=%s', ascii_frame)
+    logging.debug('frame=%s', frame)
     decoded_frame = {}
     frame_so_far = ''
 
-    for char in ascii_frame:
+    for char in frame:
         if '>' in char and 'source' not in decoded_frame:
             decoded_frame['source'] = frame_so_far
             frame_so_far = ''
@@ -100,20 +100,7 @@ def decode_aprs_ascii_frame(ascii_frame):
     return decoded_frame
 
 
-def format_path(path_list):
-    """
-    Formats path from raw APRS KISS frame.
-
-    :param path_list: List of path elements.
-    :type path_list: list
-
-    :return: Formatted APRS path.
-    :rtype: str
-    """
-    return ','.join(path_list)
-
-
-def format_aprs_frame(frame):
+def encode_frame(frame):
     """
     Formats APRS frame-as-dict into APRS frame-as-string.
 
@@ -131,6 +118,19 @@ def format_aprs_frame(frame):
     return formatted_frame
 
 
+def format_path(path_list):
+    """
+    Formats path from raw APRS KISS frame.
+
+    :param path_list: List of path elements.
+    :type path_list: list
+
+    :return: Formatted APRS path.
+    :rtype: str
+    """
+    return ','.join(path_list)
+
+
 def valid_callsign(callsign):
     """
     Validates callsign.
diff --git a/src/apex/cli.py b/src/apex/cli.py
index bfbafe0e24acda9376d1a6374d4ad7e391203263..962d195e0c57954499ae9667b3dbd5bb197efe2f 100644
--- a/src/apex/cli.py
+++ b/src/apex/cli.py
@@ -140,6 +140,8 @@ def main(verbose, configfile):
                 tnc_port = int(config.get(port_section, 'tnc_port'))
                 port_map[port_name] = {'identifier': port_identifier, 'net': port_net, 'tnc': aprs_tnc,
                                        'tnc_port': tnc_port}
+
+    aprsis = None
     if config.has_section('APRS-IS'):
         aprsis_callsign = config.get('APRS-IS', 'callsign')
         if config.has_option('APRS-IS', 'password'):
@@ -148,7 +150,7 @@ def main(verbose, configfile):
             aprsis_password = -1
         aprsis_server = config.get('APRS-IS', 'server')
         aprsis_server_port = config.get('APRS-IS', 'server_port')
-        aprsis = apex.aprs.AprsInternetService(aprsis_callsign, aprsis_password)
+        aprsis = apex.aprs.ReconnectingPacketBuffer(apex.aprs.IGate(aprsis_callsign, aprsis_password))
         aprsis.connect(aprsis_server, int(aprsis_server_port))
 
     click.echo("Press ctrl + c at any time to exit")
@@ -165,7 +167,7 @@ def main(verbose, configfile):
                 click.echo('Plugin found at the following location: %s' % repr(plugin_loader))
             loaded_plugin = load_plugin(plugin_loader)
             plugin_modules.append(loaded_plugin)
-            new_thread = threading.Thread(target=loaded_plugin.connect, args=(config, port_map, packet_cache, aprsis))
+            new_thread = threading.Thread(target=loaded_plugin.start, args=(config, port_map, packet_cache, aprsis))
             new_thread.start()
             plugin_threads.append(new_thread)
     except IOError:
@@ -177,10 +179,13 @@ def main(verbose, configfile):
 
         running = False
 
+        click.echo()
         click.echo('SIGINT caught, shutting down..')
 
         for plugin_module in plugin_modules:
             plugin_module.stop()
+        if aprsis:
+            aprsis.close()
         # Lets wait until all the plugins successfully end
         for plugin_thread in plugin_threads:
             plugin_thread.join()
diff --git a/src/apex/plugins/apexparadigm/__init__.py b/src/apex/plugins/apexparadigm/__init__.py
index e7481f322dc725e584bc95a471ada0e72c4aecfe..efa34360c4c0651a635903a58fe62d45037e8036 100644
--- a/src/apex/plugins/apexparadigm/__init__.py
+++ b/src/apex/plugins/apexparadigm/__init__.py
@@ -89,8 +89,8 @@ class ApexParadigmPlugin(object):
                                 if frame_hash not in self.packet_cache.values():
                                     self.packet_cache[str(frame_hash)] = frame_hash
                                     port['tnc'].write(frame, port['tnc_port'])
-                                    self.aprsis.send(frame)
-                                    click.echo(port_name + ' >> ' + apex.aprs.util.format_aprs_frame(frame))
+                                    self.aprsis.write(frame)
+                                    click.echo(port_name + ' >> ' + apex.aprs.util.encode_frame(frame))
                                 return
                         else:
                             if port['net'].startswith(node):
@@ -100,8 +100,8 @@ class ApexParadigmPlugin(object):
                                 if frame_hash not in self.packet_cache.values():
                                     self.packet_cache[str(frame_hash)] = frame_hash
                                     port['tnc'].write(frame, port['tnc_port'])
-                                    self.aprsis.send(frame)
-                                    click.echo(port_name + ' >> ' + apex.aprs.util.format_aprs_frame(frame))
+                                    self.aprsis.write(frame)
+                                    click.echo(port_name + ' >> ' + apex.aprs.util.encode_frame(frame))
                                 return
                     if node == port_callsign and ssid == port_ssid:
                         if ssid is 0:
@@ -112,8 +112,8 @@ class ApexParadigmPlugin(object):
                         if frame_hash not in self.packet_cache.values():
                             self.packet_cache[str(frame_hash)] = frame_hash
                             port['tnc'].write(frame, port['tnc_port'])
-                            self.aprsis.send(frame)
-                            click.echo(port_name + ' >> ' + apex.aprs.util.format_aprs_frame(frame))
+                            self.aprsis.write(frame)
+                            click.echo(port_name + ' >> ' + apex.aprs.util.encode_frame(frame))
                         return
                     elif node == 'GATE' and port['net'].startswith('2M'):
                         frame['path'] = frame['path'][:hop_index] + [recv_port['identifier'] + '*'] + [node + '*'] +\
@@ -122,8 +122,8 @@ class ApexParadigmPlugin(object):
                         if frame_hash not in self.packet_cache.values():
                             self.packet_cache[str(frame_hash)] = frame_hash
                             port['tnc'].write(frame, port['tnc_port'])
-                            self.aprsis.send(frame)
-                            click.echo(port_name + ' >> ' + apex.aprs.util.format_aprs_frame(frame))
+                            self.aprsis.write(frame)
+                            click.echo(port_name + ' >> ' + apex.aprs.util.encode_frame(frame))
                         return
                 if node.startswith('WIDE') and ssid > 1:
                     frame['path'] = frame['path'][:hop_index] + [recv_port['identifier'] + '*'] +\
@@ -132,8 +132,8 @@ class ApexParadigmPlugin(object):
                     if frame_hash not in self.packet_cache.values():
                         self.packet_cache[str(frame_hash)] = frame_hash
                         recv_port['tnc'].write(frame, recv_port['tnc_port'])
-                        self.aprsis.send(frame)
-                        click.echo(recv_port_name + ' >> ' + apex.aprs.util.format_aprs_frame(frame))
+                        self.aprsis.write(frame)
+                        click.echo(recv_port_name + ' >> ' + apex.aprs.util.encode_frame(frame))
                     return
                 elif node.startswith('WIDE') and ssid is 1:
                     frame['path'] = frame['path'][:hop_index] + [recv_port['identifier'] + '*'] + [node + '*'] + frame['path'][hop_index+1:]
@@ -141,15 +141,15 @@ class ApexParadigmPlugin(object):
                     if frame_hash not in self.packet_cache.values():
                         self.packet_cache[str(frame_hash)] = frame_hash
                         recv_port['tnc'].write(frame, recv_port['tnc_port'])
-                        self.aprsis.send(frame)
-                        click.echo(recv_port_name + ' >> ' + apex.aprs.util.format_aprs_frame(frame))
+                        self.aprsis.write(frame)
+                        click.echo(recv_port_name + ' >> ' + apex.aprs.util.encode_frame(frame))
                     return
                 elif node.startswith('WIDE') and ssid is 0:
                     frame['path'][hop_index] = node + '*'
                     # no return
                 else:
                     # If we didnt digipeat it then we didn't modify the frame, send it to aprsis as-is
-                    self.aprsis.send(frame)
+                    self.aprsis.write(frame)
                     return
 
     def __preemptive_digipeat(self, frame, recv_port, recv_port_name):
@@ -273,8 +273,8 @@ class ApexParadigmPlugin(object):
         if frame_hash not in self.packet_cache.values():
             self.packet_cache[str(frame_hash)] = frame_hash
             selected_hop['port']['tnc'].write(frame, selected_hop['port']['tnc_port'])
-            self.aprsis.send(frame)
-            click.echo(selected_hop['port_name'] + ' >> ' + apex.aprs.util.format_aprs_frame(frame))
+            self.aprsis.write(frame)
+            click.echo(selected_hop['port_name'] + ' >> ' + apex.aprs.util.encode_frame(frame))
         return
 
     def stop(self):
diff --git a/src/apex/plugins/beacon/__init__.py b/src/apex/plugins/beacon/__init__.py
index 885e58a1c2627caba23ddb09fb02e2944aced6ce..fec7238bee42485cc261d52981e87a96e452d348 100644
--- a/src/apex/plugins/beacon/__init__.py
+++ b/src/apex/plugins/beacon/__init__.py
@@ -76,6 +76,6 @@ class BeaconPlugin(object):
                     if frame_hash not in self.packet_cache.values():
                         self.packet_cache[str(frame_hash)] = frame_hash
                         port['tnc'].write(beacon_frame, port['tnc_port'])
-                        click.echo(port_name + ' >> ' + apex.aprs.util.format_aprs_frame(beacon_frame))
+                        click.echo(port_name + ' >> ' + apex.aprs.util.encode_frame(beacon_frame))
             else:
                 time.sleep(1)
diff --git a/src/apex/plugins/id/__init__.py b/src/apex/plugins/id/__init__.py
index 931227bc13be93043949b07ebfb71201da4243fa..df4598fcd68732a9bbcfa1e1caf737667d03ba83 100644
--- a/src/apex/plugins/id/__init__.py
+++ b/src/apex/plugins/id/__init__.py
@@ -76,6 +76,6 @@ class IdPlugin(object):
                     if frame_hash not in self.packet_cache.values():
                         self.packet_cache[str(frame_hash)] = frame_hash
                         port['tnc'].write(id_frame, port['tnc_port'])
-                        click.echo(port_name + ' >> ' + apex.aprs.util.format_aprs_frame(id_frame))
+                        click.echo(port_name + ' >> ' + apex.aprs.util.encode_frame(id_frame))
             else:
                 time.sleep(1)
diff --git a/src/apex/plugins/status/__init__.py b/src/apex/plugins/status/__init__.py
index bcfb1dd3b554ea5fcc9f359ac2bce24f84544cd9..d780f5a5d76e74c42255d520637f26200e98c897 100644
--- a/src/apex/plugins/status/__init__.py
+++ b/src/apex/plugins/status/__init__.py
@@ -78,6 +78,6 @@ class StatusPlugin(object):
                     if frame_hash not in self.packet_cache.values():
                         self.packet_cache[str(frame_hash)] = frame_hash
                         port['tnc'].write(status_frame, port['tnc_port'])
-                        print(port_name + ' >> ' + apex.aprs.util.format_aprs_frame(status_frame))
+                        print(port_name + ' >> ' + apex.aprs.util.encode_frame(status_frame))
             else:
                 time.sleep(1)
diff --git a/tests/test_aprs.py b/tests/test_aprs.py
index 1616bf97d85991af187e6ae57c7947e583486b14..481ad52791a29139a22ea40e8b010d70e7712bc3 100644
--- a/tests/test_aprs.py
+++ b/tests/test_aprs.py
@@ -11,8 +11,8 @@ from __future__ import print_function
 import sys
 import unittest
 
-import apex.aprs.aprs_internet_service
 import apex.aprs.constants
+import apex.aprs.igate
 
 if sys.version_info < (3, 0):
     import httpretty
@@ -23,6 +23,7 @@ __email__ = 'jeffrey.freeman@syncleus.com'
 __license__ = 'Apache License, Version 2.0'
 __copyright__ = 'Copyright 2016, Syncleus, Inc. and contributors'
 __credits__ = []
+__version__ = '0.0.2'
 
 ALPHABET = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
 NUMBERS = '0123456789'
@@ -189,7 +190,7 @@ class AprsTest(unittest.TestCase):  # pylint: disable=R0904
                 status=204
             )
 
-            aprs_conn = apex.aprs.aprs_internet_service.AprsInternetService(
+            aprs_conn = apex.aprs.igate.IGate(
                 user=self.fake_callsign,
                 input_url=self.fake_server
             )
@@ -202,7 +203,7 @@ class AprsTest(unittest.TestCase):  # pylint: disable=R0904
                 'text': '=3745.00N/12227.00W-Simulated Location'
             }
 
-            result = aprs_conn.send(msg)
+            result = aprs_conn.write(msg)
 
             self.assertTrue(result)
 
@@ -217,7 +218,7 @@ class AprsTest(unittest.TestCase):  # pylint: disable=R0904
                 status=401
             )
 
-            aprs_conn = apex.aprs.aprs_internet_service.AprsInternetService(
+            aprs_conn = apex.aprs.igate.IGate(
                 user=self.fake_callsign,
                 input_url=self.fake_server
             )
@@ -230,7 +231,7 @@ class AprsTest(unittest.TestCase):  # pylint: disable=R0904
                 'text': '=3745.00N/12227.00W-Simulated Location'
             }
 
-            result = aprs_conn.send(msg, protocol='HTTP')
+            result = aprs_conn.write(msg, protocol='HTTP')
 
             self.assertFalse(result)
 
@@ -239,7 +240,7 @@ class AprsTest(unittest.TestCase):  # pylint: disable=R0904
             """
             Tests APRS-IS binding against a real APRS-IS server.
             """
-            aprs_conn = apex.aprs.aprs_internet_service.AprsInternetService(
+            aprs_conn = apex.aprs.igate.IGate(
                 user=self.real_callsign,
                 input_url=self.real_server
             )
@@ -253,6 +254,6 @@ class AprsTest(unittest.TestCase):  # pylint: disable=R0904
             }
             self.logger.debug(locals())
 
-            result = aprs_conn.send(msg)
+            result = aprs_conn.write(msg)
 
             self.assertFalse(result)
diff --git a/tests/test_util.py b/tests/test_util.py
index 1a3bad956c5da1ae58376e06a1785d7a1bd4c376..0423c29ed8ec39158457915c318b2ebfd666d38c 100644
--- a/tests/test_util.py
+++ b/tests/test_util.py
@@ -195,7 +195,7 @@ class AprsUtilTestCase(unittest.TestCase):  # pylint: disable=R0904
         ascii_frame = (
             'W2GMD-9>APOTC1,WIDE1-1,WIDE2-1:!3745.94N/12228.05W>118/010/'
             'A=000269 38C=Temp http://w2gmd.org/ Twitter: @ampledata')
-        frame = apex.aprs.util.decode_aprs_ascii_frame(ascii_frame)
+        frame = apex.aprs.util.decode_frame(ascii_frame)
         self.assertEqual(
             {
                 'source': 'W2GMD-9',
@@ -218,7 +218,7 @@ class AprsUtilTestCase(unittest.TestCase):  # pylint: disable=R0904
             'path': ['WIDE1-1'],
             'text': 'test_format_aprs_frame'
         }
-        formatted_frame = apex.aprs.util.format_aprs_frame(frame)
+        formatted_frame = apex.aprs.util.encode_frame(frame)
         self.assertEqual(
             formatted_frame,
             'W2GMD-1>OMG,WIDE1-1:test_format_aprs_frame'