def __init__(self, old, new, overhead, maxbuff, emptychar=None): """ Does the binary difference Args: * old (bytes): the reference (old) binary data * new (bytes): the new binary data to be compared * overhead (int): the size of headers associated to the bin-data * maxbuff (int): the maximum size of bin-data * emptychar (byte): the padding character if the new bin-data is shorter than the reference, or `None` for no padding """ self.overhead = int(overhead) self.maxbuff = int(maxbuff) self.new = Byt(new) self.old = Byt(old) cutit = len(self.new) self.newlen = len(self.new) self.oldlen = len(self.old) # if new bin-data is shorter than old if cutit < len(self.old): # if padding is requested, pad the new bin with eptychar if emptychar is None: self.new += Byt(emptychar)[0] * (self.oldlen - self.newlen) self.newlen = self.oldlen # else, shorten the old bin-data else: self.old = self.old[:cutit] self.oldlen = self.newlen self.minlen = min(self.oldlen, self.newlen) self.maxlen = max(self.oldlen, self.newlen)
def __init__(self, number, name, pid, desc, lparam, subsystem, param, subSystemKey, adcsCommandId): """ Creates a self-checking command Args: * number (int): the unique id * name (str): the name (code friendly) * pid (str): the unique pid identity string * desc (str): the description * lparam (int): the total length of the parameters, in octets, or "*" for any * subsystem (str): the subsystem key * param (iterable of list): an iterable of parameter lists of form and order: (name, desc, rng, typ, size, unit) * subSystemKey (Byt[1]): the hex code of the sub-system * adcsCommandId (Byt[1]): the hex code command id from the ADCS """ self.subSystemKey = Byt().fromHex(subSystemKey[:2]) self.adcsCommandId = Byt().fromHex(adcsCommandId[:2]) super(CmADCS, self).__init__(number=number, name=name, pid=pid, desc=desc, lparam=lparam, subsystem=subsystem, param=param)
def process(self, key, data): """ Saves the packet in the database and does """ # ignores the reporting if key == 'rpt': return report('receivedTM') if param_all.AX25ENCAPS: source, destination, blobish = Framer.decode_radio(data['data']) if source == Byt(): report('receivedCallsignTM', source=source, ll=len(blobish), destination=destination) else: report('junkFromRF') else: blobish = data['data'] report('receivedRawTM', ll=len(blobish)) blobparser = CCSDSBlob(blobish) start = 0 pk = blobparser.grab_first_packet(start=start) while pk is not None: data['data'] = Byt(pk) process_incoming(**data) start += len(pk) pk = blobparser.grab_first_packet(start=start) return
def decode_radio(self, frame): """ Parses and extracts the components of an AX25+KISS-Encoded Frame """ # init source, destination, text = Byt(), Byt(), Byt() if self.ISKISS: # parse KISS away frame = kissutils.strip_df_start(frame) # recover special codes frame = kissutils.recover_special_codes(frame) frameints = frame.ints() frame_len = len(frameints) if frame_len > 16: for idx, char in enumerate(frameints): # Is address field length correct? # Find the first ODD Byte followed by the next boundary: if (char & 0x01 and ((idx + 1) % 7) == 0): i = (idx + 1) // 7 # Less than 2 callsigns? For frames <= 70 bytes if 1 < i < 11 and frame_len >= idx + 2: if (frameints[idx + 1] & 0x03 == 0x03 and frameints[idx + 2] in [0xf0, 0xcf]): text = frame[idx + 3:] destination = Callsign(frame[:7]) source = Callsign(frame[7:]) #self._extract_kiss_path(frame, i) return source, destination, text return source, destination, text
def add_siggy(self, fullPacket, **kwargs): """ Does surgery of the input full packet with null signature (fullPacket) and adds the signature into it Returns the packet with included signature, and the signature If the signature should not be used because of parametrization, the signature is not added to the packet and a None signature is returned """ if not kwargs.pop('signit', param_all.USESIGGY)\ or self.mode == 'telemetry': return fullPacket, None # calculates the signature from full packet siggy = hmac(fullPacket) # apply mask siggy = Byt(s for idx, s in enumerate(siggy.ints())\ if param_all.KEYMASK[idx] == '1') # f**k endians if bincore.TWINKLETWINKLELITTLEINDIA: siggy = siggy[::-1] # grab the bounds of the siggy location, to chunk it into the packet startSiggy = param_ccsds.HEADER_P_KEYS.size +\ param_ccsds.SIGNATURE.start//8 # return concatenated turd return fcts.setstr( fullPacket, slice(startSiggy, startSiggy + param_ccsds.SIGNATURE.len // 8), siggy), siggy
def _grabCSV(self, itera): ll = [] cm = None for line in itera: # is empty line ? if line[param_commands.CSVNAME] == "" and\ line[param_commands.CSVPARAMNAME] == "": continue # that's a new command, not an additional parameter if line[param_commands.CSVSUBSYSTEM] != "": if cm is not None: ll.append(cm) cm = { 'number': int(line[param_commands.CSVNUMBER]), 'name':\ core.rchop(core.ustr(line[param_commands.CSVNAME])\ .replace(' ', '_'), '_TM'), 'pid': core.ustr(line[param_commands.CSVPID]).lower(), 'desc': core.ustr(line[param_commands.CSVDESC]), 'lparam':\ int(0\ if line[param_commands.CSVLPARAM].strip() == ""\ else line[param_commands.CSVLPARAM])\ if line[param_commands.CSVLPARAM].strip() != "*"\ else "*", 'subsystem':\ core.ustr(line[param_commands.CSVSUBSYSTEM])\ .lower().replace(' ', '_'), 'param': [], 'n_nparam':\ int(line[param_commands.CSVNPARAM]\ if line[param_commands.CSVNPARAM].strip() != ""\ else 0)} # adcs specific stuff if self._cmds == 'adcs': cm.update({ 'subSystemKey': Byt(int(line[\ param_commands.CSVSUBSYSTEMKEYADCS]\ .strip(), 16))[0].hex(), 'adcsCommandId': Byt(int(line[\ param_commands.CSVCOMMANDIDADCS]\ .strip(), 16))[0].hex()}) # no parameter command if cm['n_nparam'] == 0: cm['lparam'] = 0 else: # adding parameters to the last command added cm['param'].append([ core.ustr(line[param_commands.CSVPARAMNAME])\ .replace(' ', '_'), core.ustr(line[param_commands.CSVPARAMDESC]), core.ustr(line[param_commands.CSVPARAMRNG]), core.ustr(line[param_commands.CSVPARAMTYP])\ .replace('_t', ''), core.ustr(line[param_commands.CSVPARAMSIZE]), core.ustr(line[param_commands.CSVPARAMUNIT])\ .replace(' ', '_'), core.ustr(line[param_commands.CSVPARAMEX])]) ll.append(cm) return ll
def str2hex(txt, pad=0, **kwargs): """ A hexa-text '2F 3C A8' to bytes pad in hex chars type = hexadecimal verbose = hexa string -> hexadecimal """ return padit(txt=Byt.fromHex(txt), l=pad, ch=Byt(0))
def test_pformat_uint(): p = PFormat('uint', 8) assert p.minmax == (0, 2**8 - 1) assert p.is_valid(256) == False assert p.is_valid(255) == True assert p.is_valid(0) == True assert p.is_valid(-1) == False assert p._tohex(0) == Byt('\x00') assert p._tohex(241) == Byt('\xf1')
def txt2hex(txt, pad=0, **kwargs): """ A display-text [32--126] to bytes pad in hex chars type = hexadecimal verbose = message -> hexadecimal """ txt = Byt(i for i in Byt(txt).ints() if i >= 32 and i <= 126) return padit(txt=txt, l=pad, ch=Byt(0))
def __init__(self, callsign): """ Defines parts of a Callsign decoded from either ASCII or KISS Args: * callsign (str): the callsign """ self.callsign = Byt() self.ssid = Byt('0') self.digi = False self.parse(callsign)
def _extract_callsign_from_AX25_frame(self, frame): """ Extracts a Callsign and SSID from a KISS-Encoded APRS Frame. Args: * frame (bytes): KISS-Encoded APRS Frame as str of octs """ frame = Byt(frame[:7]).ints() callsign = Byt(x >> 1 for x in frame[:6]) self.callsign = callsign.strip() self.ssid = Byt(str((frame[6] >> 1) & 0x0f))
def double2hex(v, pad=0, **kwargs): """ Give a float64, returns chars pad in hex chars type = hexadecimal verbose = float32 -> hexadecimal """ litFucInd = kwargs.get('litFucInd', TWINKLETWINKLELITTLEINDIA) end = '<' if litFucInd else '>' v = Byt(struct.pack(end + 'd', v)) return padit(txt=v, l=pad, ch=Byt(0))
def test_pformat_str(): p = PFormat('str') assert p.minmax == (0, 255) assert p.bits == 0 assert p.typ == 'str' assert p.is_valid(['a']) == False assert p.is_valid('ab') == False assert p.is_valid(32) == False assert p.is_valid('a') == True assert p.is_valid('\x01') == True assert p._tohex('a') == Byt('a') assert p._tohex('\x41') == Byt('A')
def hmac(txt): """ Calculates the HMAC-SHA256 of the ``txt`` given as input """ txt = Byt(txt) digest = create_string_buffer(param_all.KEYLENGTH) if param_all.VITELACLE is not None: _hmacfct(param_all.VITELACLE, txt, len(txt), digest) else: print('No key found, using NULL') _hmacfct(Byt("\x00" * param_all.KEYLENGTH), txt, len(txt), digest) return Byt(digest.raw)
def test_param_str_tuple(): p = Parameter('hop', 'blah', ftup(0, 10), 'str') assert p.is_valid('rt') == False assert p.is_valid(3) == False assert p.is_valid([10]) == False assert p.is_valid(['\x00']) == True assert p.is_valid('\x09') == True assert p.is_valid(['\x10']) == False assert p.is_valid('\x00\x00') == False assert p.is_valid(['\x10'], withvalue=True)[0] == False assert p.is_valid(['\x04'], withvalue=True) == (True, ['\x04']) assert p.tohex('\x04') == Byt('\x04') assert p.tohex(['\x02']) == Byt('\x02')
def test_param_uint_tuple(): p = Parameter('hop', 'blah', ftup(0, 10), 'uint8', 1, None) assert p.is_valid('rt') == False assert p.is_valid(3) == True assert p.is_valid([10]) == True assert p.is_valid([5, 0]) == False assert p.is_valid([-1]) == False assert p.is_valid(11) == False assert p.is_valid([1.0]) == False assert p.is_valid([12.0], withvalue=True)[0] == False assert p.is_valid([0], withvalue=True) == (True, [0]) assert p.tohex(3) == Byt('\x03') assert p.tohex(10) == Byt('\x0A')
def test_param_str_list(): p = Parameter('hop', 'blah', [0, 1, 2, 4], 'str', 2) assert p._isdict == False assert p.is_valid(['\x01', '\x04']) == True assert p.is_valid(['\x01', '\x03']) == False assert p.is_valid(['\x02']) == False assert p.is_valid(['\x00', '\x01'], withvalue=True) == (True, ['\x00', '\x01']) assert p.is_valid(['\x05', '\x02']) == False assert p.is_valid(['\x01', '\x02', '\x00']) == False assert p.is_valid(['\x03', '\x01'], withvalue=True)[0] == False assert p.is_valid('\x04\x00', withvalue=True) == (True, ['\x04', '\x00']) assert p.tohex(['\x04', '\x01']) == Byt('\x04\x01') assert p.tohex('\x01\x02') == Byt('\x01\x02')
def encode_callsign(self): """ Encodes Callsign (or Callsign-SSID) as KISS """ encoded_ssid = ((int(self.ssid) << 1) | 0x60) callsign = Byt(self.callsign) if self.digi: encoded_ssid |= 0x80 # Pad the callsign to 6 characters callsign = fcts.fillit(callsign, l=6, ch=Byt(' ')).ints() encoded_callsign = Byt(p << 1 for p in callsign) return encoded_callsign + Byt(encoded_ssid)
def int2hex(i, pad=0, **kwargs): """ Give an int, returns chars pad in hex chars type = hexadecimal verbose = unsigned integer -> hexadecimal """ litFucInd = kwargs.get('litFucInd', TWINKLETWINKLELITTLEINDIA) hx = hex(i)[2:].replace('L', '') # replace if long int hx = Byt(binascii.unhexlify(('0' * (len(hx) % 2)) + hx)) if litFucInd: hx = hx[::-1] if pad > 1: hx = padit(txt=hx, l=pad, ch=Byt(0)) return hx
def tohex(self, value): """ Equivalent of calling the object """ valid, value = self.is_valid(value, withvalue=True) if valid is False: raise cmdexception.InvalidParameterValue(self.name, value) ret = Byt() if self._isdict: for item in value: ret += Byt(self.rng[item]) else: for item in value: ret += self.typ._tohex(item) return ret
def test_cm_base2(): ee = copy.deepcopy(echo) ee['lparam'] = '*' ee['param'] = (('hop', 'blah', ftup(0, 255), 'str', ftup(1, 4)), ) c = Cm(**ee) assert c.number == 1 assert c.name == 'echo' assert c.desc == 'blah' assert c.level == 0 assert c.subsystem == "obc" assert c.pid == "L0ComManager" assert c.lparam is None assert c.nparam == 1 assert c.p_0_hop.typ.typ == 'str' assert c.generate_data(hop='abab')[0] == Byt('\x61\x62\x61\x62') assert c.generate_data(hop='a')[0] == Byt('\x61')
def merge_flow(datalist, trailingSplit=True): """ Merges the packets if the flow mode is activated Args: * datalist (list of Byt): the packets to merge together * trailingSplit (bool): whether to add a trailing split character """ if not param_all.FRAMESFLOW: raise exc.NotInFramesFlow() # merge CCSDS using the special split chars if not param_all.AX25ENCAPS: res = (param_all.CCSDSSPLITCHAR*2).join([ Byt(item).replace(param_all.CCSDSSPLITCHAR, param_all.CCSDSESCAPEDSPLIT)\ for item in datalist\ if len(item) > 0]) if trailingSplit: res += param_all.CCSDSSPLITCHAR * 2 return res # merge KISS using the special split chars elif param_all.KISSENCAPS: raise exc.NotImplemented("Frames-FLow with KISS") else: raise exc.NotImplemented("Unknown mode")
def unpack_data(self, packet, hds, hdx): """ Unpacks the data of the packet, returns a dictionary Args: * packet (byts): the string that contains the full packet * hds (dict): the packet headers * hdx (dict): the packet auxiliary headers """ data = {'all': Byt(), 'unpacked': {}} start = param_ccsds.HEADER_P_KEYS.size if self.mode == 'telemetry': start += param_ccsds.HEADER_S_KEYS_TELEMETRY.size else: start += param_ccsds.HEADER_S_KEYS_TELECOMMAND.size catnum = int(hds[param_ccsds.PACKETCATEGORY.name]) pld = int(hds[param_ccsds.PAYLOADFLAG.name]) # aux header size cat = param_category.CATEGORIES[pld][catnum] start += cat.aux_size data['all'] = packet[start:] if cat.data_trousseau is not None: # pass tc id in case of tcanswer category data['unpacked'] = cat.data_trousseau.unpack(data=data['all'], hds=hds, hdx=hdx) return data
def process(self, key, data): """ Checks for feedback from listen """ # ignores anything but dic and rpt if key is sentTC if key != 'dic' and key != 'rpt': return if 'data' not in data.keys(): return blobish = data['data'] # strip AX25 if need be if param_all.AX25ENCAPS: source, destination, blobish = Framer.decode_radio(blobish) # case of the RFCheckoutBox returning garbage if len(blobish) == 0: return # if report type message, gotta check if report_key is sentTC if key == 'rpt': # case of getting the TC back, update time_sent in DB if str(data[param_sys.REPORTKEY]) == 'sentTC'\ and param_all.SAVETC: res = TCUnPacker.unpack_primHeader(blobish) pkid = res[param_ccsds.PACKETID.name] db.update_sent_TC_time(pkid, data['t']) elif key == 'dic': blobparser = CCSDSBlob(blobish) start = 0 pk = blobparser.grab_first_packet(start=start) while pk is not None: data['data'] = Byt(pk) process_incoming(**data) start += len(pk) pk = blobparser.grab_first_packet(start=start)
def test_param_uint_list(): p = Parameter('hop', 'blah', [0, 1, 2, 4], 'uint8', ftup(2, 3), None) assert p._isdict == False assert p.is_valid('rt') == False assert p.is_valid(1) == False assert p.is_valid([2]) == False assert p.is_valid([0, 1], withvalue=True) == (True, [0, 1]) assert p.is_valid([4, 2]) == True assert p.is_valid([1.0, 2]) == False assert p.is_valid([1, 2, 0]) == True assert p.is_valid([1, 2, 0, 0]) == False assert p.is_valid([0, 3]) == False assert p.is_valid([2.0, 1], withvalue=True)[0] == False assert p.is_valid([4, 0], withvalue=True) == (True, [4, 0]) assert p.tohex([4, 1]) == Byt('\x04\x01') assert p.tohex([1, 0]) == Byt('\x01\x00')
def generate_slices(self, slices): """ Returns the list of data blocks """ blocks = {} for sl in slices: blocks[sl.s] = Byt(self.new[sl.slice()]) return blocks
def hex2hex(v, pad=0, **kwargs): """ Give some hex, get some hex pad in hex chars type = hexadecimal verbose = Kept as hexadecimal """ return padit(txt=v, l=pad, ch=Byt(0))
def pack_data(self, values, header, retvalues=False): """ Encodes the values into a CCSDS auxiliary header, returns hex string and encoded values Args: * values (dict): the values to pack * header (dict): * retvalues (bool): if ``True``, returns the encoded values """ if self.mode != 'telemetry': return (Byt(), {}) if retvalues else Byt() # encode the data here TBD if retvalues: return Byt(), {} else: return Byt()
def crc32(message, crc=None): """ Give a message, returns a CRC on 4 octet using basecrc as crc start-value (if given) """ crc = 0xffffffff if crc is None else int(crc) for byte in Byt(message).iterInts(): crc = (crc >> 8) ^ CRC32TABLE[(crc ^ byte) & 0xFF] return two_comp_uint(crc, 32)
def process_report(inputs): global PIDS key = str(inputs.get(param_sys.REPORTKEY)) broadcast(key, **inputs) if key == 'myPID': who = inputs['who'] if who in PIDS.keys(): killit = PIDS.pop(who) killit.stop() # restart watchdog PID with new PID number PIDS[who] = PIDWatchDog(name=who, pid=inputs['pid'], timeout=param_all.PROCESSTIMEOUT, whenDead=revive_process, whenAlive=say_hi, who=who) elif key == 'broadcastTC': pass elif key == 'GotBlob': blobish = Byt(inputs['blob']) try: # strip AX25 if need be if param_all.AX25ENCAPS: source, destination, blobish = Framer.decode_radio(blobish) # case of the RFCheckoutBox returning garbage if len(blobish) == 0: print('That was junk from RFCheckoutBox') return if len(blobish) == 0: print("Can't unpack empty packet") return hd, hdx, dd = TMUnPacker.unpack(blobish) except: print('Tried to unpack.. but an error occurred: {}'\ .format(sys.exc_info()[0])) return inpacket = dict(hd) inpacket.update(hdx) inpacket['_all_data'] = dd['all'] inpacket['_sz_blobish'] = len(blobish) WATCH_TRANS.tell_key('tmf', **inpacket) pldflag = int(hd[param_ccsds.PAYLOADFLAG.name]) catnum = int(hd[param_ccsds.PACKETCATEGORY.name]) # print Header Prim print(param_ccsds.HEADER_P_KEYS.disp(dict(hd))) # print Header Sec TM print(param_ccsds.HEADER_S_KEYS_TELEMETRY.disp(dict(hd))) # print Header Aux if any cat = param_category.CATEGORIES[pldflag][catnum] if cat.aux_size > 0: print(cat.aux_trousseau.disp(dict(hdx))) # print data if any if cat.data_trousseau is not None: print(cat.data_trousseau.disp(dd['unpacked'], hds=hd, hdx=hdx)) else: pass