diff --git a/lib/kiss/kiss_abstract.rb b/lib/kiss/kiss_abstract.rb
index 86c8dadbcf58c748551ed1969dd0ca736a5a35b6..914e14865e4e04fb84c28fda212f3b9d4584ca8c 100644
--- a/lib/kiss/kiss_abstract.rb
+++ b/lib/kiss/kiss_abstract.rb
@@ -6,7 +6,7 @@ module Kiss
     class KissAbstract
         include Abstractify::Abstract
 
-        abstract :write_interface, :read_interface
+        abstract :write_interface, :read_interface, :connect, :close
 
         protected
         def initialize(strip_df_start=true)
@@ -54,9 +54,132 @@ module Kiss
             (port << 4) & command_code
         end
 
-        protected
-        def write_setting(command, value)
-            write_interface([FEND] + [command] + escape_special_codes(value) + [FEND])
+        private
+        def self.extract_path(start, raw_frame)
+            full_path = []
+
+            (2...start).each do |i|
+                path = identity_as_string(extract_callsign(raw_frame[i * 7..-1]))
+                if path and path.length > 0
+                    if raw_frame[i * 7 + 6] & 0x80 != 0
+                        full_path << [path, '*'].join
+                    else
+                        full_path << path
+                    end
+                end
+            end
+            return full_path
+        end
+
+        private
+        def self.parse_identity_string(identity_string)
+            # If we are parsing a spent token then first lets get rid of the astresick suffix.
+            if identity_string[-1] == '*'
+                identity_string = identity_string[0..-1]
+            end
+
+            if identity_string.include? '-'
+                call_sign, ssid = identity_string.split('-')
+            else
+                call_sign = identity_string
+                ssid = 0
+            end
+
+            return {:callsign => call_sign, :ssid => ssid.to_i}
+        end
+
+        private
+        def self.identity_as_string(identity)
+            if identity[:ssid] and identity[:ssid] > 0
+                return [identity[:callsign], identity[:ssid].to_s].join('-')
+            else
+                return identity[:callsign]
+            end
+        end
+
+        private
+        def self.extract_callsign(raw_frame)
+            callsign_as_array = raw_frame[0...6].map { |x| (x >> 1).chr }
+            callsign = callsign_as_array.join.strip
+            ssid = (raw_frame[6] >> 1) & 0x0f
+            ssid = (ssid == nil or ssid == 0 ? nil : ssid)
+            return {:callsign => callsign, :ssid => ssid}
+        end
+
+        private
+        def self.encode_callsign(callsign)
+            call_sign = callsign[:callsign]
+
+            enc_ssid = (callsign[:ssid] << 1) | 0x60
+
+            if call_sign.include? '*'
+                call_sign.gsub!(/\*/, '')
+                enc_ssid |= 0x80
+            end
+
+            while call_sign.length < 6
+                call_sign = [call_sign, ' '].join
+            end
+
+            return call_sign.chars.map { |p| p.ord << 1 } + [enc_ssid]
+        end
+
+        private
+        def self.encode_frame(frame)
+            enc_frame = encode_callsign(parse_identity_string(frame[:destination].to_s)) + encode_callsign(parse_identity_string(frame[:source].to_s))
+
+            frame[:path].each do |hop|
+                enc_frame += encode_callsign(parse_identity_string(hop.to_s))
+            end
+
+            return enc_frame[0...-1] + [enc_frame[-1] | 0x01] + [Kiss::SLOT_TIME] + [0xf0] + frame[:payload].chars.map { |c| c.ord }
+        end
+
+        private
+        def self.decode_frame(raw_frame)
+            frame_len = raw_frame.length
+
+            if frame_len > 16
+                (0...frame_len - 2).each do |raw_slice|
+                    # Is address field length correct?
+                    if raw_frame[raw_slice] & 0x01 != 0 and ((raw_slice + 1) % 7) == 0
+                        i = (raw_slice.to_f + 1.0) / 7.0
+                        # Less than 2 callsigns?
+                        if 1.0 < i and i < 11.0
+                            if raw_frame[raw_slice + 1] & 0x03 == 0x03 and [0xf0, 0xcf].include? raw_frame[raw_slice + 2]
+                                payload_as_array = raw_frame[raw_slice + 3..-1].map { |b| b.chr }
+                                payload = payload_as_array.join
+                                destination = identity_as_string(extract_callsign(raw_frame))
+                                source = identity_as_string(extract_callsign(raw_frame[7..-1]))
+                                path = extract_path(i.to_i, raw_frame)
+                                return {:source => source, :destination => destination, :path => path, :payload => payload}
+                            end
+                        end
+                    end
+                end
+            end
+            return nil
+        end
+
+        private
+        def self.valid_frame(raw_frame)
+            frame_len = raw_frame.length
+
+            if frame_len > 16
+                (0...frame_len - 2).each do |raw_slice|
+                    # Is address field length correct?
+                    if raw_frame[raw_slice] & 0x01 != 0 and ((raw_slice + 1) % 7) == 0
+                        i = (raw_slice.to_f + 1.0) / 7.0
+                        # Less than 2 callsigns?
+                        if 1.0 < i and i < 11.0
+                            if raw_frame[raw_slice + 1] & 0x03 == 0x03 and [0xf0, 0xcf].include? raw_frame[raw_slice + 2]
+                                return true
+                            end
+                        end
+                    end
+                end
+            end
+            return false
         end
 
         private
