ampledata / kiss

A pure-Python implementation of the KISS Protocol for communicating with serial TNC devices for use with Amateur Radio.
Other
84 stars 35 forks source link

AttributeError: 'Frame' object has no attribute 'encode_kiss' #26

Open zyphlar opened 4 years ago

zyphlar commented 4 years ago

I'm not a python pro, but it seems the serial_write example doesn't work too well.

AttributeError: 'Frame' object has no attribute 'text'

and then

AttributeError: 'Frame' object has no attribute 'encode_kiss'

As far as I can tell, I should be using aprs/kiss_classes.py but seem to be getting aprs/classes.py instead and I'm not experienced enough to know how to make that happen.

simseve commented 3 years ago

Same issue here.... but also not sure how to make sure aprs/kiss_classes.py are invoked correctly. Is the author still keen to give us an hand? I hope so. thanks for the great work.

w1oko commented 3 years ago

Same issue here. I'm trying to use the example to send KISS frames, but receive the same errors. I dug into the code a bit and also noticed that there are two sets of class files - classes.py and kiss_classes.py. The code in the example seems to depend on kiss_classes.py, but the class "Frame" is defined in both files, and only classes.py is called from the init file. I have tried renaming the Frame class in the kiss_classes.py file and added that into the init file, which gets me a bit further, but end up with other errors. Any help / insights from the author would be much appreciated. This will be an awesome piece of code to work on some cool IOT via Radio projects. I'm working on interfacing with an MQTT broker and this is the last piece of the puzzle that I can't figure out.

KenwoodFox commented 3 years ago

Also the same issue, what could have happened? maybe an issue with the library changing?...

sticky-tea commented 1 year ago

Same issue

KenwoodFox commented 1 year ago

Same issue

Any luck on a solution?

w1oko commented 1 year ago

It has been a while since I worked on this, but I did get it working a few years ago. Unfortunately I don't have everything in front of me at the moment, and I'm buried with other priorities, but my recollection was that there was a timing issue and by delaying the execution of some piece of code (of course I can't remember what!!) ultimately solved the problem. Hopefully this vague clue helps get you going. When I have more time to return to this, I'm sure it will come back to me, and I will make an effort to document the solution and post here.

KenwoodFox commented 1 year ago

Still working on this, bleh

modernham commented 8 months ago

Here I am too now. I've been banging my head against this for a few days. It seems encode_kiss is gone, but there is an encode_ax25 that doesn't seem to work. I even dug up the old code with encode_kiss and couldn't get it to work either. I tried feeding it raw frames from a capture file.

It's a shame when projects like these go unmaintained. It would probably be faster to just build it all form scratch again. Even the test cases in this repository still reference non existent aprs library functions.

I'm not sure the write function works no matter what you feed it. I even tried to just give it the raw kiss frames that is read.

modernham commented 8 months ago

Figured out a solution fellas, and anyone that comes here in the future. Created an "Encode Packet" class to encode and parse the packet into a an actual KISS / APRS frame before sending it to the write function.


import aprs

def encode_packet(in_frame) -> bytes:
    """
    Encodes an APRS Frame as AX.25.
    """
    frame = aprs.parse_frame(in_frame)
    encoded_frame = []
    encoded_frame.append(frame.destination.encode_ax25())
    encoded_frame.append(frame.source.encode_ax25())
    for path_call in frame.path:
        encoded_frame.append(path_call.encode_ax25())
    encoded_frame[-1] = b'\xae\x92\x88\x8ad@c'
    encoded_frame.append(aprs.ADDR_INFO_DELIM)
    encoded_frame.append(bytes(frame.info))

    fcs = aprs.FCS()
    for bit in encoded_frame:
        fcs.update_bit(bit)

    return b''.join(encoded_frame)

def EncodeKISS(source, destination, message):
    aprs_frame = aprs.parse_frame(source + ">APIN20,WIDE1-1,WIDE2-1::" + destination + " :" + message)
    return(encode_packet(aprs_frame))

The return of "EncodeKISS" which takes a source, destination callsign and a message can be fed directly in your write buffer.

Smittyman50 commented 8 months ago

@modernham Any chance you can show your code in context with a write to a TCP kiss connection? I'm trying to use the socket example and I don't understand how to use your code with that example. I have an instance of direwolf running on my linux machine and I'm wanting to use a python script to connect via kiss, send a frame, then disconnect.

modernham commented 8 months ago

@modernham Any chance you can show your code in context with a write to a TCP kiss connection? I'm trying to use the socket example and I don't understand how to use your code with that example. I have an instance of direwolf running on my linux machine and I'm wanting to use a python script to connect via kiss, send a frame, then disconnect.

Doing this from my phone, so hopefully turned out okay:


import aprs
Import kiss

"""
Does some voodoo magic to turn an APRS frame into a set of bytes than can be sent over the network to direwolf in 
a way that it's understood. Don't ask me how it works, because even I don't know.
"""
def encode_packet(in_frame) -> bytes:
    """
    Encodes an APRS Frame as AX.25.
    """
    frame = aprs.parse_frame(in_frame)
    encoded_frame = []
    encoded_frame.append(frame.destination.encode_ax25())
    encoded_frame.append(frame.source.encode_ax25())
    for path_call in frame.path:
        encoded_frame.append(path_call.encode_ax25())
    encoded_frame[-1] = b'\xae\x92\x88\x8ad@c'
    encoded_frame.append(aprs.ADDR_INFO_DELIM)
    encoded_frame.append(bytes(frame.info))

    fcs = aprs.FCS()
    for bit in encoded_frame:
        fcs.update_bit(bit)

    return b''.join(encoded_frame)

def EncodeKISSMessage(source, destination, message):
    aprs_frame = aprs.parse_frame(source + ">APIN20,WIDE1-1,WIDE2-1::" + destination + " :" + message)
    kiss_frame = encode_packet(aprs_frame)
    frame_escaped = kiss.escape_special_codes(kiss_frame)

    frame_kiss = b''.join([
        kiss.FEND,
        kiss.DATA_FRAME,
        frame_escaped,
        kiss.FEND
    ])
    return(frame_kiss)

"""
Creates a KISS packet for the TNC
"""
def WriteAPRSMessageFrame(tnc_stream, packet):
    data = packet.split(",")
    sender = data[0]
    receiver = data[1]
    message = data[2]
    if sender and receiver and message:
        frame = EncodeKISSMessage(sender.upper(), receiver.upper(), message)
        tnc_stream._write_handler(frame)
        print("[!] Sent Message to TNC: " + packet)
    return

  tnc_stream = kiss.TCPKISS(DIREWOLF_IP, DIREWOLF_PORT, False)
  tnc_stream.start()

  WriteAPRSMessageFrame(tnc_stream, "mycall,theircall,the message")

You also have to patch the library itself with this method:

This was very much a hack of the authors code to force it to work. There was old code in the library and it looks like some kiss code was in the aprs library itself. Also included was an "encode ax25" function in the aprs module Frame class that was modified to make the above. It took some screwing around with but it is indeed working. I sent a good frame from another application like pinpoint aprs and sniffed the traffic with Wireshark. I then proceeded to try different ways to imitate it over and over again with the kiss python library until I came up with the above.

modernham commented 8 months ago

Had to edit code to change things a bit. Didn't realize I had made a manual patch.