mirror of https://github.com/hak5/ToorChat.git
2381 lines
87 KiB
Python
2381 lines
87 KiB
Python
#!/usr/bin/env ipython
|
|
import sys, threading, time, struct, select
|
|
import usb
|
|
|
|
import bits
|
|
from chipcondefs import *
|
|
# from rflib_version import *
|
|
|
|
# band limits in Hz
|
|
FREQ_MIN_300 = 281000000
|
|
FREQ_MAX_300 = 361000000
|
|
FREQ_MIN_400 = 378000000
|
|
FREQ_MAX_400 = 481000000
|
|
FREQ_MIN_900 = 749000000
|
|
FREQ_MAX_900 = 962000000
|
|
|
|
# band transition points in Hz
|
|
FREQ_EDGE_400 = 369000000
|
|
FREQ_EDGE_900 = 615000000
|
|
|
|
# VCO transition points in Hz
|
|
FREQ_MID_300 = 318000000
|
|
FREQ_MID_400 = 424000000
|
|
FREQ_MID_900 = 848000000
|
|
|
|
EP_TIMEOUT_IDLE = 400
|
|
EP_TIMEOUT_ACTIVE = 10
|
|
|
|
USB_RX_WAIT = 100
|
|
USB_TX_WAIT = 10000
|
|
|
|
USB_BM_REQTYPE_TGTMASK =0x1f
|
|
USB_BM_REQTYPE_TGT_DEV =0x00
|
|
USB_BM_REQTYPE_TGT_INTF =0x01
|
|
USB_BM_REQTYPE_TGT_EP =0x02
|
|
|
|
USB_BM_REQTYPE_TYPEMASK =0x60
|
|
USB_BM_REQTYPE_TYPE_STD =0x00
|
|
USB_BM_REQTYPE_TYPE_CLASS =0x20
|
|
USB_BM_REQTYPE_TYPE_VENDOR =0x40
|
|
USB_BM_REQTYPE_TYPE_RESERVED =0x60
|
|
|
|
USB_BM_REQTYPE_DIRMASK =0x80
|
|
USB_BM_REQTYPE_DIR_OUT =0x00
|
|
USB_BM_REQTYPE_DIR_IN =0x80
|
|
|
|
USB_GET_STATUS =0x00
|
|
USB_CLEAR_FEATURE =0x01
|
|
USB_SET_FEATURE =0x03
|
|
USB_SET_ADDRESS =0x05
|
|
USB_GET_DESCRIPTOR =0x06
|
|
USB_SET_DESCRIPTOR =0x07
|
|
USB_GET_CONFIGURATION =0x08
|
|
USB_SET_CONFIGURATION =0x09
|
|
USB_GET_INTERFACE =0x0a
|
|
USB_SET_INTERFACE =0x11
|
|
USB_SYNCH_FRAME =0x12
|
|
|
|
APP_GENERIC = 0x01
|
|
APP_DEBUG = 0xfe
|
|
APP_SYSTEM = 0xff
|
|
|
|
|
|
SYS_CMD_PEEK = 0x80
|
|
SYS_CMD_POKE = 0x81
|
|
SYS_CMD_PING = 0x82
|
|
SYS_CMD_STATUS = 0x83
|
|
SYS_CMD_POKE_REG = 0x84
|
|
SYS_CMD_GET_CLOCK = 0x85
|
|
SYS_CMD_BUILDTYPE = 0x86
|
|
SYS_CMD_BOOTLOADER = 0x87
|
|
SYS_CMD_RESET = 0x8f
|
|
|
|
EP0_CMD_GET_DEBUG_CODES = 0x00
|
|
EP0_CMD_GET_ADDRESS = 0x01
|
|
EP0_CMD_POKEX = 0x01
|
|
EP0_CMD_PEEKX = 0x02
|
|
EP0_CMD_PING0 = 0x03
|
|
EP0_CMD_PING1 = 0x04
|
|
EP0_CMD_RESET = 0xfe
|
|
|
|
|
|
DEBUG_CMD_STRING = 0xf0
|
|
DEBUG_CMD_HEX = 0xf1
|
|
DEBUG_CMD_HEX16 = 0xf2
|
|
DEBUG_CMD_HEX32 = 0xf3
|
|
DEBUG_CMD_INT = 0xf4
|
|
|
|
EP5OUT_MAX_PACKET_SIZE = 64
|
|
EP5IN_MAX_PACKET_SIZE = 64
|
|
# EP5OUT_BUFFER_SIZE must match firmware/include/chipcon_usb.h definition
|
|
EP5OUT_BUFFER_SIZE = 516
|
|
|
|
|
|
SYNCM_NONE = 0
|
|
SYNCM_15_of_16 = 1
|
|
SYNCM_16_of_16 = 2
|
|
SYNCM_30_of_32 = 3
|
|
SYNCM_CARRIER = 4
|
|
SYNCM_CARRIER_15_of_16 = 5
|
|
SYNCM_CARRIER_16_of_16 = 6
|
|
SYNCM_CARRIER_30_of_32 = 7
|
|
|
|
RF_STATE_RX = 1
|
|
RF_STATE_TX = 2
|
|
RF_STATE_IDLE = 3
|
|
|
|
RFLIB_VERSION = ""
|
|
|
|
RF_SUCCESS = 0
|
|
|
|
RF_MAX_TX_BLOCK = 255
|
|
# RF_MAX_BLOCK must match BUFFER_SIZE definition in firmware/include/cc1111rf.h
|
|
RF_MAX_RX_BLOCK = 512
|
|
|
|
|
|
MODES = {}
|
|
lcls = locals()
|
|
for lcl in lcls.keys():
|
|
if lcl.startswith("MARC_STATE_"):
|
|
MODES[lcl] = lcls[lcl]
|
|
MODES[lcls[lcl]] = lcl
|
|
|
|
|
|
""" MODULATIONS
|
|
Note that MSK is only supported for data rates above 26 kBaud and GFSK,
|
|
ASK , and OOK is only supported for data rate up until 250 kBaud. MSK
|
|
cannot be used if Manchester encoding/decoding is enabled.
|
|
"""
|
|
MOD_2FSK = 0x00
|
|
MOD_GFSK = 0x10
|
|
MOD_ASK_OOK = 0x30
|
|
MOD_MSK = 0x70
|
|
MANCHESTER = 0x08
|
|
|
|
MODULATIONS = {
|
|
MOD_2FSK : "2FSK",
|
|
MOD_GFSK : "GFSK",
|
|
MOD_ASK_OOK : "ASK/OOK",
|
|
MOD_MSK : "MSK",
|
|
MOD_2FSK | MANCHESTER : "2FSK/Manchester encoding",
|
|
MOD_GFSK | MANCHESTER : "GFSK/Manchester encoding",
|
|
MOD_ASK_OOK | MANCHESTER : "ASK/OOK/Manchester encoding",
|
|
MOD_MSK | MANCHESTER : "MSK/Manchester encoding",
|
|
}
|
|
|
|
SYNCMODES = {
|
|
SYNCM_NONE: "None",
|
|
SYNCM_15_of_16: "15 of 16 bits must match",
|
|
SYNCM_16_of_16: "16 of 16 bits must match",
|
|
SYNCM_30_of_32: "30 of 32 sync bits must match",
|
|
SYNCM_CARRIER: "Carrier Detect",
|
|
SYNCM_CARRIER_15_of_16: "Carrier Detect and 15 of 16 sync bits must match",
|
|
SYNCM_CARRIER_16_of_16: "Carrier Detect and 16 of 16 sync bits must match",
|
|
SYNCM_CARRIER_30_of_32: "Carrier Detect and 30 of 32 sync bits must match",
|
|
}
|
|
|
|
BSLIMITS = {
|
|
BSCFG_BS_LIMIT_0: "No data rate offset compensation performed",
|
|
BSCFG_BS_LIMIT_3: "+/- 3.125% data rate offset",
|
|
BSCFG_BS_LIMIT_6: "+/- 6.25% data rate offset",
|
|
BSCFG_BS_LIMIT_12: "+/- 12.5% data rate offset",
|
|
}
|
|
|
|
AESMODES = {
|
|
ENCCS_MODE_CBC: "CBC - Cipher Block Chaining",
|
|
ENCCS_MODE_CBCMAC: "CBC-MAC - Cipher Block Chaining Message Authentication Code",
|
|
ENCCS_MODE_CFB: "CFB - Cipher Feedback",
|
|
ENCCS_MODE_CTR: "CTR - Counter",
|
|
ENCCS_MODE_ECB: "ECB - Electronic Codebook",
|
|
ENCCS_MODE_OFB: "OFB - Output Feedback",
|
|
}
|
|
|
|
NUM_PREAMBLE = [2, 3, 4, 6, 8, 12, 16, 24 ]
|
|
|
|
ADR_CHK_TYPES = [
|
|
"No address check",
|
|
"Address Check, No Broadcast",
|
|
"Address Check, 0x00 is broadcast",
|
|
"Address Check, 0x00 and 0xff are broadcast",
|
|
]
|
|
|
|
|
|
|
|
PKT_FORMATS = [
|
|
"Normal mode",
|
|
"reserved...",
|
|
"Random TX mode",
|
|
"reserved",
|
|
]
|
|
|
|
LENGTH_CONFIGS = [
|
|
"Fixed Packet Mode",
|
|
"Variable Packet Mode (len=first byte after sync word)",
|
|
"reserved",
|
|
"reserved",
|
|
]
|
|
LC_USB_INITUSB = 0x2
|
|
LC_MAIN_RFIF = 0xd
|
|
LC_USB_DATA_RESET_RESUME = 0xa
|
|
LC_USB_RESET = 0xb
|
|
LC_USB_EP5OUT = 0xc
|
|
LC_RF_VECTOR = 0x10
|
|
LC_RFTXRX_VECTOR = 0x11
|
|
|
|
LCE_USB_EP5_TX_WHILE_INBUF_WRITTEN = 0x1
|
|
LCE_USB_EP0_SENT_STALL = 0x4
|
|
LCE_USB_EP5_OUT_WHILE_OUTBUF_WRITTEN = 0x5
|
|
LCE_USB_EP5_LEN_TOO_BIG = 0x6
|
|
LCE_USB_EP5_GOT_CRAP = 0x7
|
|
LCE_USB_EP5_STALL = 0x8
|
|
LCE_USB_DATA_LEFTOVER_FLAGS = 0x9
|
|
LCE_RF_RXOVF = 0x10
|
|
LCE_RF_TXUNF = 0x11
|
|
|
|
LCS = {}
|
|
LCES = {}
|
|
lcls = locals()
|
|
for lcl in lcls.keys():
|
|
if lcl.startswith("LCE_"):
|
|
LCES[lcl] = lcls[lcl]
|
|
LCES[lcls[lcl]] = lcl
|
|
if lcl.startswith("LC_"):
|
|
LCS[lcl] = lcls[lcl]
|
|
LCS[lcls[lcl]] = lcl
|
|
|
|
MARC_STATE_MAPPINGS = [
|
|
(0, 'MARC_STATE_SLEEP', RFST_SIDLE),
|
|
(1, 'MARC_STATE_IDLE', RFST_SIDLE),
|
|
(3, 'MARC_STATE_VCOON_MC', RFST_SIDLE),
|
|
(4, 'MARC_STATE_REGON_MC', RFST_SIDLE),
|
|
(5, 'MARC_STATE_MANCAL', RFST_SCAL),
|
|
(6, 'MARC_STATE_VCOON', RFST_SIDLE),
|
|
(7, 'MARC_STATE_REGON', RFST_SIDLE),
|
|
(8, 'MARC_STATE_STARTCAL', RFST_SCAL),
|
|
(9, 'MARC_STATE_BWBOOST', RFST_SIDLE),
|
|
(10, 'MARC_STATE_FS_LOCK', RFST_SIDLE),
|
|
(11, 'MARC_STATE_IFADCON', RFST_SIDLE),
|
|
(12, 'MARC_STATE_ENDCAL', RFST_SCAL),
|
|
(13, 'MARC_STATE_RX', RFST_SRX),
|
|
(14, 'MARC_STATE_RX_END', RFST_SIDLE ), # FIXME: this should actually be the config setting in register
|
|
(15, 'MARC_STATE_RX_RST', RFST_SIDLE),
|
|
(16, 'MARC_STATE_TXRX_SWITCH', RFST_SIDLE),
|
|
(17, 'MARC_STATE_RX_OVERFLOW', RFST_SIDLE),
|
|
(18, 'MARC_STATE_FSTXON', RFST_SFSTXON),
|
|
(19, 'MARC_STATE_TX', RFST_STX),
|
|
(20, 'MARC_STATE_TX_END', RFST_STX), # FIXME: this should actually be the config setting in register
|
|
(21, 'MARC_STATE_RXTX_SWITCH', RFST_SIDLE),
|
|
(22, 'MARC_STATE_TX_UNDERFLOW', RFST_SIDLE) # FIXME: this should actually be the config setting in register
|
|
]
|
|
|
|
def keystop():
|
|
return len(select.select([sys.stdin],[],[],0)[0])
|
|
|
|
|
|
class ChipconUsbTimeoutException(Exception):
|
|
def __str__(self):
|
|
return "Timeout waiting for USB response."
|
|
|
|
direct=False
|
|
|
|
class USBDongle:
|
|
######## INITIALIZATION ########
|
|
def __init__(self, idx=0, debug=False, copyDongle=None):
|
|
self.rsema = None
|
|
self.xsema = None
|
|
self._do = None
|
|
self.idx = idx
|
|
self.cleanup()
|
|
self._debug = debug
|
|
self._threadGo = False
|
|
self._recv_time = 0
|
|
self.radiocfg = RadioConfig()
|
|
self.recv_thread = threading.Thread(target=self.runEP5)
|
|
self.recv_thread.setDaemon(True)
|
|
self.recv_thread.start()
|
|
self.resetup(copyDongle=copyDongle)
|
|
self.max_packet_size = RF_MAX_RX_BLOCK
|
|
|
|
def cleanup(self):
|
|
self._usberrorcnt = 0;
|
|
self.recv_queue = ''
|
|
self.recv_mbox = {}
|
|
self.xmit_queue = []
|
|
self.trash = []
|
|
|
|
def setup(self, console=True, copyDongle=None):
|
|
global dongles
|
|
|
|
if copyDongle is not None:
|
|
self.devnum = copyDongle.devnum
|
|
self._d = copyDongle._d
|
|
self._do = copyDongle._do
|
|
self._usbmaxi = copyDongle._usbmaxi
|
|
self._usbmaxo = copyDongle._usbmaxo
|
|
self._usbcfg = copyDongle._usbcfg
|
|
self._usbintf = copyDongle._usbintf
|
|
self._usbeps = copyDongle._usbeps
|
|
self._threadGo = True
|
|
self.ep5timeout = EP_TIMEOUT_ACTIVE
|
|
copyDongle._threadGo = False # we're taking over from here.
|
|
self.rsema = copyDongle.rsema
|
|
self.xsema = copyDongle.xsema
|
|
return
|
|
|
|
dongles = []
|
|
self.ep5timeout = EP_TIMEOUT_ACTIVE
|
|
|
|
for bus in usb.busses():
|
|
for dev in bus.devices:
|
|
# OpenMoko assigned or Legacy TI
|
|
if (dev.idVendor == 0x0451 and dev.idProduct == 0x4715) or (dev.idVendor == 0x1d50 and (dev.idProduct == 0x6047 or dev.idProduct == 0x6048)):
|
|
if self._debug: print >>sys.stderr,(dev)
|
|
do = dev.open()
|
|
iSN = do.getDescriptor(1,0,50)[16]
|
|
devnum = dev.devnum
|
|
dongles.append((devnum, dev, do))
|
|
|
|
dongles.sort()
|
|
if len(dongles) == 0:
|
|
raise(Exception("No Dongle Found. Please insert a RFCAT dongle."))
|
|
|
|
self.rsema = threading.Lock()
|
|
self.xsema = threading.Lock()
|
|
|
|
# claim that interface!
|
|
do = dongles[self.idx][2]
|
|
|
|
try:
|
|
do.claimInterface(0)
|
|
except Exception,e:
|
|
if console or self._debug: print >>sys.stderr,("Error claiming usb interface:" + repr(e))
|
|
|
|
|
|
|
|
self.devnum, self._d, self._do = dongles[self.idx]
|
|
self._usbmaxi, self._usbmaxo = (EP5IN_MAX_PACKET_SIZE, EP5OUT_MAX_PACKET_SIZE)
|
|
self._usbcfg = self._d.configurations[0]
|
|
self._usbintf = self._usbcfg.interfaces[0][0]
|
|
self._usbeps = self._usbintf.endpoints
|
|
for ep in self._usbeps:
|
|
if ep.address & 0x80:
|
|
self._usbmaxi = ep.maxPacketSize
|
|
else:
|
|
self._usbmaxo = ep.maxPacketSize
|
|
|
|
self._threadGo = True
|
|
|
|
def resetup(self, console=True, copyDongle=None):
|
|
self._do=None
|
|
#self._threadGo = True
|
|
if self._debug: print >>sys.stderr,("waiting (resetup) %x" % self.idx)
|
|
while (self._do==None):
|
|
try:
|
|
self.setup(console, copyDongle)
|
|
if copyDongle is None:
|
|
self._clear_buffers(False)
|
|
|
|
except Exception, e:
|
|
#if console: sys.stderr.write('.')
|
|
print >>sys.stderr,("Error in resetup():" + repr(e))
|
|
if console or self._debug: print >>sys.stderr,("Error in resetup():" + repr(e))
|
|
time.sleep(1)
|
|
|
|
|
|
|
|
######## BASE FOUNDATIONAL "HIDDEN" CALLS ########
|
|
def _sendEP0(self, request=0, buf=None, value=0x200, index=0, timeout=1000):
|
|
if buf == None:
|
|
buf = 'HELLO THERE'
|
|
#return self._do.controlMsg(USB_BM_REQTYPE_TGT_EP|USB_BM_REQTYPE_TYPE_VENDOR|USB_BM_REQTYPE_DIR_OUT, request, "\x00\x00\x00\x00\x00\x00\x00\x00"+buf, value, index, timeout), buf
|
|
return self._do.controlMsg(USB_BM_REQTYPE_TGT_EP|USB_BM_REQTYPE_TYPE_VENDOR|USB_BM_REQTYPE_DIR_OUT, request, buf, value, index, timeout), buf
|
|
|
|
def _recvEP0(self, request=0, length=64, value=0, index=0, timeout=100):
|
|
retary = ["%c"%x for x in self._do.controlMsg(USB_BM_REQTYPE_TGT_EP|USB_BM_REQTYPE_TYPE_VENDOR|USB_BM_REQTYPE_DIR_IN, request, length, value, index, timeout)]
|
|
if len(retary):
|
|
return ''.join(retary)
|
|
return ""
|
|
|
|
def _sendEP5(self, buf=None, timeout=1000):
|
|
global direct
|
|
if (buf==None):
|
|
buf = "\xff\x82\x07\x00ABCDEFG"
|
|
if direct:
|
|
self._do.bulkWrite(5, buf, timeout)
|
|
return
|
|
|
|
while (len(buf)>0):
|
|
drain = buf[:self._usbmaxo]
|
|
buf = buf[self._usbmaxo:]
|
|
|
|
if self._debug: print >>sys.stderr,"XMIT:"+repr(drain)
|
|
try:
|
|
self._do.bulkWrite(5, drain, timeout)
|
|
except Exception, e:
|
|
if self._debug: print >>sys.stderr,"requeuing on error '%s' (%s)" % (repr(drain), e)
|
|
self.xsema.acquire()
|
|
msg = self.xmit_queue.insert(0, drain)
|
|
self.xsema.release()
|
|
if self._debug: print >>sys.stderr, repr(self.xmit_queue)
|
|
'''
|
|
drain = buf[:self._usbmaxo]
|
|
buf = buf[self._usbmaxo:]
|
|
if len(buf):
|
|
if self._debug: print >>sys.stderr,"requeuing '%s'" % repr(buf)
|
|
self.xsema.acquire()
|
|
msg = self.xmit_queue.insert(0, buf)
|
|
self.xsema.release()
|
|
if self._debug: print >>sys.stderr, repr(self.xmit_queue)
|
|
if self._debug: print >>sys.stderr,"XMIT:"+repr(drain)
|
|
try:
|
|
self._do.bulkWrite(5, drain, timeout)
|
|
except Exception, e:
|
|
if self._debug: print >>sys.stderr,"requeuing on error '%s' (%s)" % (repr(drain), e)
|
|
self.xsema.acquire()
|
|
msg = self.xmit_queue.insert(0, drain)
|
|
self.xsema.release()
|
|
if self._debug: print >>sys.stderr, repr(self.xmit_queue)
|
|
|
|
---
|
|
while (len(buf)>0):
|
|
drain = buf[:self._usbmaxo]
|
|
buf = buf[self._usbmaxo:]
|
|
|
|
if self._debug: print >>sys.stderr,"XMIT:"+repr(drain)
|
|
self._do.bulkWrite(5, drain, timeout)
|
|
time.sleep(1)
|
|
---
|
|
if (len(buf) > self._usbmaxo):
|
|
drain = buf[:self._usbmaxo]
|
|
buf = buf[self._usbmaxo:]
|
|
self.xsema.acquire()
|
|
msg = self.xmit_queue.insert(0, buf)
|
|
self.xsema.release()
|
|
else:
|
|
drain = buf[:]
|
|
if self._debug: print >>sys.stderr,"XMIT:"+repr(drain)
|
|
self._do.bulkWrite(5, drain, timeout)
|
|
---
|
|
while (len(buf)>0):
|
|
if (len(buf) > self._usbmaxo):
|
|
drain = buf[:self._usbmaxo]
|
|
buf = buf[self._usbmaxo:]
|
|
else:
|
|
drain = buf[:]
|
|
if self._debug: print >>sys.stderr,"XMIT:"+repr(drain)
|
|
self._do.bulkWrite(5, drain, timeout)
|
|
time.sleep(1)
|
|
'''
|
|
|
|
def _recvEP5(self, timeout=100):
|
|
retary = ["%c"%x for x in self._do.bulkRead(0x85, 500, timeout)]
|
|
if self._debug: print >>sys.stderr,"RECV:"+repr(retary)
|
|
if len(retary):
|
|
return ''.join(retary)
|
|
#return retary
|
|
return ''
|
|
|
|
def _clear_buffers(self, clear_recv_mbox=False):
|
|
threadGo = self._threadGo
|
|
self._threadGo = False
|
|
if self._debug:
|
|
print >>sys.stderr,("_clear_buffers()")
|
|
if clear_recv_mbox:
|
|
for key in self.recv_mbox.keys():
|
|
self.trash.extend(self.recvAll(key))
|
|
self.trash.append((time.time(),self.recv_queue))
|
|
self.recv_queue = ''
|
|
# self.xmit_queue = [] # do we want to keep this?
|
|
self._threadGo = threadGo
|
|
|
|
|
|
######## TRANSMIT/RECEIVE THREADING ########
|
|
def runEP5(self):
|
|
msg = ''
|
|
self.threadcounter = 0
|
|
|
|
while True:
|
|
if (self._do is None or not self._threadGo):
|
|
time.sleep(.04)
|
|
continue
|
|
|
|
self.threadcounter = (self.threadcounter + 1) & 0xffffffff
|
|
|
|
#### transmit stuff. if any exists in the xmit_queue
|
|
msgsent = False
|
|
msgrecv = False
|
|
try:
|
|
if len(self.xmit_queue):
|
|
self.xsema.acquire()
|
|
msg = self.xmit_queue.pop(0)
|
|
self.xsema.release()
|
|
self._sendEP5(msg)
|
|
msgsent = True
|
|
else:
|
|
if self._debug>3: sys.stderr.write("NoMsgToSend ")
|
|
#except IndexError:
|
|
#if self._debug==3: sys.stderr.write("NoMsgToSend ")
|
|
#pass
|
|
except:
|
|
sys.excepthook(*sys.exc_info())
|
|
|
|
|
|
#### handle debug application
|
|
try:
|
|
q = None
|
|
b = self.recv_mbox.get(APP_DEBUG, None)
|
|
if (b != None):
|
|
for cmd in b.keys():
|
|
q = b[cmd]
|
|
if len(q):
|
|
buf,timestamp = q.pop(0)
|
|
#cmd = ord(buf[1])
|
|
if self._debug > 1: print >>sys.stderr,("buf length: %x\t\t cmd: %x\t\t(%s)"%(len(buf), cmd, repr(buf)))
|
|
if (cmd == DEBUG_CMD_STRING):
|
|
if (len(buf) < 4):
|
|
if (len(q)):
|
|
buf2 = q.pop(0)
|
|
buf += buf2
|
|
q.insert(0,buf)
|
|
if self._debug: sys.stderr.write('*')
|
|
else:
|
|
length, = struct.unpack("<H", buf[2:4])
|
|
if self._debug >1: print >>sys.stderr,("len=%d"%length)
|
|
if (len(buf) < 4+length):
|
|
if (len(q)):
|
|
buf2 = q.pop(0)
|
|
buf += buf2
|
|
q.insert(0,buf)
|
|
if self._debug: sys.stderr.write('&')
|
|
else:
|
|
printbuf = buf[4:4+length]
|
|
requeuebuf = buf[4+length:]
|
|
if len(requeuebuf):
|
|
if self._debug>1: print >>sys.stderr,(" - DEBUG..requeuing %s"%repr(requeuebuf))
|
|
q.insert(0,requeuebuf)
|
|
print >>sys.stderr,("DEBUG: (%.3f) %s" % (timestamp, repr(printbuf)))
|
|
elif (cmd == DEBUG_CMD_HEX):
|
|
#print >>sys.stderr, repr(buf)
|
|
print >>sys.stderr, "DEBUG: (%.3f) %x"%(timestamp, struct.unpack("B", buf[4:5])[0])
|
|
elif (cmd == DEBUG_CMD_HEX16):
|
|
#print >>sys.stderr, repr(buf)
|
|
print >>sys.stderr, "DEBUG: (%.3f) %x"%(timestamp, struct.unpack("<H", buf[4:6])[0])
|
|
elif (cmd == DEBUG_CMD_HEX32):
|
|
#print >>sys.stderr, repr(buf)
|
|
print >>sys.stderr, "DEBUG: (%.3f) %x"%(timestamp, struct.unpack("<L", buf[4:8])[0])
|
|
elif (cmd == DEBUG_CMD_INT):
|
|
print >>sys.stderr, "DEBUG: (%.3f) %d"%(timestamp, struct.unpack("<L", buf[4:8])[0])
|
|
else:
|
|
print >>sys.stderr,('DEBUG COMMAND UNKNOWN: %x (buf=%s)'%(cmd,repr(buf)))
|
|
|
|
except:
|
|
sys.excepthook(*sys.exc_info())
|
|
|
|
#### receive stuff.
|
|
try:
|
|
#### first we populate the queue
|
|
msg = self._recvEP5(timeout=self.ep5timeout)
|
|
if len(msg) > 0:
|
|
self.recv_queue += msg
|
|
msgrecv = True
|
|
except usb.USBError, e:
|
|
#sys.stderr.write(repr(self.recv_queue))
|
|
#sys.stderr.write(repr(e))
|
|
errstr = repr(e)
|
|
if self._debug>4: print >>sys.stderr,repr(sys.exc_info())
|
|
if ('No error' in errstr):
|
|
pass
|
|
elif ('Operation timed out' in errstr):
|
|
pass
|
|
else:
|
|
if ('could not release intf' in errstr):
|
|
pass
|
|
elif ('No such device' in errstr):
|
|
self._threadGo = False
|
|
self.resetup(False)
|
|
elif ('Input/output error' in errstr): # USBerror 5
|
|
self._threadGo = False
|
|
self.resetup(False)
|
|
|
|
else:
|
|
if self._debug: print "Error in runEP5() (receiving): %s" % errstr
|
|
if self._debug>2: sys.excepthook(*sys.exc_info())
|
|
self._usberrorcnt += 1
|
|
pass
|
|
except AttributeError,e:
|
|
if "'NoneType' object has no attribute 'bInterfaceNumber'" in str(e):
|
|
print "Error: dongle went away. USB bus problems?"
|
|
self._threadGo = False
|
|
self.resetup(False)
|
|
|
|
except:
|
|
sys.excepthook(*sys.exc_info())
|
|
|
|
|
|
#### parse, sort, and deliver the mail.
|
|
try:
|
|
# FIXME: is this robust? or just overcomplex?
|
|
if len(self.recv_queue):
|
|
idx = self.recv_queue.find('@')
|
|
if (idx==-1):
|
|
if self._debug > 3:
|
|
sys.stderr.write('@')
|
|
else:
|
|
if (idx>0):
|
|
if self._debug: print >>sys.stderr,("runEP5(): idx>0?")
|
|
self.trash.append(self.recv_queue[:idx])
|
|
self.recv_queue = self.recv_queue[idx:]
|
|
|
|
# recv_queue is vulnerable here, but it's ok because we only modify it earlier in this same thread
|
|
# DON'T CHANGE recv_queue from other threads!
|
|
msg = self.recv_queue
|
|
msglen = len(msg)
|
|
while (msglen>=5): # if not enough to parse length... we'll wait.
|
|
if not self._recv_time: # should be 0 to start and when done with a packet
|
|
self._recv_time = time.time()
|
|
app = ord(msg[1])
|
|
cmd = ord(msg[2])
|
|
length, = struct.unpack("<H", msg[3:5])
|
|
|
|
if self._debug>1: print>>sys.stderr,("app=%x cmd=%x len=%x"%(app,cmd,length))
|
|
|
|
if (msglen >= length+5):
|
|
#### if the queue has enough characters to handle the next message... chop it and put it in the appropriate recv_mbox
|
|
msg = self.recv_queue[1:length+5] # drop the initial '@' and chop out the right number of chars
|
|
self.recv_queue = self.recv_queue[length+5:] # chop it out of the queue
|
|
|
|
b = self.recv_mbox.get(app,None)
|
|
if self.rsema.acquire(): # THREAD SAFETY DANCE
|
|
#if self._debug: print ("rsema.UNlocked", "rsema.locked")[self.rsema.locked()],0
|
|
try:
|
|
if (b == None):
|
|
b = {}
|
|
self.recv_mbox[app] = b
|
|
except:
|
|
sys.excepthook(*sys.exc_info())
|
|
finally:
|
|
self.rsema.release() # THREAD SAFETY DANCE COMPLETE
|
|
#if self._debug: print ("rsema.UNlocked", "rsema.locked")[self.rsema.locked()],0
|
|
|
|
q = b.get(cmd)
|
|
if self.rsema.acquire(): # THREAD SAFETY DANCE
|
|
#if self._debug: print ("rsema.UNlocked", "rsema.locked")[self.rsema.locked()],1
|
|
try:
|
|
if (q is None):
|
|
q = []
|
|
b[cmd] = q
|
|
|
|
q.append((msg, self._recv_time))
|
|
self._recv_time = 0 # we've delivered the current message
|
|
except:
|
|
sys.excepthook(*sys.exc_info())
|
|
finally:
|
|
self.rsema.release() # THREAD SAFETY DANCE COMPLETE
|
|
#if self._debug: print ("rsema.UNlocked", "rsema.locked")[self.rsema.locked()],1
|
|
|
|
else:
|
|
if self._debug>1: sys.stderr.write('=')
|
|
|
|
msg = self.recv_queue
|
|
msglen = len(msg)
|
|
# end of while loop
|
|
#else:
|
|
# if self._debug: sys.stderr.write('.')
|
|
except:
|
|
sys.excepthook(*sys.exc_info())
|
|
|
|
|
|
if not (msgsent or msgrecv or len(msg)) :
|
|
#time.sleep(.1)
|
|
self.ep5timeout = EP_TIMEOUT_IDLE
|
|
else:
|
|
self.ep5timeout = EP_TIMEOUT_ACTIVE
|
|
if self._debug > 5: sys.stderr.write(" %s:%s:%d .-P."%(msgsent,msgrecv,len(msg)))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
######## APPLICATION API ########
|
|
def recv(self, app, cmd=None, wait=USB_RX_WAIT):
|
|
for x in xrange(wait+1):
|
|
try:
|
|
b = self.recv_mbox.get(app)
|
|
if cmd is None:
|
|
keys = b.keys()
|
|
if len(keys):
|
|
cmd = b.keys()[-1]
|
|
if b is not None:
|
|
q = b.get(cmd)
|
|
#print >>sys.stderr,"debug(recv) q='%s'"%repr(q)
|
|
if q is not None and self.rsema.acquire(False):
|
|
#if self._debug: print ("rsema.UNlocked", "rsema.locked")[self.rsema.locked()],2
|
|
try:
|
|
resp, rt = q.pop(0)
|
|
self.rsema.release()
|
|
#if self._debug: print ("rsema.UNlocked", "rsema.locked")[self.rsema.locked()],2
|
|
return resp[4:], rt
|
|
except IndexError:
|
|
pass
|
|
#sys.excepthook(*sys.exc_info())
|
|
except AttributeError:
|
|
sys.excepthook(*sys.exc_info())
|
|
pass
|
|
self.rsema.release()
|
|
#if self._debug: print ("rsema.UNlocked", "rsema.locked")[self.rsema.locked()],2
|
|
except:
|
|
sys.excepthook(*sys.exc_info())
|
|
|
|
time.sleep(.001) # only hits here if we don't have something in queue
|
|
|
|
raise(ChipconUsbTimeoutException())
|
|
|
|
def recvAll(self, app, cmd=None):
|
|
retval = self.recv_mbox.get(app,None)
|
|
if retval is not None:
|
|
if cmd is not None:
|
|
b = retval
|
|
if self.rsema.acquire():
|
|
#if self._debug: print ("rsema.UNlocked", "rsema.locked")[self.rsema.locked()],3
|
|
try:
|
|
retval = b.get(cmd)
|
|
b[cmd]=[]
|
|
if len(retval):
|
|
retval = [ (d[4:],t) for d,t in retval ]
|
|
except:
|
|
sys.excepthook(*sys.exc_info())
|
|
finally:
|
|
self.rsema.release()
|
|
#if self._debug: print ("rsema.UNlocked", "rsema.locked")[self.rsema.locked()],3
|
|
else:
|
|
if self.rsema.acquire():
|
|
#if self._debug: print ("rsema.UNlocked", "rsema.locked")[self.rsema.locked()],4
|
|
try:
|
|
self.recv_mbox[app]={}
|
|
finally:
|
|
self.rsema.release()
|
|
#if self._debug: print ("rsema.UNlocked", "rsema.locked")[self.rsema.locked()],4
|
|
return retval
|
|
|
|
def send(self, app, cmd, buf, wait=USB_TX_WAIT):
|
|
msg = "%c%c%s%s"%(app,cmd, struct.pack("<H",len(buf)),buf)
|
|
self.xsema.acquire()
|
|
self.xmit_queue.append(msg)
|
|
self.xsema.release()
|
|
return self.recv(app, cmd, wait)
|
|
|
|
def getDebugCodes(self, timeout=100):
|
|
x = self._recvEP0(timeout=timeout)
|
|
if (x != None and len(x)==2):
|
|
return struct.unpack("BB", x)
|
|
else:
|
|
return x
|
|
|
|
def ep0GetAddr(self):
|
|
addr = self._recvEP0(request=EP0_CMD_GET_ADDRESS)
|
|
return addr
|
|
def ep0Reset(self):
|
|
x = self._recvEP0(request=0xfe, value=0x5352, index=0x4e54)
|
|
return x
|
|
|
|
def ep0Peek(self, addr, length, timeout=100):
|
|
x = self._recvEP0(request=EP0_CMD_PEEKX, value=addr, length=length, timeout=timeout)
|
|
return x#x[3:]
|
|
|
|
def ep0Poke(self, addr, buf='\x00', timeout=100):
|
|
x = self._sendEP0(request=EP0_CMD_POKEX, buf=buf, value=addr, timeout=timeout)
|
|
return x
|
|
|
|
def ep0Ping(self, count=10):
|
|
good=0
|
|
bad=0
|
|
for x in range(count):
|
|
#r = self._recvEP0(3, 10)
|
|
r = self._recvEP0(request=2, value=count, length=count, timeout=1000)
|
|
print "PING: %d bytes received: %s"%(len(r), repr(r))
|
|
if r==None:
|
|
bad+=1
|
|
else:
|
|
good+=1
|
|
return (good,bad)
|
|
|
|
def debug(self, delay=1):
|
|
while True:
|
|
"""
|
|
try:
|
|
print >>sys.stderr, ("DONGLE RESPONDING: mode :%x, last error# %d"%(self.getDebugCodes()))
|
|
except:
|
|
pass
|
|
print >>sys.stderr,('recv_queue:\t\t (%d bytes) "%s"'%(len(self.recv_queue),repr(self.recv_queue)[:len(self.recv_queue)%39+20]))
|
|
print >>sys.stderr,('trash: \t\t (%d bytes) "%s"'%(len(self.trash),repr(self.trash)[:len(self.trash)%39+20]))
|
|
print >>sys.stderr,('recv_mbox \t\t (%d keys) "%s"'%(len(self.recv_mbox),repr(self.recv_mbox)[:len(repr(self.recv_mbox))%79]))
|
|
for x in self.recv_mbox.keys():
|
|
print >>sys.stderr,(' recv_mbox %d\t (%d records) "%s"'%(x,len(self.recv_mbox[x]),repr(self.recv_mbox[x])[:len(repr(self.recv_mbox[x]))%79]))
|
|
"""
|
|
print self.reprRadioState()
|
|
print self.reprClientState()
|
|
|
|
x,y,z = select.select([sys.stdin],[],[], delay)
|
|
if sys.stdin in x:
|
|
sys.stdin.read(1)
|
|
break
|
|
|
|
def ping(self, count=10, buf="ABCDEFGHIJKLMNOPQRSTUVWXYZ", wait=1000):
|
|
good=0
|
|
bad=0
|
|
start = time.time()
|
|
for x in range(count):
|
|
istart = time.time()
|
|
|
|
try:
|
|
r = self.send(APP_SYSTEM, SYS_CMD_PING, buf, wait)
|
|
r,rt = r
|
|
istop = time.time()
|
|
print "PING: %d bytes transmitted, received: %s (%f seconds)"%(len(buf), repr(r), istop-istart)
|
|
except ChipconUsbTimeoutException, e:
|
|
r = None
|
|
print "Ping Failed."
|
|
if r==None:
|
|
bad+=1
|
|
else:
|
|
good+=1
|
|
stop = time.time()
|
|
return (good,bad,stop-start)
|
|
|
|
def bootloader(self):
|
|
'''
|
|
switch to bootloader mode. based on Fergus Noble's CC-Bootloader (https://github.com/fnoble/CC-Bootloader)
|
|
this allows the firmware to be updated via USB instead of goodfet/ccdebugger
|
|
'''
|
|
try:
|
|
r = self.send(APP_SYSTEM, SYS_CMD_BOOTLOADER, "")
|
|
except ChipconUsbTimeoutException:
|
|
pass
|
|
|
|
def RESET(self):
|
|
try:
|
|
r = self.send(APP_SYSTEM, SYS_CMD_RESET, "RESET_NOW\x00")
|
|
except ChipconUsbTimeoutException:
|
|
pass
|
|
|
|
def peek(self, addr, bytecount=1):
|
|
r, t = self.send(APP_SYSTEM, SYS_CMD_PEEK, struct.pack("<HH", bytecount, addr))
|
|
return r
|
|
|
|
def poke(self, addr, data):
|
|
r, t = self.send(APP_SYSTEM, SYS_CMD_POKE, struct.pack("<H", addr) + data)
|
|
return r
|
|
|
|
def pokeReg(self, addr, data):
|
|
r, t = self.send(APP_SYSTEM, SYS_CMD_POKE_REG, struct.pack("<H", addr) + data)
|
|
return r
|
|
|
|
def getBuildInfo(self):
|
|
r, t = self.send(APP_SYSTEM, SYS_CMD_BUILDTYPE, '')
|
|
return r
|
|
|
|
def getInterruptRegisters(self):
|
|
regs = {}
|
|
# IEN0,1,2
|
|
regs['IEN0'] = self.peek(IEN0,1)
|
|
regs['IEN1'] = self.peek(IEN1,1)
|
|
regs['IEN2'] = self.peek(IEN2,1)
|
|
# TCON
|
|
regs['TCON'] = self.peek(TCON,1)
|
|
# S0CON
|
|
regs['S0CON'] = self.peek(S0CON,1)
|
|
# IRCON
|
|
regs['IRCON'] = self.peek(IRCON,1)
|
|
# IRCON2
|
|
regs['IRCON2'] = self.peek(IRCON2,1)
|
|
# S1CON
|
|
regs['S1CON'] = self.peek(S1CON,1)
|
|
# RFIF
|
|
regs['RFIF'] = self.peek(RFIF,1)
|
|
# DMAIE
|
|
regs['DMAIE'] = self.peek(DMAIE,1)
|
|
# DMAIF
|
|
regs['DMAIF'] = self.peek(DMAIF,1)
|
|
# DMAIRQ
|
|
regs['DMAIRQ'] = self.peek(DMAIRQ,1)
|
|
return regs
|
|
|
|
######## RADIO METHODS #########
|
|
### radio state
|
|
def getMARCSTATE(self, radiocfg=None):
|
|
if radiocfg is None:
|
|
self.getRadioConfig()
|
|
radiocfg=self.radiocfg
|
|
|
|
mode = radiocfg.marcstate
|
|
return (MODES[mode], mode)
|
|
|
|
### set standard radio state to TX/RX/IDLE (TX is pretty much only good for jamming). TX/RX modes are set to return to whatever state you choose here.
|
|
def setModeTX(self):
|
|
'''
|
|
BOTH: set radio to TX state
|
|
AND: set radio to return to TX state when done with other states
|
|
'''
|
|
self.setRfMode(RF_STATE_TX)
|
|
|
|
def setModeRX(self):
|
|
'''
|
|
BOTH: set radio to RX state
|
|
AND: set radio to return to RX state when done with other states
|
|
'''
|
|
self.setRfMode(RF_STATE_RX)
|
|
|
|
def setModeIDLE(self):
|
|
'''
|
|
BOTH: set radio to IDLE state
|
|
AND: set radio to return to IDLE state when done with other states
|
|
'''
|
|
self.setRfMode(RF_STATE_IDLE)
|
|
|
|
|
|
### send raw state change to radio (doesn't update the return state for after RX/TX occurs)
|
|
def strobeModeTX(self):
|
|
'''
|
|
set radio to TX state (transient)
|
|
'''
|
|
self.poke(X_RFST, "%c"%RFST_STX)
|
|
|
|
def strobeModeRX(self):
|
|
'''
|
|
set radio to RX state (transient)
|
|
'''
|
|
self.poke(X_RFST, "%c"%RFST_SRX)
|
|
|
|
def strobeModeIDLE(self):
|
|
'''
|
|
set radio to IDLE state (transient)
|
|
'''
|
|
self.poke(X_RFST, "%c"%RFST_SIDLE)
|
|
|
|
def strobeModeFSTXON(self):
|
|
'''
|
|
set radio to FSTXON state (transient)
|
|
'''
|
|
self.poke(X_RFST, "%c"%RFST_SFSTXON)
|
|
|
|
def strobeModeCAL(self):
|
|
'''
|
|
set radio to CAL state (will return to whichever state is configured (via setMode* functions)
|
|
'''
|
|
self.poke(X_RFST, "%c"%RFST_SCAL)
|
|
|
|
def strobeModeReturn(self, marcstate=None):
|
|
"""
|
|
attempts to return the the correct mode after configuring some radio register(s).
|
|
it uses the marcstate provided (or self.radiocfg.marcstate if none are provided) to determine how to strobe the radio.
|
|
"""
|
|
if marcstate is None:
|
|
marcstate = self.radiocfg.marcstate
|
|
self.poke(X_RFST, "%c"%MARC_STATE_MAPPINGS[marcstate][2])
|
|
|
|
|
|
|
|
def setRFRegister(self, regaddr, value, suppress=False):
|
|
'''
|
|
set the radio register 'regaddr' to 'value' (first setting RF state to IDLE, then returning to RX/TX)
|
|
value is always considered a 1-byte value
|
|
if 'suppress' the radio state (RX/TX/IDLE) is not modified
|
|
'''
|
|
if suppress:
|
|
self.poke(regaddr, chr(value))
|
|
return
|
|
|
|
marcstate = self.radiocfg.marcstate
|
|
if marcstate != MARC_STATE_IDLE:
|
|
self.strobeModeIDLE()
|
|
|
|
self.poke(regaddr, chr(value))
|
|
|
|
self.strobeModeReturn(marcstate)
|
|
#if (marcstate == MARC_STATE_RX):
|
|
#self.strobeModeRX()
|
|
#elif (marcstate == MARC_STATE_TX):
|
|
#self.strobeModeTX()
|
|
# if other than these, we can stay in IDLE
|
|
|
|
### radio config
|
|
def getRadioConfig(self):
|
|
bytedef = self.peek(0xdf00, 0x3e)
|
|
self.radiocfg.vsParse(bytedef)
|
|
return bytedef
|
|
|
|
def setRadioConfig(self, bytedef = None):
|
|
if bytedef is None:
|
|
bytedef = self.radiocfg.vsEmit()
|
|
|
|
statestr, marcstate = self.getMARCSTATE()
|
|
if marcstate != MARC_STATE_IDLE:
|
|
self.strobeModeIDLE()
|
|
|
|
self.poke(0xdf00, bytedef)
|
|
|
|
self.strobeModeReturn(marcstate)
|
|
#if (marcstate == MARC_STATE_RX):
|
|
#self.strobeModeRX()
|
|
#elif (marcstate == MARC_STATE_TX):
|
|
#self.strobeModeTX()
|
|
|
|
self.getRadioConfig()
|
|
|
|
return bytedef
|
|
|
|
def setFreq(self, freq=902000000, mhz=24, radiocfg=None, applyConfig=True):
|
|
if radiocfg is None:
|
|
radiocfg = self.radiocfg
|
|
else:
|
|
applyConfig = False
|
|
|
|
freqmult = (0x10000 / 1000000.0) / mhz
|
|
num = int(freq * freqmult)
|
|
radiocfg.freq2 = num >> 16
|
|
radiocfg.freq1 = (num>>8) & 0xff
|
|
radiocfg.freq0 = num & 0xff
|
|
|
|
if (freq > FREQ_EDGE_900 and freq < FREQ_MID_900) or (freq > FREQ_EDGE_400 and freq < FREQ_MID_400) or (freq < FREQ_MID_300):
|
|
# select low VCO
|
|
radiocfg.fscal2 = 0x0A
|
|
else:
|
|
# select high VCO
|
|
radiocfg.fscal2 = 0x2A
|
|
|
|
if applyConfig:
|
|
marcstate = radiocfg.marcstate
|
|
if marcstate != MARC_STATE_IDLE:
|
|
self.strobeModeIDLE()
|
|
self.poke(FREQ2, struct.pack("3B", self.radiocfg.freq2, self.radiocfg.freq1, self.radiocfg.freq0))
|
|
self.poke(FSCAL2, struct.pack("B", self.radiocfg.fscal2))
|
|
|
|
self.strobeModeReturn(marcstate)
|
|
#if (radiocfg.marcstate == MARC_STATE_RX):
|
|
#self.strobeModeRX()
|
|
#elif (radiocfg.marcstate == MARC_STATE_TX):
|
|
#self.strobeModeTX()
|
|
|
|
def getFreq(self, mhz=24, radiocfg=None):
|
|
freqmult = (0x10000 / 1000000.0) / mhz
|
|
if radiocfg==None:
|
|
self.getRadioConfig()
|
|
radiocfg = self.radiocfg
|
|
|
|
num = (radiocfg.freq2<<16) + (radiocfg.freq1<<8) + radiocfg.freq0
|
|
freq = num / freqmult
|
|
return freq, hex(num)
|
|
|
|
# set 'standard' power - for more complex power shaping this will need to be done manually
|
|
def setPower(self, power=None, radiocfg=None, invert=False):
|
|
if radiocfg == None:
|
|
self.getRadioConfig()
|
|
radiocfg = self.radiocfg
|
|
|
|
mod= self.getMdmModulation(radiocfg=radiocfg)
|
|
|
|
# we may be only changing PA_POWER, not power levels
|
|
if power is not None:
|
|
if mod == MOD_ASK_OOK and not invert:
|
|
radiocfg.pa_table0= 0x00
|
|
radiocfg.pa_table1= power
|
|
else:
|
|
radiocfg.pa_table0= power
|
|
radiocfg.pa_table1= 0x00
|
|
self.setRFRegister(PA_TABLE0, radiocfg.pa_table0)
|
|
self.setRFRegister(PA_TABLE1, radiocfg.pa_table1)
|
|
|
|
radiocfg.frend0 &= ~FREND0_PA_POWER
|
|
if mod == MOD_ASK_OOK:
|
|
radiocfg.frend0 |= 0x01
|
|
|
|
self.setRFRegister(FREND0, radiocfg.frend0)
|
|
|
|
# max power settings are frequency dependent, so set frequency before calling
|
|
def setMaxPower(self, radiocfg=None, invert=False):
|
|
if radiocfg == None:
|
|
self.getRadioConfig()
|
|
radiocfg = self.radiocfg
|
|
|
|
freq= self.getFreq(radiocfg=radiocfg)[0]
|
|
|
|
if freq <= 400000000:
|
|
power= 0xC2
|
|
elif freq <= 464000000:
|
|
power= 0xC0
|
|
elif freq <= 900000000:
|
|
power= 0xC2
|
|
else:
|
|
power= 0xC0
|
|
|
|
self.setPower(power, radiocfg=radiocfg, invert=invert)
|
|
|
|
def setMdmModulation(self, mod, radiocfg=None, invert=False):
|
|
if radiocfg == None:
|
|
self.getRadioConfig()
|
|
radiocfg = self.radiocfg
|
|
|
|
if (mod) & ~MDMCFG2_MOD_FORMAT:
|
|
raise(Exception("Please use constants MOD_FORMAT_* to specify modulation and "))
|
|
|
|
radiocfg.mdmcfg2 &= ~MDMCFG2_MOD_FORMAT
|
|
radiocfg.mdmcfg2 |= (mod)
|
|
|
|
power= None
|
|
# ASK_OOK needs to flip power table
|
|
if mod == MOD_ASK_OOK and not invert:
|
|
if radiocfg.pa_table1 == 0x00 and radiocfg.pa_table0 != 0x00:
|
|
power= radiocfg.pa_table0
|
|
else:
|
|
if radiocfg.pa_table0 == 0x00 and radiocfg.pa_table1 != 0x00:
|
|
power= radiocfg.pa_table1
|
|
|
|
self.setRFRegister(MDMCFG2, radiocfg.mdmcfg2)
|
|
self.setPower(power, radiocfg=radiocfg, invert=invert)
|
|
|
|
def getMdmModulation(self, radiocfg=None):
|
|
if radiocfg == None:
|
|
self.getRadioConfig()
|
|
radiocfg = self.radiocfg
|
|
|
|
mdmcfg2 = radiocfg.mdmcfg2
|
|
mod = (mdmcfg2) & MDMCFG2_MOD_FORMAT
|
|
return mod
|
|
|
|
def printRadioConfig(self, mhz=24, radiocfg=None):
|
|
print self.reprRadioConfig(mhz, radiocfg)
|
|
|
|
def reprRadioConfig(self, mhz=24, radiocfg=None):
|
|
if radiocfg == None:
|
|
self.getRadioConfig()
|
|
radiocfg = self.radiocfg
|
|
output = []
|
|
|
|
output.append( "== Hardware ==")
|
|
output.append( self.reprHardwareConfig())
|
|
output.append( "\n== Software ==")
|
|
output.append( self.reprSoftwareConfig())
|
|
output.append( "\n== Frequency Configuration ==")
|
|
output.append( self.reprFreqConfig(mhz, radiocfg))
|
|
output.append( "\n== Modem Configuration ==")
|
|
output.append( self.reprModemConfig(mhz, radiocfg))
|
|
output.append( "\n== Packet Configuration ==")
|
|
output.append( self.reprPacketConfig(radiocfg))
|
|
output.append( "\n== AES Crypto Configuration ==")
|
|
output.append( self.reprAESMode())
|
|
output.append( "\n== Radio Test Signal Configuration ==")
|
|
output.append( self.reprRadioTestSignalConfig(radiocfg))
|
|
output.append( "\n== Radio State ==")
|
|
output.append( self.reprRadioState(radiocfg))
|
|
output.append("\n== Client State ==")
|
|
output.append( self.reprClientState())
|
|
return "\n".join(output)
|
|
|
|
def reprHardwareConfig(self):
|
|
output= []
|
|
|
|
hardware= self.getBuildInfo()
|
|
output.append("Dongle: %s" % hardware.split(' ')[0])
|
|
try:
|
|
output.append("Firmware rev: %s" % hardware.split('r')[1])
|
|
except:
|
|
output.append("Firmware rev: Not found! Update needed!")
|
|
# see if we have a bootloader by loooking for it's recognition semaphores
|
|
# in SFR I2SCLKF0 & I2SCLKF1
|
|
if(self.peek(0xDF46,1) == '\xF0' and self.peek(0xDF47,1) == '\x0D'):
|
|
output.append("Bootloader: CC-Bootloader")
|
|
else:
|
|
output.append("Bootloader: Not installed")
|
|
return "\n".join(output)
|
|
|
|
def reprSoftwareConfig(self):
|
|
output= []
|
|
|
|
output.append("rflib rev: %s" % RFLIB_VERSION)
|
|
return "\n".join(output)
|
|
|
|
def reprMdmModulation(self, radiocfg=None):
|
|
mod = self.getMdmModulation(radiocfg)
|
|
return ("Modulation: %s" % MODULATIONS[mod])
|
|
|
|
def getMdmChanSpc(self, mhz=24, radiocfg=None):
|
|
if radiocfg==None:
|
|
self.getRadioConfig()
|
|
radiocfg = self.radiocfg
|
|
|
|
chanspc_m = radiocfg.mdmcfg0
|
|
chanspc_e = radiocfg.mdmcfg1 & 3
|
|
chanspc = 1000000.0 * mhz/pow(2,18) * (256 + chanspc_m) * pow(2, chanspc_e)
|
|
#print "chanspc_e: %x chanspc_m: %x chanspc: %f hz" % (chanspc_e, chanspc_m, chanspc)
|
|
return (chanspc)
|
|
|
|
def setMdmChanSpc(self, chanspc=None, chanspc_m=None, chanspc_e=None, mhz=24, radiocfg=None):
|
|
'''
|
|
calculates the appropriate exponent and mantissa and updates the correct registers
|
|
chanspc is in kHz. if you prefer, you may set the chanspc_m and chanspc_e settings
|
|
directly.
|
|
|
|
only use one or the other:
|
|
* chanspc
|
|
* chanspc_m and chanspc_e
|
|
'''
|
|
if radiocfg==None:
|
|
self.getRadioConfig()
|
|
radiocfg = self.radiocfg
|
|
|
|
if (chanspc != None):
|
|
for e in range(4):
|
|
m = int(((chanspc * pow(2,18) / (1000000.0 * mhz * pow(2,e)))-256) +.5) # rounded evenly
|
|
if m < 256:
|
|
chanspc_e = e
|
|
chanspc_m = m
|
|
break
|
|
if chanspc_e is None or chanspc_m is None:
|
|
raise(Exception("ChanSpc does not translate into acceptable parameters. Should you be changing this?"))
|
|
|
|
#chanspc = 1000000.0 * mhz/pow(2,18) * (256 + chanspc_m) * pow(2, chanspc_e)
|
|
#print "chanspc_e: %x chanspc_m: %x chanspc: %f hz" % (chanspc_e, chanspc_m, chanspc)
|
|
|
|
radiocfg.mdmcfg1 &= ~MDMCFG1_CHANSPC_E # clear out old exponent value
|
|
radiocfg.mdmcfg1 |= chanspc_e
|
|
radiocfg.mdmcfg0 = chanspc_m
|
|
self.setRFRegister(MDMCFG1, (radiocfg.mdmcfg1))
|
|
self.setRFRegister(MDMCFG0, (radiocfg.mdmcfg0))
|
|
|
|
def makePktVLEN(self, maxlen=RF_MAX_TX_BLOCK, radiocfg=None):
|
|
if radiocfg==None:
|
|
self.getRadioConfig()
|
|
radiocfg = self.radiocfg
|
|
|
|
if maxlen > RF_MAX_TX_BLOCK:
|
|
raise(Exception("Packet too large (%d bytes). Maximum variable length packet is %d bytes." % (maxlen, RF_MAX_TX_BLOCK)))
|
|
|
|
radiocfg.pktctrl0 &= ~PKTCTRL0_LENGTH_CONFIG
|
|
radiocfg.pktctrl0 |= 1
|
|
radiocfg.pktlen = maxlen
|
|
self.setRFRegister(PKTCTRL0, (radiocfg.pktctrl0))
|
|
self.setRFRegister(PKTLEN, (radiocfg.pktlen))
|
|
|
|
|
|
def makePktFLEN(self, flen=RF_MAX_TX_BLOCK, radiocfg=None):
|
|
if radiocfg==None:
|
|
self.getRadioConfig()
|
|
radiocfg = self.radiocfg
|
|
|
|
if flen > EP5OUT_BUFFER_SIZE - 4:
|
|
raise(Exception("Packet too large (%d bytes). Maximum fixed length packet is %d bytes." % (flen, EP5OUT_BUFFER_SIZE - 6)))
|
|
|
|
radiocfg.pktctrl0 &= ~PKTCTRL0_LENGTH_CONFIG
|
|
# if we're sending a large block, pktlen is dealt with by the firmware
|
|
# using 'infinite' mode
|
|
if flen > RF_MAX_TX_BLOCK:
|
|
radiocfg.pktlen = 0x00
|
|
else:
|
|
radiocfg.pktlen = flen
|
|
self.setRFRegister(PKTCTRL0, (radiocfg.pktctrl0))
|
|
self.setRFRegister(PKTLEN, (radiocfg.pktlen))
|
|
|
|
def setEnablePktCRC(self, enable=True, radiocfg=None):
|
|
if radiocfg==None:
|
|
self.getRadioConfig()
|
|
radiocfg = self.radiocfg
|
|
|
|
crcE = (0,1)[enable]<<2
|
|
crcM = ~(1<<2)
|
|
radiocfg.pktctrl0 &= crcM
|
|
radiocfg.pktctrl0 |= crcE
|
|
self.setRFRegister(PKTCTRL0, (radiocfg.pktctrl0))
|
|
|
|
def getEnablePktCRC(self, radiocfg=None):
|
|
if radiocfg==None:
|
|
self.getRadioConfig()
|
|
radiocfg = self.radiocfg
|
|
|
|
return (radiocfg.pktctrl0 >>2) & 0x1
|
|
|
|
def setEnablePktDataWhitening(self, enable=True, radiocfg=None):
|
|
if radiocfg==None:
|
|
self.getRadioConfig()
|
|
radiocfg = self.radiocfg
|
|
|
|
dwEnable = (0,1)[enable]<<6
|
|
radiocfg.pktctrl0 &= ~PKTCTRL0_WHITE_DATA
|
|
radiocfg.pktctrl0 |= dwEnable
|
|
self.setRFRegister(PKTCTRL0, (radiocfg.pktctrl0))
|
|
|
|
def getEnablePktDataWhitening(self, radiocfg=None):
|
|
if radiocfg==None:
|
|
self.getRadioConfig()
|
|
radiocfg = self.radiocfg
|
|
|
|
return (radiocfg.pktctrl0 >>6) & 0x1
|
|
|
|
def setPktPQT(self, num=3, radiocfg=None):
|
|
if radiocfg==None:
|
|
self.getRadioConfig()
|
|
radiocfg = self.radiocfg
|
|
|
|
num &= 7
|
|
num <<= 5
|
|
numM = ~(7<<5)
|
|
radiocfg.pktctrl1 &= numM
|
|
radiocfg.pktctrl1 |= num
|
|
self.setRFRegister(PKTCTRL1, (radiocfg.pktctrl1))
|
|
|
|
def getPktPQT(self, radiocfg=None):
|
|
if radiocfg==None:
|
|
self.getRadioConfig()
|
|
radiocfg = self.radiocfg
|
|
|
|
return (radiocfg.pktctrl1 >> 5) & 7
|
|
|
|
def setEnablePktAppendStatus(self, enable=True, radiocfg=None):
|
|
'''
|
|
enable append status bytes. two bytes will be appended to the payload of the packet, containing
|
|
RSSI and LQI values as well as CRC OK.
|
|
'''
|
|
if radiocfg == None:
|
|
self.getRadioConfig()
|
|
radiocfg = self.radiocfg
|
|
|
|
radiocfg.pktctrl1 &= ~PKTCTRL1_APPEND_STATUS
|
|
radiocfg.pktctrl1 |= (enable<<2)
|
|
self.setRFRegister(PKTCTRL1, radiocfg.pktctrl1)
|
|
|
|
def getEnablePktAppendStatus(self, radiocfg=None):
|
|
'''
|
|
return append status bytes setting.
|
|
'''
|
|
if radiocfg == None:
|
|
self.getRadioConfig()
|
|
radiocfg = self.radiocfg
|
|
|
|
pktctrl1 = radiocfg.pktctrl1
|
|
append = (pktctrl1>>2) & 0x01
|
|
return append
|
|
|
|
def setEnableMdmManchester(self, enable=True, radiocfg=None):
|
|
if radiocfg == None:
|
|
self.getRadioConfig()
|
|
radiocfg = self.radiocfg
|
|
|
|
radiocfg.mdmcfg2 &= ~MDMCFG2_MANCHESTER_EN
|
|
radiocfg.mdmcfg2 |= (enable<<3)
|
|
self.setRFRegister(MDMCFG2, radiocfg.mdmcfg2)
|
|
|
|
def getEnableMdmManchester(self, radiocfg=None):
|
|
if radiocfg == None:
|
|
self.getRadioConfig()
|
|
radiocfg = self.radiocfg
|
|
|
|
mdmcfg2 = radiocfg.mdmcfg2
|
|
mchstr = (mdmcfg2>>3) & 0x01
|
|
return mchstr
|
|
|
|
def setEnableMdmFEC(self, enable=True, radiocfg=None):
|
|
if radiocfg==None:
|
|
self.getRadioConfig()
|
|
radiocfg = self.radiocfg
|
|
|
|
fecEnable = (0,1)[enable]<<7
|
|
radiocfg.mdmcfg1 &= ~MFMCFG1_FEC_EN
|
|
radiocfg.mdmcfg1 |= fecEnable
|
|
self.setRFRegister(MDMCFG1, (radiocfg.mdmcfg1))
|
|
|
|
def getEnableMdmFEC(self, radiocfg=None):
|
|
if radiocfg == None:
|
|
self.getRadioConfig()
|
|
radiocfg = self.radiocfg
|
|
|
|
mdmcfg1 = radiocfg.mdmcfg1
|
|
fecEnable = (mdmcfg1>>7) & 0x01
|
|
return fecEnable
|
|
|
|
def setEnableMdmDCFilter(self, enable=True, radiocfg=None):
|
|
if radiocfg==None:
|
|
self.getRadioConfig()
|
|
radiocfg = self.radiocfg
|
|
|
|
dcfEnable = (0,1)[enable]<<7
|
|
radiocfg.mdmcfg2 &= ~MDMCFG2_DEM_DCFILT_OFF
|
|
radiocfg.mdmcfg2 |= dcfEnable
|
|
self.setRFRegister(MDMCFG2, radiocfg.mdmcfg2)
|
|
|
|
def getEnableMdmDCFilter(self, radiocfg=None):
|
|
if radiocfg==None:
|
|
self.getRadioConfig()
|
|
radiocfg = self.radiocfg
|
|
|
|
dcfEnable = (radiocfg.mdmcfg2>>7) & 0x1
|
|
return dcfEnable
|
|
|
|
|
|
def setFsIF(self, freq_if, mhz=24, radiocfg=None):
|
|
'''
|
|
Note that the SmartRF Studio software
|
|
automatically calculates the optimum register
|
|
setting based on channel spacing and channel
|
|
filter bandwidth. (from cc1110f32.pdf)
|
|
'''
|
|
if radiocfg==None:
|
|
self.getRadioConfig()
|
|
radiocfg = self.radiocfg
|
|
|
|
ifBits = freq_if * pow(2,10) / (1000000.0 * mhz)
|
|
ifBits = int(ifBits + .5) # rounded evenly
|
|
|
|
if ifBits >0x1f:
|
|
raise(Exception("FAIL: freq_if is too high? freqbits: %x (must be <0x1f)" % ifBits))
|
|
radiocfg.fsctrl1 &= ~(0x1f)
|
|
radiocfg.fsctrl1 |= int(ifBits)
|
|
self.setRFRegister(FSCTRL1, (radiocfg.fsctrl1))
|
|
|
|
def getFsIF(self, mhz=24, radiocfg=None):
|
|
if radiocfg==None:
|
|
self.getRadioConfig()
|
|
radiocfg = self.radiocfg
|
|
|
|
freq_if = (radiocfg.fsctrl1&0x1f) * (1000000.0 * mhz / pow(2,10))
|
|
return freq_if
|
|
|
|
|
|
def setFsOffset(self, if_off, mhz=24, radiocfg=None):
|
|
'''
|
|
Note that the SmartRF Studio software
|
|
automatically calculates the optimum register
|
|
setting based on channel spacing and channel
|
|
filter bandwidth. (from cc1110f32.pdf)
|
|
'''
|
|
if radiocfg==None:
|
|
self.getRadioConfig()
|
|
radiocfg = self.radiocfg
|
|
|
|
radiocfg.fsctrl0 = if_off
|
|
self.setRFRegister(FSCTRL0, (radiocfg.fsctrl0))
|
|
|
|
def getFsOffset(self, mhz=24, radiocfg=None):
|
|
if radiocfg==None:
|
|
self.getRadioConfig()
|
|
radiocfg = self.radiocfg
|
|
|
|
freqoff = radiocfg.fsctrl0
|
|
return freqoff
|
|
|
|
def getChannel(self, radiocfg=None):
|
|
if radiocfg==None:
|
|
self.getRadioConfig()
|
|
radiocfg = self.radiocfg
|
|
|
|
self.getRadioConfig()
|
|
return radiocfg.channr
|
|
|
|
def setChannel(self, channr, radiocfg=None):
|
|
if radiocfg==None:
|
|
self.getRadioConfig()
|
|
radiocfg = self.radiocfg
|
|
|
|
radiocfg.channr = channr
|
|
self.setRFRegister(CHANNR, (radiocfg.channr))
|
|
|
|
def setMdmChanBW(self, bw, mhz=24, radiocfg=None):
|
|
'''
|
|
For best performance, the channel filter
|
|
bandwidth should be selected so that the
|
|
signal bandwidth occupies at most 80% of the
|
|
channel filter bandwidth. The channel centre
|
|
tolerance due to crystal accuracy should also
|
|
be subtracted from the signal bandwidth. The
|
|
following example illustrates this:
|
|
|
|
With the channel filter bandwidth set to 500
|
|
kHz, the signal should stay within 80% of 500
|
|
kHz, which is 400 kHz. Assuming 915 MHz
|
|
frequency and +/-20 ppm frequency uncertainty
|
|
for both the transmitting device and the
|
|
receiving device, the total frequency
|
|
uncertainty is +/-40 ppm of 915 MHz, which is
|
|
+/-37 kHz. If the whole transmitted signal
|
|
bandwidth is to be received within 400 kHz, the
|
|
transmitted signal bandwidth should be
|
|
maximum 400 kHz - 2*37 kHz, which is 326
|
|
kHz.
|
|
|
|
DR:1.2kb Dev:5.1khz Mod:GFSK RXBW:63kHz sensitive fsctrl1:06 mdmcfg:e5 a3 13 23 11 dev:16 foc/bscfg:17/6c agctrl:03 40 91 frend:56 10
|
|
DR:1.2kb Dev:5.1khz Mod:GFSK RXBW:63kHz lowpower fsctrl1:06 mdmcfg:e5 a3 93 23 11 dev:16 foc/bscfg:17/6c agctrl:03 40 91 frend:56 10 (DEM_DCFILT_OFF)
|
|
DR:2.4kb Dev:5.1khz Mod:GFSK RXBW:63kHz sensitive fsctrl1:06 mdmcfg:e6 a3 13 23 11 dev:16 foc/bscfg:17/6c agctrl:03 40 91 frend:56 10
|
|
DR:2.4kb Dev:5.1khz Mod:GFSK RXBW:63kHz lowpower fsctrl1:06 mdmcfg:e6 a3 93 23 11 dev:16 foc/bscfg:17/6c agctrl:03 40 91 frend:56 10 (DEM_DCFILT_OFF)
|
|
DR:38.4kb Dev:20khz Mod:GFSK RXBW:94kHz sensitive fsctrl1:08 mdmcfg:ca a3 13 23 11 dev:36 foc/bscfg:16/6c agctrl:43 40 91 frend:56 10 (IF changes, Deviation)
|
|
DR:38.4kb Dev:20khz Mod:GFSK RXBW:94kHz lowpower fsctrl1:08 mdmcfg:ca a3 93 23 11 dev:36 foc/bscfg:16/6c agctrl:43 40 91 frend:56 10 (.. DEM_DCFILT_OFF)
|
|
|
|
DR:250kb Dev:129khz Mod:GFSK RXBW:600kHz sensitive fsctrl1:0c mdmcfg:1d 55 13 23 11 dev:63 foc/bscfg:1d/1c agctrl:c7 00 b0 frend:b6 10 (IF_changes, Deviation)
|
|
|
|
DR:500kb Mod:MSK RXBW:750kHz sensitive fsctrl1:0e mdmcfg:0e 55 73 43 11 dev:00 foc/bscfg:1d/1c agctrl:c7 00 b0 frend:b6 10 (IF_changes, Modulation of course, Deviation has different meaning with MSK)
|
|
'''
|
|
if radiocfg==None:
|
|
self.getRadioConfig()
|
|
radiocfg = self.radiocfg
|
|
|
|
chanbw_e = None
|
|
chanbw_m = None
|
|
for e in range(4):
|
|
m = int(((mhz*1000000.0 / (bw *pow(2,e) * 8.0 )) - 4) + .5) # rounded evenly
|
|
if m < 4:
|
|
chanbw_e = e
|
|
chanbw_m = m
|
|
break
|
|
if chanbw_e is None:
|
|
raise(Exception("ChanBW does not translate into acceptable parameters. Should you be changing this?"))
|
|
|
|
bw = 1000.0*mhz / (8.0*(4+chanbw_m) * pow(2,chanbw_e))
|
|
#print "chanbw_e: %x chanbw_m: %x chanbw: %f kHz" % (e, m, bw)
|
|
|
|
radiocfg.mdmcfg4 &= ~(MDMCFG4_CHANBW_E | MDMCFG4_CHANBW_M)
|
|
radiocfg.mdmcfg4 |= ((chanbw_e<<6) | (chanbw_m<<4))
|
|
self.setRFRegister(MDMCFG4, (radiocfg.mdmcfg4))
|
|
|
|
def getMdmChanBW(self, mhz=24, radiocfg=None):
|
|
if radiocfg==None:
|
|
self.getRadioConfig()
|
|
radiocfg = self.radiocfg
|
|
|
|
chanbw_e = (radiocfg.mdmcfg4 >> 6) & 0x3
|
|
chanbw_m = (radiocfg.mdmcfg4 >> 4) & 0x3
|
|
bw = 1000000.0*mhz / (8.0*(4+chanbw_m) * pow(2,chanbw_e))
|
|
#print "chanbw_e: %x chanbw_m: %x chanbw: %f hz" % (chanbw_e, chanbw_m, bw)
|
|
return bw
|
|
|
|
def setMdmDRate(self, drate, mhz=24, radiocfg=None):
|
|
'''
|
|
set the baud of data being modulated through the radio
|
|
'''
|
|
if radiocfg==None:
|
|
self.getRadioConfig()
|
|
radiocfg = self.radiocfg
|
|
|
|
drate_e = None
|
|
drate_m = None
|
|
for e in range(16):
|
|
m = int((drate * pow(2,28) / (pow(2,e)* (mhz*1000000.0))-256) + .5) # rounded evenly
|
|
if m < 256:
|
|
drate_e = e
|
|
drate_m = m
|
|
break
|
|
if drate_e is None:
|
|
raise(Exception("DRate does not translate into acceptable parameters. Should you be changing this?"))
|
|
|
|
drate = 1000000.0 * mhz * (256+drate_m) * pow(2,drate_e) / pow(2,28)
|
|
if self._debug: print "drate_e: %x drate_m: %x drate: %f Hz" % (drate_e, drate_m, drate)
|
|
|
|
radiocfg.mdmcfg3 = drate_m
|
|
radiocfg.mdmcfg4 &= ~MDMCFG4_DRATE_E
|
|
radiocfg.mdmcfg4 |= drate_e
|
|
self.setRFRegister(MDMCFG3, (radiocfg.mdmcfg3))
|
|
self.setRFRegister(MDMCFG4, (radiocfg.mdmcfg4))
|
|
|
|
def getMdmDRate(self, mhz=24, radiocfg=None):
|
|
'''
|
|
get the baud of data being modulated through the radio
|
|
'''
|
|
if radiocfg==None:
|
|
self.getRadioConfig()
|
|
radiocfg = self.radiocfg
|
|
|
|
drate_e = radiocfg.mdmcfg4 & 0xf
|
|
drate_m = radiocfg.mdmcfg3
|
|
|
|
drate = 1000000.0 * mhz * (256+drate_m) * pow(2,drate_e) / pow(2,28)
|
|
#print "drate_e: %x drate_m: %x drate: %f hz" % (drate_e, drate_m, drate)
|
|
return drate
|
|
|
|
|
|
def setMdmDeviatn(self, deviatn, mhz=24, radiocfg=None):
|
|
'''
|
|
configure the deviation settings for the given modulation scheme
|
|
'''
|
|
if radiocfg==None:
|
|
self.getRadioConfig()
|
|
radiocfg = self.radiocfg
|
|
|
|
dev_e = None
|
|
dev_m = None
|
|
for e in range(8):
|
|
m = int((deviatn * pow(2,17) / (pow(2,e)* (mhz*1000000.0))-8) + .5) # rounded evenly
|
|
if m < 8:
|
|
dev_e = e
|
|
dev_m = m
|
|
break
|
|
if dev_e is None:
|
|
raise(Exception("Deviation does not translate into acceptable parameters. Should you be changing this?"))
|
|
|
|
dev = 1000000.0 * mhz * (8+dev_m) * pow(2,dev_e) / pow(2,17)
|
|
#print "dev_e: %x dev_m: %x deviatn: %f Hz" % (e, m, dev)
|
|
|
|
radiocfg.deviatn = (dev_e << 4) | dev_m
|
|
self.setRFRegister(DEVIATN, radiocfg.deviatn)
|
|
|
|
def getMdmDeviatn(self, mhz=24, radiocfg=None):
|
|
if radiocfg==None:
|
|
self.getRadioConfig()
|
|
radiocfg = self.radiocfg
|
|
|
|
dev_e = radiocfg.deviatn >> 4
|
|
dev_m = radiocfg.deviatn & DEVIATN_DEVIATION_M
|
|
dev = 1000000.0 * mhz * (8+dev_m) * pow(2,dev_e) / pow(2,17)
|
|
return dev
|
|
|
|
|
|
def setMdmSyncWord(self, word, radiocfg=None):
|
|
if radiocfg==None:
|
|
self.getRadioConfig()
|
|
radiocfg = self.radiocfg
|
|
|
|
radiocfg.sync1 = word >> 8
|
|
radiocfg.sync0 = word & 0xff
|
|
self.setRFRegister(SYNC1, (radiocfg.sync1))
|
|
self.setRFRegister(SYNC0, (radiocfg.sync0))
|
|
|
|
def getMdmSyncMode(self, radiocfg=None):
|
|
if radiocfg==None:
|
|
self.getRadioConfig()
|
|
radiocfg = self.radiocfg
|
|
|
|
return radiocfg.mdmcfg2 & MDMCFG2_SYNC_MODE
|
|
|
|
def setMdmSyncMode(self, syncmode=SYNCM_15_of_16, radiocfg=None):
|
|
if radiocfg==None:
|
|
self.getRadioConfig()
|
|
radiocfg = self.radiocfg
|
|
|
|
radiocfg.mdmcfg2 &= ~MDMCFG2_SYNC_MODE
|
|
radiocfg.mdmcfg2 |= syncmode
|
|
self.setRFRegister(MDMCFG2, (radiocfg.mdmcfg2))
|
|
|
|
def getMdmNumPreamble(self, radiocfg=None):
|
|
'''
|
|
get the minimum number of preamble bits to be transmitted. note this is a flag, not a count
|
|
so the return value must be interpeted - e.g. 0x30 == 0x03 << 4 == MFMCFG1_NUM_PREAMBLE_6 == 6 bytes
|
|
'''
|
|
if radiocfg == None:
|
|
self.getRadioConfig()
|
|
radiocfg = self.radiocfg
|
|
|
|
preamble= (radiocfg.mdmcfg1 & MFMCFG1_NUM_PREAMBLE)
|
|
return preamble
|
|
|
|
def setMdmNumPreamble(self, preamble=MFMCFG1_NUM_PREAMBLE_4, radiocfg=None):
|
|
'''
|
|
set the minimum number of preamble bits to be transmitted (default: MFMCFG1_NUM_PREAMBLE_4)
|
|
'''
|
|
if radiocfg == None:
|
|
self.getRadioConfig()
|
|
radiocfg = self.radiocfg
|
|
|
|
radiocfg.mdmcfg1 &= ~MFMCFG1_NUM_PREAMBLE
|
|
radiocfg.mdmcfg1 |= preamble
|
|
self.setRFRegister(MDMCFG1, (radiocfg.mdmcfg1))
|
|
|
|
def getBSLimit(self, radiocfg=None):
|
|
'''
|
|
get the saturation point for the data rate offset compensation algorithm
|
|
'''
|
|
if radiocfg==None:
|
|
self.getRadioConfig()
|
|
radiocfg = self.radiocfg
|
|
|
|
return radiocfg.bscfg&BSCFG_BS_LIMIT
|
|
|
|
def setBSLimit(self, bslimit, radiocfg=None):
|
|
'''
|
|
set the saturation point for the data rate offset compensation algorithm
|
|
'''
|
|
if radiocfg==None:
|
|
self.getRadioConfig()
|
|
radiocfg = self.radiocfg
|
|
|
|
radiocfg.bscfg &= ~BSCFG_BS_LIMIT
|
|
radiocfg.bscfg |= bslimit
|
|
self.setRFRegister(BSCFG, (radiocfg.bscfg))
|
|
|
|
def calculateMdmDeviatn(self, mhz=24, radiocfg=None):
|
|
''' calculates the optimal DEVIATN setting for the current freq/baud
|
|
* totally experimental *
|
|
from Smart RF Studio:
|
|
1.2 kbaud 5.1khz dev
|
|
2.4 kbaud 5.1khz dev
|
|
38.4 kbaud 20khz dev
|
|
250 kbaud 129khz dev
|
|
'''
|
|
baud = self.getMdmDRate(mhz, radiocfg)
|
|
if baud <= 2400:
|
|
deviatn = 5100
|
|
elif baud <= 38400:
|
|
deviatn = 20000 * ((baud-2400)/36000)
|
|
else:
|
|
deviatn = 129000 * ((baud-38400)/211600)
|
|
self.setMdmDeviatn(deviatn)
|
|
|
|
def calculatePktChanBW(self, mhz=24, radiocfg=None):
|
|
''' calculates the optimal ChanBW setting for the current freq/baud
|
|
* totally experimental *
|
|
from Smart RF Studio:
|
|
1.2 kbaud BW: 63khz
|
|
2.4 kbaud BW: 63khz
|
|
38.4kbaud BW: 94khz
|
|
250 kbaud BW: 600khz
|
|
'''
|
|
freq, freqhex = self.getFreq()
|
|
center_freq = freq + 14000000
|
|
freq_uncertainty = 20e-6 * freq # +-20ppm
|
|
freq_uncertainty *= 2 # both xmitter and receiver
|
|
#minbw = (2 * freq_uncertainty) + self.getMdmDRate() # uncertainty for both sender/receiver
|
|
minbw = (self.getMdmDRate() + freq_uncertainty)
|
|
|
|
possibles = [ 53e3,63e3,75e3,93e3,107e3,125e3,150e3,188e3,214e3,250e3,300e3,375e3,428e3,500e3,600e3,750e3, ]
|
|
for bw in possibles:
|
|
#if (.8 * bw) > minbw: # can't occupy more the 80% of BW
|
|
if (bw) > minbw:
|
|
break
|
|
self.setMdmChanBW(bw, mhz, radiocfg)
|
|
|
|
def calculateFsIF(self, mhz=24, radiocfg=None):
|
|
''' calculates the optimal IF setting for the current freq/baud
|
|
* totally experimental *
|
|
1.2 kbaud IF: 140khz
|
|
2.4 kbaud IF: 140khz
|
|
38.4kbaud IF: 164khz (140khz for "sensitive" version)
|
|
250 kbaud IF: 281khz
|
|
500 kbaud IF: 328khz
|
|
'''
|
|
pass
|
|
def calculateFsOffset(self, mhz=24, radiocfg=None):
|
|
''' calculates the optimal FreqOffset setting for the current freq/baud
|
|
* totally experimental *
|
|
'''
|
|
|
|
pass
|
|
|
|
def reprModemConfig(self, mhz=24, radiocfg=None):
|
|
output = []
|
|
if radiocfg==None:
|
|
self.getRadioConfig()
|
|
radiocfg = self.radiocfg
|
|
|
|
reprMdmModulation = self.reprMdmModulation(radiocfg)
|
|
syncmode = self.getMdmSyncMode(radiocfg)
|
|
|
|
output.append(reprMdmModulation)
|
|
|
|
drate = self.getMdmDRate(mhz, radiocfg)
|
|
output.append("DRate: %f hz"%drate)
|
|
|
|
bw = self.getMdmChanBW(mhz, radiocfg)
|
|
output.append("ChanBW: %f hz"%bw)
|
|
|
|
output.append("DEVIATION: %f hz" % self.getMdmDeviatn(mhz, radiocfg))
|
|
|
|
output.append("Sync Mode: %s" % SYNCMODES[syncmode])
|
|
|
|
num_preamble = (radiocfg.mdmcfg1>>4)&7
|
|
output.append("Min TX Preamble: %d bytes" % (NUM_PREAMBLE[num_preamble]) )
|
|
|
|
chanspc = self.getMdmChanSpc(mhz, radiocfg)
|
|
output.append("Chan Spacing: %f hz" % chanspc)
|
|
|
|
bslimit = radiocfg.bscfg & BSCFG_BS_LIMIT
|
|
output.append("BSLimit: %s"%BSLIMITS[bslimit])
|
|
|
|
output.append("DC Filter: %s" % (("enabled", "disabled")[self.getEnableMdmDCFilter(radiocfg)]))
|
|
|
|
mchstr = self.getEnableMdmManchester(radiocfg)
|
|
output.append("Manchester Encoding: %s" % (("disabled","enabled")[mchstr]))
|
|
|
|
fec = self.getEnableMdmFEC(radiocfg)
|
|
output.append("Fwd Err Correct: %s" % (("disabled","enabled")[fec]))
|
|
|
|
|
|
return "\n".join(output)
|
|
|
|
def getRSSI(self):
|
|
rssi = self.peek(RSSI)
|
|
return rssi
|
|
|
|
def getLQI(self):
|
|
lqi = self.peek(LQI)
|
|
return lqi
|
|
|
|
|
|
def reprRadioTestSignalConfig(self, radiocfg=None):
|
|
if radiocfg==None:
|
|
self.getRadioConfig()
|
|
radiocfg = self.radiocfg
|
|
|
|
output = []
|
|
#output.append("GDO2_INV: %s" % ("do not Invert Output", "Invert output")[(radiocfg.iocfg2>>6)&1])
|
|
#output.append("GDO2CFG: 0x%x" % (radiocfg.iocfg2&0x3f))
|
|
#output.append("GDO_DS: %s" % (("minimum drive (>2.6vdd","Maximum drive (<2.6vdd)")[radiocfg.iocfg1>>7]))
|
|
#output.append("GDO1_INV: %s" % ("do not Invert Output", "Invert output")[(radiocfg.iocfg1>>6)&1])
|
|
#output.append("GDO1CFG: 0x%x"%(radiocfg.iocfg1&0x3f))
|
|
#output.append("GDO0_INV: %s" % ("do not Invert Output", "Invert output")[(radiocfg.iocfg0>>6)&1])
|
|
#output.append("GDO0CFG: 0x%x"%(radiocfg.iocfg0&0x3f))
|
|
output.append("TEST2: 0x%x"%radiocfg.test2)
|
|
output.append("TEST1: 0x%x"%radiocfg.test1)
|
|
output.append("TEST0: 0x%x"%(radiocfg.test0&0xfd))
|
|
output.append("VCO_SEL_CAL_EN: 0x%x"%((radiocfg.test2>>1)&1))
|
|
return "\n".join(output)
|
|
|
|
|
|
def reprFreqConfig(self, mhz=24, radiocfg=None):
|
|
if radiocfg==None:
|
|
self.getRadioConfig()
|
|
radiocfg = self.radiocfg
|
|
|
|
output = []
|
|
freq,num = self.getFreq(mhz, radiocfg)
|
|
output.append("Frequency: %f hz (%s)" % (freq,num))
|
|
|
|
output.append("Channel: %d" % radiocfg.channr)
|
|
|
|
|
|
freq_if = self.getFsIF(mhz, radiocfg)
|
|
freqoff = self.getFsOffset(mhz, radiocfg)
|
|
|
|
output.append("Intermediate freq: %d hz" % freq_if)
|
|
output.append("Frequency Offset: %d +/-" % freqoff)
|
|
|
|
return "\n".join(output)
|
|
|
|
def reprAESMode(self):
|
|
output = []
|
|
aesmode= ord(self.getAESmode()[0])
|
|
|
|
output.append("AES Mode: %s" % AESMODES[(aesmode & AES_CRYPTO_MODE)])
|
|
if aesmode & AES_CRYPTO_IN_ENABLE:
|
|
output.append("Crypt RF Input: %s" % ("Decrypt", "Encrypt")[(aesmode & AES_CRYPTO_IN_TYPE)])
|
|
else:
|
|
output.append("Crypt RF Input: off")
|
|
if aesmode & AES_CRYPTO_OUT_ENABLE:
|
|
output.append("Crypt RF Output: %s" % ("Decrypt", "Encrypt")[(aesmode & AES_CRYPTO_OUT_TYPE) >> 2])
|
|
else:
|
|
output.append("Crypt RF Output: off")
|
|
|
|
return "\n".join(output)
|
|
|
|
def reprPacketConfig(self, radiocfg=None):
|
|
if radiocfg==None:
|
|
self.getRadioConfig()
|
|
radiocfg = self.radiocfg
|
|
|
|
output = []
|
|
output.append("Sync Word: 0x%.2X%.2X" % (radiocfg.sync1, radiocfg.sync0))
|
|
output.append("Packet Length: %d" % radiocfg.pktlen)
|
|
length_config = radiocfg.pktctrl0&3
|
|
output.append("Length Config: %s" % LENGTH_CONFIGS[length_config])
|
|
|
|
output.append("Configured Address: 0x%x" % radiocfg.addr)
|
|
|
|
pqt = self.getPktPQT(radiocfg)
|
|
output.append("Preamble Quality Threshold: 4 * %d" % pqt)
|
|
|
|
append = (radiocfg.pktctrl1>>2) & 1
|
|
output.append("Append Status: %s" % ("No","Yes")[append])
|
|
|
|
adr_chk = radiocfg.pktctrl1&3
|
|
output.append("Rcvd Packet Check: %s" % ADR_CHK_TYPES[adr_chk])
|
|
|
|
whitedata = self.getEnablePktDataWhitening(radiocfg)
|
|
output.append("Data Whitening: %s" % ("off", "ON (but only with cc2400_en==0)")[whitedata])
|
|
|
|
pkt_format = (radiocfg.pktctrl0>>5)&3
|
|
output.append("Packet Format: %s" % PKT_FORMATS[pkt_format])
|
|
|
|
crc = self.getEnablePktCRC(radiocfg)
|
|
output.append("CRC: %s" % ("disabled", "ENABLED")[crc])
|
|
|
|
return "\n".join(output)
|
|
"""
|
|
SYNC1 = 0xa9;
|
|
SYNC0 = 0x47;
|
|
PKTLEN = 0xff;
|
|
PKTCTRL1 = 0x04; // APPEND_STATUS
|
|
PKTCTRL0 = 0x01; // VARIABLE LENGTH, no crc, no whitening
|
|
ADDR = 0x00;
|
|
CHANNR = 0x00;
|
|
FSCTRL1 = 0x0c; // IF
|
|
FSCTRL0 = 0x00;
|
|
FREQ2 = 0x25;
|
|
FREQ1 = 0x95;
|
|
FREQ0 = 0x55;
|
|
MDMCFG4 = 0x1d; // chan_bw and drate_e
|
|
MDMCFG3 = 0x55; // drate_m
|
|
MDMCFG2 = 0x13; // gfsk, 30/32+carrier sense sync
|
|
MDMCFG1 = 0x23; // 4-preamble-bytes, chanspc_e
|
|
MDMCFG0 = 0x11; // chanspc_m
|
|
DEVIATN = 0x63;
|
|
MCSM2 = 0x07; // RX_TIMEOUT
|
|
MCSM1 = 0x30; // CCA_MODE RSSI below threshold unless currently recvg pkt
|
|
MCSM0 = 0x18; // fsautosync when going from idle to rx/tx/fstxon
|
|
FOCCFG = 0x1d;
|
|
BSCFG = 0x1c; // bit sync config
|
|
AGCCTRL2 = 0xc7;
|
|
AGCCTRL1 = 0x00;
|
|
AGCCTRL0 = 0xb0;
|
|
FREND1 = 0xb6;
|
|
FREND0 = 0x10;
|
|
FSCAL3 = 0xea;
|
|
FSCAL2 = 0x2a;
|
|
FSCAL1 = 0x00;
|
|
FSCAL0 = 0x1f;
|
|
TEST2 = 0x88;
|
|
TEST1 = 0x31;
|
|
TEST0 = 0x09;
|
|
PA_TABLE0 = 0x83;
|
|
"""
|
|
|
|
def reprRadioState(self, radiocfg=None):
|
|
output = []
|
|
try:
|
|
if radiocfg==None:
|
|
self.getRadioConfig()
|
|
radiocfg = self.radiocfg
|
|
|
|
output.append(" MARCSTATE: %s (%x)" % (self.getMARCSTATE(radiocfg)))
|
|
output.append(" DONGLE RESPONDING: mode :%x, last error# %d"%(self.getDebugCodes()))
|
|
except:
|
|
output.append(repr(sys.exc_info()))
|
|
output.append(" DONGLE *not* RESPONDING")
|
|
|
|
return "\n".join(output)
|
|
|
|
def reprClientState(self, width=120):
|
|
output = ["="*width]
|
|
output.append(' client thread cycles: %d' % self.threadcounter)
|
|
output.append(' client errored cycles: %d' % self._usberrorcnt)
|
|
output.append(' recv_queue: (%d bytes) %s'%(len(self.recv_queue),repr(self.recv_queue)[:width-42]))
|
|
output.append(' trash: (%d blobs) "%s"'%(len(self.trash),repr(self.trash)[:width-44]))
|
|
output.append(' recv_mbox (%d keys) "%s"'%(len(self.recv_mbox),repr([hex(x) for x in self.recv_mbox.keys()])[:width-44]))
|
|
for app in self.recv_mbox.keys():
|
|
appbox = self.recv_mbox[app]
|
|
output.append(' app 0x%x (%d records)'%(app,len(appbox)))
|
|
for cmd in appbox.keys():
|
|
output.append(' [0x%x] (%d frames) "%s"'%(cmd, len(appbox[cmd]), repr(appbox[cmd])[:width-36]))
|
|
output.append('')
|
|
return "\n".join(output)
|
|
|
|
|
|
######## APPLICATION METHODS ########
|
|
def setup900MHz(self):
|
|
self.getRadioConfig()
|
|
rc = self.radiocfg
|
|
rc.iocfg0 = 0x06
|
|
rc.sync1 = 0x0b
|
|
rc.sync0 = 0x0b
|
|
rc.pktlen = 0xff
|
|
rc.pktctrl1 = 0xe5
|
|
rc.pktctrl0 = 0x04
|
|
rc.fsctrl1 = 0x12
|
|
rc.fsctrl0 = 0x00
|
|
rc.addr = 0x00
|
|
rc.channr = 0x00
|
|
rc.mdmcfg4 = 0x3e
|
|
rc.mdmcfg3 = 0x55
|
|
rc.mdmcfg2 = 0x73
|
|
rc.mdmcfg1 = 0x23
|
|
rc.mdmcfg0 = 0x55
|
|
rc.mcsm2 = 0x07
|
|
rc.mcsm1 = 0x30
|
|
rc.mcsm0 = 0x00
|
|
rc.deviatn = 0x16
|
|
rc.foccfg = 0x17
|
|
rc.bscfg = 0x6c
|
|
rc.agcctrl2 |= AGCCTRL2_MAX_DVGA_GAIN
|
|
rc.agcctrl2 = 0x03
|
|
rc.agcctrl1 = 0x40
|
|
rc.agcctrl0 = 0x91
|
|
rc.frend1 = 0x56
|
|
rc.frend0 = 0x10
|
|
rc.fscal3 = 0xEA
|
|
rc.fscal2 = 0x2A
|
|
rc.fscal1 = 0x00
|
|
rc.fscal0 = 0x1F
|
|
rc.test2 = 0x88
|
|
rc.test1 = 0x31
|
|
rc.test0 = 0x09
|
|
rc.pa_table0 = 0xc0
|
|
self.setRadioConfig()
|
|
|
|
def setup900MHzHopTrans(self):
|
|
self.getRadioConfig()
|
|
rc = self.radiocfg
|
|
rc.iocfg0 = 0x06
|
|
rc.sync1 = 0x0b
|
|
rc.sync0 = 0x0b
|
|
rc.pktlen = 0xff
|
|
rc.pktctrl1 = 0x04
|
|
rc.pktctrl0 = 0x05
|
|
rc.addr = 0x00
|
|
rc.channr = 0x00
|
|
rc.fsctrl1 = 0x06
|
|
rc.fsctrl0 = 0x00
|
|
rc.mdmcfg4 = 0xee
|
|
rc.mdmcfg3 = 0x55
|
|
rc.mdmcfg2 = 0x73
|
|
rc.mdmcfg1 = 0x23
|
|
rc.mdmcfg0 = 0x55
|
|
rc.mcsm2 = 0x07
|
|
rc.mcsm1 = 0x30
|
|
rc.mcsm0 = 0x18
|
|
rc.deviatn = 0x16
|
|
rc.foccfg = 0x17
|
|
rc.bscfg = 0x6c
|
|
rc.agcctrl2 = 0x03
|
|
rc.agcctrl1 = 0x40
|
|
rc.agcctrl0 = 0x91
|
|
rc.frend1 = 0x56
|
|
rc.frend0 = 0x10
|
|
rc.fscal3 = 0xEA
|
|
rc.fscal2 = 0x2A
|
|
rc.fscal1 = 0x00
|
|
rc.fscal0 = 0x1F
|
|
rc.test2 = 0x88
|
|
rc.test1 = 0x31
|
|
rc.test0 = 0x09
|
|
rc.pa_table0 = 0xc0
|
|
self.setRadioConfig()
|
|
|
|
def setup900MHzContTrans(self):
|
|
self.getRadioConfig()
|
|
rc = self.radiocfg
|
|
rc.iocfg0 = 0x06
|
|
rc.sync1 = 0x0b
|
|
rc.sync0 = 0x0b
|
|
rc.pktlen = 0xff
|
|
rc.pktctrl1 = 0x04
|
|
rc.pktctrl0 = 0x05
|
|
rc.addr = 0x00
|
|
rc.channr = 0x00
|
|
rc.fsctrl1 = 0x06
|
|
rc.fsctrl0 = 0x00
|
|
rc.freq2 = 0x26
|
|
rc.freq1 = 0x55
|
|
rc.freq0 = 0x55
|
|
rc.mdmcfg4 = 0xee
|
|
rc.mdmcfg3 = 0x55
|
|
rc.mdmcfg2 = 0x73
|
|
rc.mdmcfg1 = 0x23
|
|
rc.mdmcfg0 = 0x55
|
|
rc.mcsm2 = 0x07
|
|
rc.mcsm1 = 0x30
|
|
rc.mcsm0 = 0x18
|
|
rc.deviatn = 0x16
|
|
rc.foccfg = 0x17
|
|
rc.bscfg = 0x6c
|
|
rc.agcctrl2 = 0x03
|
|
rc.agcctrl1 = 0x40
|
|
rc.agcctrl0 = 0x91
|
|
rc.frend1 = 0x56
|
|
rc.frend0 = 0x10
|
|
rc.fscal3 = 0xEA
|
|
rc.fscal2 = 0x2A
|
|
rc.fscal1 = 0x00
|
|
rc.fscal0 = 0x1F
|
|
rc.test2 = 0x88
|
|
rc.test1 = 0x31
|
|
rc.test0 = 0x09
|
|
rc.pa_table0 = 0xc0
|
|
self.setRadioConfig()
|
|
|
|
def setup_rfstudio_902PktTx(self):
|
|
self.getRadioConfig()
|
|
rc = self.radiocfg
|
|
rc.iocfg2 = 0x00
|
|
rc.iocfg1 = 0x00
|
|
rc.iocfg0 = 0x06
|
|
rc.sync1 = 0x0b
|
|
rc.sync0 = 0x0b
|
|
rc.pktlen = 0xff
|
|
rc.pktctrl1 = 0x04
|
|
rc.pktctrl0 = 0x05
|
|
rc.addr = 0x00
|
|
rc.channr = 0x00
|
|
rc.fsctrl1 = 0x0c
|
|
rc.fsctrl0 = 0x00
|
|
rc.freq2 = 0x25
|
|
rc.freq1 = 0x95
|
|
rc.freq0 = 0x55
|
|
rc.mdmcfg4 = 0x1d
|
|
rc.mdmcfg3 = 0x55
|
|
rc.mdmcfg2 = 0x13
|
|
rc.mdmcfg1 = 0x23
|
|
rc.mdmcfg0 = 0x11
|
|
rc.mcsm2 = 0x07
|
|
rc.mcsm1 = 0x30
|
|
rc.mcsm0 = 0x18
|
|
rc.deviatn = 0x63
|
|
rc.foccfg = 0x1d
|
|
rc.bscfg = 0x1c
|
|
rc.agcctrl2 = 0xc7
|
|
rc.agcctrl1 = 0x00
|
|
rc.agcctrl0 = 0xb0
|
|
rc.frend1 = 0xb6
|
|
rc.frend0 = 0x10
|
|
rc.fscal3 = 0xEA
|
|
rc.fscal2 = 0x2A
|
|
rc.fscal1 = 0x00
|
|
rc.fscal0 = 0x1F
|
|
rc.test2 = 0x88
|
|
rc.test1 = 0x31
|
|
rc.test0 = 0x09
|
|
rc.pa_table7 = 0x00
|
|
rc.pa_table6 = 0x00
|
|
rc.pa_table5 = 0x00
|
|
rc.pa_table4 = 0x00
|
|
rc.pa_table3 = 0x00
|
|
rc.pa_table2 = 0x00
|
|
rc.pa_table1 = 0x00
|
|
#rc.pa_table0 = 0x8e
|
|
rc.pa_table0 = 0xc0
|
|
self.setRadioConfig()
|
|
|
|
def lowball(self, level=1, sync=0xaaaa, length=250, pqt=0, crc=False, fec=False, datawhite=False):
|
|
'''
|
|
this configures the radio to the lowest possible level of filtering, potentially allowing complete radio noise to come through as data. very useful in some circumstances.
|
|
level == 0 changes the Sync Mode to SYNCM_NONE (wayyy more garbage)
|
|
level == 1 (default) sets the Sync Mode to SYNCM_CARRIER (requires a valid carrier detection for the data to be considered a packet)
|
|
level == 2 sets the Sync Mode to SYNCM_CARRIER_15_of_16 (requires a valid carrier detection and 15 of 16 bits of SYNC WORD match for the data to be considered a packet)
|
|
level == 3 sets the Sync Mode to SYNCM_CARRIER_16_of_16 (requires a valid carrier detection and 16 of 16 bits of SYNC WORD match for the data to be considered a packet)
|
|
'''
|
|
if hasattr(self, '_last_radiocfg') and len(self._last_radiocfg):
|
|
print('not saving radio state. already have one saved. use lowballRestore() to restore the saved config and the next time you run lowball() the radio config will be saved.')
|
|
else:
|
|
self._last_radiocfg = self.getRadioConfig()
|
|
|
|
self.makePktFLEN(length)
|
|
self.setEnablePktCRC(crc)
|
|
self.setEnableMdmFEC(fec)
|
|
self.setEnablePktDataWhitening(datawhite)
|
|
self.setMdmSyncWord(sync)
|
|
self.setPktPQT(pqt)
|
|
|
|
if (level == 3):
|
|
self.setMdmSyncMode(SYNCM_CARRIER_16_of_16)
|
|
elif (level == 2):
|
|
self.setMdmSyncMode(SYNCM_15_of_16)
|
|
elif (level == 1):
|
|
self.setMdmSyncMode(SYNCM_CARRIER)
|
|
else:
|
|
self.setMdmSyncMode(SYNCM_NONE)
|
|
|
|
|
|
def lowballRestore(self):
|
|
if not hasattr(self, '_last_radiocfg'):
|
|
raise(Exception("lowballRestore requires that lowball have been executed first (it saves radio config state!)"))
|
|
self.setRadioConfig(self._last_radiocfg)
|
|
self._last_radiocfg = ''
|
|
|
|
|
|
def checkRepr(self, matchstr, checkval, maxdiff=0):
|
|
starry = self.reprRadioConfig().split('\n')
|
|
line,val = getValueFromReprString(starry, matchstr)
|
|
try:
|
|
f = checkval.__class__(val.split(" ")[0])
|
|
if abs(f-checkval) <= maxdiff:
|
|
print " passed: reprRadioConfig test: %s %s" % (repr(val), checkval)
|
|
else:
|
|
print " *FAILED* reprRadioConfig test: %s %s %s" % (repr(line), repr(val), checkval)
|
|
|
|
except ValueError, e:
|
|
print " ERROR checking repr: %s" % e
|
|
|
|
|
|
def unittest(self, mhz=24):
|
|
print "\nTesting USB ping()"
|
|
self.ping(3)
|
|
|
|
print "\nTesting USB ep0Ping()"
|
|
self.ep0Ping()
|
|
|
|
print "\nTesting USB enumeration"
|
|
print "getString(0,100): %s" % repr(self._do.getString(0,100))
|
|
|
|
print "\nTesting USB EP MAX_PACKET_SIZE handling (ep0Peek(0xf000, 100))"
|
|
print repr(self.ep0Peek(0xf000, 100))
|
|
|
|
print "\nTesting USB EP MAX_PACKET_SIZE handling (peek(0xf000, 300))"
|
|
print repr(self.peek(0xf000, 400))
|
|
|
|
print "\nTesting USB poke/peek"
|
|
data = "".join([chr(c) for c in xrange(120)])
|
|
where = 0xf300
|
|
self.poke(where, data)
|
|
ndata = self.peek(where, len(data))
|
|
if ndata != data:
|
|
print " *FAILED*\n '%s'\n '%s'" % (data.encode("hex"), ndata.encode("hex"))
|
|
raise(Exception(" *FAILED*\n '%s'\n '%s'" % (data.encode("hex"), ndata.encode("hex"))))
|
|
else:
|
|
print " passed '%s'" % (ndata.encode("hex"))
|
|
|
|
print "\nTesting getValueFromReprString()"
|
|
starry = self.reprRadioConfig().split('\n')
|
|
print repr(getValueFromReprString(starry, 'hz'))
|
|
|
|
print "\nTesting reprRadioConfig()"
|
|
print self.reprRadioConfig()
|
|
|
|
print "\nTesting Frequency Get/Setters"
|
|
# FREQ
|
|
freq0,freq0str = self.getFreq()
|
|
|
|
testfreq = 902000000
|
|
self.setFreq(testfreq)
|
|
freq,freqstr = self.getFreq()
|
|
if abs(testfreq - freq) < 1024:
|
|
print " passed: %d : %f (diff: %f)" % (testfreq, freq, testfreq-freq)
|
|
else:
|
|
print " *FAILED* %d : %f (diff: %f)" % (testfreq, freq, testfreq-freq)
|
|
|
|
testfreq = 868000000
|
|
self.setFreq(testfreq)
|
|
freq,freqstr = self.getFreq()
|
|
if abs(testfreq - freq) < 1024:
|
|
print " passed: %d : %f (diff: %f)" % (testfreq, freq, testfreq-freq)
|
|
else:
|
|
print " *FAILED* %d : %f (diff: %f)" % (testfreq, freq, testfreq-freq)
|
|
|
|
testfreq = 433000000
|
|
self.setFreq(testfreq)
|
|
freq,freqstr = self.getFreq()
|
|
if abs(testfreq - freq) < 1024:
|
|
print " passed: %d : %f (diff: %f)" % (testfreq, freq, testfreq-freq)
|
|
else:
|
|
print " *FAILED* %d : %f (diff: %f)" % (testfreq, freq, testfreq-freq)
|
|
|
|
self.checkRepr("Frequency:", float(testfreq), 1024)
|
|
self.setFreq(freq0)
|
|
|
|
# CHANNR
|
|
channr0 = self.getChannel()
|
|
for x in range(15):
|
|
self.setChannel(x)
|
|
channr = self.getChannel()
|
|
if channr != x:
|
|
print " *FAILED* get/setChannel(): %d : %d" % (x, channr)
|
|
else:
|
|
print " passed: get/setChannel(): %d : %d" % (x, channr)
|
|
self.checkRepr("Channel:", channr)
|
|
self.setChannel(channr0)
|
|
|
|
# IF and FREQ_OFF
|
|
freq_if = self.getFsIF()
|
|
freqoff = self.getFsOffset()
|
|
for fif, foff in ((164062,1),(140625,2),(187500,3)):
|
|
self.setFsIF(fif)
|
|
self.setFsOffset(foff)
|
|
nfif = self.getFsIF()
|
|
nfoff = self.getFsOffset()
|
|
if abs(nfif - fif) > 5:
|
|
print " *FAILED* get/setFsIFandOffset(): %d : %f (diff: %f)" % (fif,nfif,nfif-fif)
|
|
else:
|
|
print " passed: get/setFsIFandOffset(): %d : %f (diff: %f)" % (fif,nfif,nfif-fif)
|
|
|
|
if foff != nfoff:
|
|
print " *FAILED* get/setFsIFandOffset(): %d : %d (diff: %d)" % (foff,nfoff,nfoff-foff)
|
|
else:
|
|
print " passed: get/setFsIFandOffset(): %d : %d (diff: %d)" % (foff,nfoff,nfoff-foff)
|
|
self.checkRepr("Intermediate freq:", fif, 11720)
|
|
self.checkRepr("Frequency Offset:", foff)
|
|
|
|
self.setFsIF(freq_if)
|
|
self.setFsOffset(freqoff)
|
|
|
|
### continuing with more simple tests. add completeness later?
|
|
# Modem tests
|
|
|
|
mod = self.getMdmModulation(self.radiocfg)
|
|
self.setMdmModulation(mod, self.radiocfg)
|
|
modcheck = self.getMdmModulation(self.radiocfg)
|
|
if mod != modcheck:
|
|
print " *FAILED* get/setMdmModulation(): %d : %d " % (mod, modcheck)
|
|
else:
|
|
print " passed: get/setMdmModulation(): %d : %d " % (mod, modcheck)
|
|
|
|
chanspc = self.getMdmChanSpc(mhz, self.radiocfg)
|
|
self.setMdmChanSpc(chanspc, mhz, self.radiocfg)
|
|
chanspc_check = self.getMdmChanSpc(mhz, self.radiocfg)
|
|
if chanspc != chanspc_check:
|
|
print " *FAILED* get/setMdmChanSpc(): %d : %d" % (chanspc, chanspc_check)
|
|
else:
|
|
print " passed: get/setMdmChanSpc(): %d : %d" % (chanspc, chanspc_check)
|
|
|
|
chanbw = self.getMdmChanBW(mhz, self.radiocfg)
|
|
self.setMdmChanBW(chanbw, mhz, self.radiocfg)
|
|
chanbw_check = self.getMdmChanBW(mhz, self.radiocfg)
|
|
if chanbw != chanbw_check:
|
|
print " *FAILED* get/setMdmChanBW(): %d : %d" % (chanbw, chanbw_check)
|
|
else:
|
|
print " passed: get/setMdmChanBW(): %d : %d" % (chanbw, chanbw_check)
|
|
|
|
drate = self.getMdmDRate(mhz, self.radiocfg)
|
|
self.setMdmDRate(drate, mhz, self.radiocfg)
|
|
drate_check = self.getMdmDRate(mhz, self.radiocfg)
|
|
if drate != drate_check:
|
|
print " *FAILED* get/setMdmDRate(): %d : %d" % (drate, drate_check)
|
|
else:
|
|
print " passed: get/setMdmDRate(): %d : %d" % (drate, drate_check)
|
|
|
|
deviatn = self.getMdmDeviatn(mhz, self.radiocfg)
|
|
self.setMdmDeviatn(deviatn, mhz, self.radiocfg)
|
|
deviatn_check = self.getMdmDeviatn(mhz, self.radiocfg)
|
|
if deviatn != deviatn_check:
|
|
print " *FAILED* get/setMdmdeviatn(): %d : %d" % (deviatn, deviatn_check)
|
|
else:
|
|
print " passed: get/setMdmdeviatn(): %d : %d" % (deviatn, deviatn_check)
|
|
|
|
syncm = self.getMdmSyncMode(self.radiocfg)
|
|
self.setMdmSyncMode(syncm, self.radiocfg)
|
|
syncm_check = self.getMdmSyncMode(self.radiocfg)
|
|
if syncm != syncm_check:
|
|
print " *FAILED* get/setMdmSyncMode(): %d : %d" % (syncm, syncm_check)
|
|
else:
|
|
print " passed: get/setMdmSyncMode(): %d : %d" % (syncm, syncm_check)
|
|
|
|
mchstr = self.getEnableMdmManchester(self.radiocfg)
|
|
self.setEnableMdmManchester(mchstr, self.radiocfg)
|
|
mchstr_check = self.getEnableMdmManchester(self.radiocfg)
|
|
if mchstr != mchstr_check:
|
|
print " *FAILED* get/setMdmManchester(): %d : %d" % (mchstr, mchstr_check)
|
|
else:
|
|
print " passed: get/setMdmManchester(): %d : %d" % (mchstr, mchstr_check)
|
|
|
|
fec = self.getEnableMdmFEC(self.radiocfg)
|
|
self.setEnableMdmFEC(fec, self.radiocfg)
|
|
fec_check = self.getEnableMdmFEC(self.radiocfg)
|
|
if fec != fec_check:
|
|
print " *FAILED* get/setEnableMdmFEC(): %d : %d" % (fec, fec_check)
|
|
else:
|
|
print " passed: get/setEnableMdmFEC(): %d : %d" % (fec, fec_check)
|
|
|
|
dcf = self.getEnableMdmDCFilter(self.radiocfg)
|
|
self.setEnableMdmDCFilter(dcf, self.radiocfg)
|
|
dcf_check = self.getEnableMdmDCFilter(self.radiocfg)
|
|
if dcf != dcf_check:
|
|
print " *FAILED* get/setEnableMdmDCFilter(): %d : %d" % (dcf, dcf_check)
|
|
else:
|
|
print " passed: get/setEnableMdmDCFilter(): %d : %d" % (dcf, dcf_check)
|
|
|
|
|
|
# Pkt tests
|
|
pqt = self.getPktPQT(self.radiocfg)
|
|
self.setPktPQT(pqt, self.radiocfg)
|
|
pqt_check = self.getPktPQT(self.radiocfg)
|
|
if pqt != pqt_check:
|
|
print " *FAILED* get/setEnableMdmFEC(): %d : %d" % (pqt, pqt_check)
|
|
else:
|
|
print " passed: get/setEnableMdmFEC(): %d : %d" % (pqt, pqt_check)
|
|
|
|
def getValueFromReprString(stringarray, line_text):
|
|
for string in stringarray:
|
|
if line_text in string:
|
|
idx = string.find(":")
|
|
val = string[idx+1:].strip()
|
|
return (string,val)
|
|
|
|
def mkFreq(freq=902000000, mhz=24):
|
|
freqmult = (0x10000 / 1000000.0) / mhz
|
|
num = int(freq * freqmult)
|
|
freq2 = num >> 16
|
|
freq1 = (num>>8) & 0xff
|
|
freq0 = num & 0xff
|
|
return (num, freq2,freq1,freq0)
|
|
|
|
|
|
|
|
if __name__ == "__main__":
|
|
idx = 0
|
|
if len(sys.argv) > 1:
|
|
idx = int(sys.argv.pop())
|
|
d = USBDongle(idx=idx)
|
|
|