@@ -117,6 +240,34 @@ module Kiss
             end
         end
 
+        private
+        def read_bytes(*args, **kwargs)
+              if @frame_buffer.length == 0
+                  fill_buffer
+              end
+
+              if @frame_buffer.length > 0
+                  return_frame = @frame_buffer[0]
+                  @frame_buffer.shift
+                  return return_frame
+              else
+                  return nil
+              end
+        end
+
+        private
+        def write_bytes(frame_bytes, port=0, *args, **kwargs)
+              kiss_packet = [FEND] + [KissAbstract.command_byte_combine(port, DATA_FRAME)] +
+                  KissAbstract.escape_special_codes(frame_bytes) + [FEND]
+
+              write_interface(kiss_packet)
+        end
+
+        protected
+        def write_setting(command, value)
+            write_interface([FEND] + [command] + escape_special_codes(value) + [FEND])
+        end
+
         public
         def connect(mode_init=nil, *args, **kwargs)
         end
@@ -127,29 +278,24 @@ module Kiss
 
         public
         def read(*args, **kwargs)
-            @lock.synchronize do
-                if @frame_buffer.length == 0
-                    fill_buffer
-                end
-
-                if @frame_buffer.length > 0
-                    return_frame = @frame_buffer[0]
-                    @frame_buffer.shift
-                    return return_frame
-                else
-                    return nil
-                end
-            end
+          @lock.synchronize do
+              frame = self.read_bytes(*args, **kwargs)
+              if frame and frame.length > 0
+                  return KissAbstract.decode_frame(frame)
+              else
+                  return nil
+              end
+          end
         end
 
         public
-        def write(frame_bytes, port=0, *args, **kwargs)
-            @lock.synchronize do
-                kiss_packet = [FEND] + [KissAbstract.command_byte_combine(port, DATA_FRAME)] +
-                    KissAbstract.escape_special_codes(frame_bytes) + [FEND]
-
-                write_interface(kiss_packet)
-            end
+        def write(frame, port=0, *args, **kwargs)
+          @lock.synchronize do
+              encoded_frame = KissAbstract.encode_frame(frame)
+              if KissAbstract.valid_frame(encoded_frame)
+                self.write_bytes(encoded_frame, *args, **kwargs)
+              end
+          end
         end
     end
 end
diff --git a/lib/kiss/kiss_serial.rb b/lib/kiss/kiss_serial.rb
index 16244ed1c893cafaed8a8bfc688f85c15d9939f5..4bad9d8ee86520162a2d12ed1278890c5d394451 100644
--- a/lib/kiss/kiss_serial.rb
+++ b/lib/kiss/kiss_serial.rb
@@ -83,4 +83,4 @@ module Kiss
             end
         end
     end
