#!/usr/bin/python # -*- coding: utf-8 -*- from __future__ import print_function RECV_BUFFER_LENGTH = 1024 from threading import Thread import socket from KissHelper import SerialParser from array import array CRCTAB = array("H",[ 61560,57841,54122,49891,46684,42965,38222,33991,31792,28089,24354,20139,14868,11165,6406,2191, 57593,61808,50155,53858,42717,46932,34255,37958,27825,32056,20387,24106,10901,15132,2439,6158, 53626,49395,62056,58337,38750,34519,46156,42437,23858,19643,32288,28585,6934,2719,14340,10637, 49659,53362,58089,62304,34783,38486,42189,46404,19891,23610,28321,32552,2967,6686,10373,14604, 45692,41973,37230,32999,62552,58833,55114,50883,15924,12221,7462,3247,30736,27033,23298,19083, 41725,45940,33263,36966,58585,62800,51147,54850,11957,16188,3495,7214,26769,31000,19331,23050, 37758,33527,45164,41445,54618,50387,63048,59329,7990,3775,15396,11693,22802,18587,31232,27529, 33791,37494,41197,45412,50651,54354,59081,63296,4023,7742,11429,15660,18835,22554,27265,31496, 29808,26105,22370,18155,12884,9181,4422,207,63544,59825,56106,51875,48668,44949,40206,35975, 25841,30072,18403,22122,8917,13148,455,4174,59577,63792,52139,55842,44701,48916,36239,39942, 21874,17659,30304,26601,4950,735,12356,8653,55610,51379,64040,60321,40734,36503,48140,44421, 17907,21626,26337,30568,983,4702,8389,12620,51643,55346,60073,64288,36767,40470,44173,48388, 13940,10237,5478,1263,28752,25049,21314,17099,47676,43957,39214,34983,64536,60817,57098,52867, 9973,14204,1511,5230,24785,29016,17347,21066,43709,47924,35247,38950,60569,64784,53131,56834, 6006,1791,13412,9709,20818,16603,29248,25545,39742,35511,47148,43429,56602,52371,65032,61313, 2039,5758,9445,13676,16851,20570,25281,29512,35775,39478,43181,47396,52635,56338,61065,65280 ]) def logf(message): import sys print(message, file=sys.stderr) class AXUDPServer(Thread): '''AXUDP Server to communicate with the digipeater''' txQueue = None # host and port as configured in aprx/aprx.conf.lora-aprs < interface > section def __init__(self, txQueue, localHost="127.0.0.1", localPort=10001, remoteHost="127.0.0.1", remotePort="20000"): Thread.__init__(self) self.socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) self.socket.bind((localHost, localPort)) self.remoteHost=remoteHost self.remotePort=remotePort self.data = str() self.txQueue = txQueue def run(self): while True: frame = self.socket.recv(RECV_BUFFER_LENGTH) print("TX:",self.axtostr(frame)) self.txQueue.put(self.axtostr(frame).encode('utf-8'), block=False) def __del__(self): self.socket.shutdown() def send(self, data, metadata): self.sendax(data, (self.remoteHost, self.remotePort), metadata) #self.socket.sendall(data) def axcall(self, text, pos): l=len(text) a="" print(text) while (pos=ord("0")) and (text[pos]<=ord("9")) or (text[pos]>=ord("A")) and (text[pos]<=ord("Z"))): a+=chr(text[pos]<<1) pos+=1 while len(a)<6: a+=chr(ord(" ")<<1) #fill with spaces ssid=0 if (pos=ord("0")) and (text[pos]<=ord("9")): ssid+=text[pos]-ord("0") pos+=1 if (pos=ord("0")) and (text[pos]<=ord("9")): ssid=ssid*10 + text[pos]-ord("0") pos+=1 if ssid>15: ssid=15 ssid=(ssid+48)<<1 if (pos> 8) ^ CRCTAB[(ord(frame[p]) ^ c) & 0xff] return c def sendax(self, text, ip, values=False): a,p=self.axcall(text, 0) #src call if (p>=len(text)) or (text[p]!=ord(">")): print("fehler 1") return ax,p=self.axcall(text, p+1) #dest call ax+=a hbit=0 while True: #via calls if p>=len(text): print("found no end of address") return #found no end of address if text[p]==ord(":"): break #end of address field if text[p]!=ord(","): print("via path error") return #via path error if len(ax)>=70: print("too many via calls") return #too many via calls a,p=self.axcall(text, p+1) ax+=a hp=len(ax)-1 if (ord(ax[hp]) & 0x80)!=0: hbit=hp #store last h-bit p+=1 a="" if values: a="\x01\x30" #axudp v2 start if 'level' in values.keys(): v=values["level"] a+="V"+str(round(v))+" " #axudp v2 append level if 'quality' in values.keys(): v=values["quality"] a+="Q"+str(round(v))+" " #axudp v2 append quality if 'txdel' in values.keys(): v=values["txdel"] a+="T"+str(round(v))+" " #axudp v2 append quality if 'snr' in values.keys(): v=values["snr"] a+="S"+str(round(v))+" " #axudp v2 append snr a+="\x00" #axudp2 end i=0 for i in range(len(ax)): ch=ord(ax[i]) if (i%7==6) and (i>=20) and (i>8) sa=array("B",[0]*len(a)) for i in range(0,len(a)): sa[i]=ord(a[i]) print(sa) print(ip) res=sock.sendto(sa, ip) ## RX: def callstr(self, b, p): s="" for i in range(6): ch=ord(b[p+i])>>1 if ch<32: s+="^" #show forbidden ctrl in call elif ch>32:s+=chr(ch) #call is filled with blanks ssid=(ord(b[p+6])>>1) & 0x0f if ssid: s+="-"+str(ssid) return s def axtostr(self, axbuf): b="" for x in axbuf: b+=chr(x) le=len(b) if le<2: return "" le-=2 c=self.udpcrc(b, le) if (b[le]!=chr(c & 0xff)) or (b[le+1]!=chr(c>>8)): return "" #crc error i=0 if axbuf[0]==1: #axudp v2 while (i=18: #2 calls + ctrl + pid + crc le-=2 s=self.callstr(b, 7) #src call s+=">"+self.callstr(b, 0) #destination call p=14 hbit=False while (((not (ord(b[p-1]) & 1)))) and (p+6=128: hbit=True elif hbit: #call before had hbit s+="*" hbit=False s+=","+callstr(b, p) p+=7 if hbit: s+="*" #last call had hbit p+=2 #pid, ctrl s+=":" while p