-end
\ No newline at end of file
+end
diff --git a/lib/kiss/test/kiss_mock.rb b/lib/kiss/test/kiss_mock.rb
index 9bbed21eff25bea81a5d0075ae7aa26d5859cda1..1f8aa238a1375f40ace129a2d9bca1d4bd1bde65 100644
--- a/lib/kiss/test/kiss_mock.rb
+++ b/lib/kiss/test/kiss_mock.rb
@@ -9,6 +9,14 @@ module Kiss
             @sent_to_interface = []
         end
 
+        public
+        def connect(mode_init=nil, *args, **kwargs)
+        end
+
+        public
+        def close(*args, **kwargs)
+        end
+
         protected
         def read_interface
             if @read_from_interface.length == 0
@@ -38,4 +46,4 @@ module Kiss
             return @sent_to_interface
         end
     end
-end
\ No newline at end of file
+end
diff --git a/spec/kiss/kiss_abstract_spec.rb b/spec/kiss/kiss_abstract_spec.rb
index fbb8a920771cfad46fd54338d41bb9045983a232..645723c85f8d40502cc08e8f00ae893e1c8c6b4b 100644
--- a/spec/kiss/kiss_abstract_spec.rb
+++ b/spec/kiss/kiss_abstract_spec.rb
@@ -1,15 +1,14 @@
 require_relative '../../lib/kiss/test/kiss_mock'
 require 'abstractify'
 
-# KG6WTF>S7TSUV,MTOSO-2,WIDE2*,qAR,KF6FIR-10:`17El#X-/kg6wtf@gosselinfamily.com
-ENCODED_FRAME = [192, 0, 75, 71, 54, 87, 84, 70, 62, 83, 55, 84, 83, 85, 86, 44, 77, 84, 79, 83, 79, 45, 50, 44, 87, 73,
-                 68, 69, 50, 42, 44, 113, 65, 82, 44, 75, 70, 54, 70, 73, 82, 45, 49, 48, 58, 96, 49, 55, 69, 108, 35,
-                 88, 45, 47, 107, 103, 54, 119, 116, 102, 64, 103, 111, 115, 115, 101, 108, 105, 110, 102, 97, 109, 105,
-                 108, 121, 46, 99, 111, 109, 192]
-DECODED_FRAME = [75, 71, 54, 87, 84, 70, 62, 83, 55, 84, 83, 85, 86, 44, 77, 84, 79, 83, 79, 45, 50, 44, 87, 73, 68,
-                 69, 50, 42, 44, 113, 65, 82, 44, 75, 70, 54, 70, 73, 82, 45, 49, 48, 58, 96, 49, 55, 69, 108, 35,
-                 88, 45, 47, 107, 103, 54, 119, 116, 102, 64, 103, 111, 115, 115, 101, 108, 105, 110, 102, 97, 109,
-                 105, 108, 121, 46, 99, 111, 109]
+ENCODED_FRAME = [192, 0, 158, 154, 142, 64, 64, 64, 96, 174, 100, 142, 154, 136, 64, 98, 174, 146, 136, 138, 98, 64, 98, 174, 146, 136, 138, 100, 64, 101, 3, 240, 116, 101, 115, 116, 95, 101, 110, 99, 111, 100, 101, 95, 102, 114, 97, 109, 101, 192]
+
+DECODED_FRAME = {
+                  :source => "W2GMD-1",
+                  :destination => "OMG",
+                  :payload => "test_encode_frame",
+                  :path => ['WIDE1-1', 'WIDE2-2']
+                }
 
 describe Kiss::KissAbstract do
   context "Do the things" do
@@ -17,6 +16,7 @@ describe Kiss::KissAbstract do
       kiss_mock = Kiss::KissMock.new
       kiss_mock.add_read_from_interface(ENCODED_FRAME)
       translated_frame = kiss_mock.read
+      #puts translated_frame
       expect(translated_frame).to eql(DECODED_FRAME)
 
       kiss_mock = Kiss::KissMock.new