- Main Page
- Related Pages
- Modules
- Data Structures
- Files

# ieee802154_io.py

Go to the documentation of this file.

    00001 #   Copyright (c) 2008 Axel Wachtler
    00002 #   All rights reserved.
    00003 #
    00004 #   Redistribution and use in source and binary forms, with or without
    00005 #   modification, are permitted provided that the following conditions
    00006 #   are met:
    00007 #
    00008 #   * Redistributions of source code must retain the above copyright
    00009 #     notice, this list of conditions and the following disclaimer.
    00010 #   * Redistributions in binary form must reproduce the above copyright
    00011 #     notice, this list of conditions and the following disclaimer in the
    00012 #     documentation and/or other materials provided with the distribution.
    00013 #   * Neither the name of the authors nor the names of its contributors
    00014 #     may be used to endorse or promote products derived from this software
    00015 #     without specific prior written permission.
    00016 #
    00017 #   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
    00018 #   AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
    00019 #   IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
    00020 #   ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
    00021 #   LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
    00022 #   CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
    00023 #   SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
    00024 #   INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
    00025 #   CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
    00026 #   ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
    00027 #   POSSIBILITY OF SUCH DAMAGE.
    00028
    00029 # $Id: ieee802154_io.py,v 1.16 2009/09/04 19:04:09 awachtler Exp $
    00030 ##
    00031 # @file
    00032 # @ingroup grpContribCapture
    00033 # @brief File/Serial port io related classes.
    00034 #
    00035
    00036 # === import ==================================================================
    00037 import sys, serial, time, threading, Queue, struct, os
    00038 from ieee802154_base import PcapBase, UtilBase
    00039
    00040 # === globals =================================================================
    00041
    00042 # === functions ===============================================================
    00043
    00044 # === classes =================================================================
    00045 ##
    00046 # @brief Capture file reader.
    00047 #
    00048 # This class reads packets from a capture file,
    00049 # that was previously stored with tshark/wireshark.
    00050 #
    00051 # @todo This class reads the entire file (@ref open()) at once.
    00052 # So it would be a good idea, to read the file block by block.
    00053 #
    00054 class FileIn(PcapBase, UtilBase):
    00055
    00056     ## Open and read a capture file.
    00057     # @param fname name of the file
    00058     def open(self, fname):
    00059         self.fname = fname
    00060         self.fh = open(fname,'r')
    00061         self.FCNT = 0
    00062         d = self.fh.read()
    00063         self.HEADER,self.frames = self.pcap_parse_data(d)
    00064         print "nb frames", len(self.frames)
    00065         self.frameidx = 0
    00066
    00067     ## close the file handle
    00068     def close(self):
    00069         self.fh.close()
    00070
    00071     ## return file header
    00072     def pcap_get_header(self):
    00073         return self.HEADER
    00074
    00075     def info(self):
    00076         afn = os.path.abspath(self.fh.name)
    00077         ret = {'type'   : 'file',
    00078                'file'   : os.path.basename(afn),
    00079                'dir'    : os.path.dirname(afn),
    00080                'frames' : self.FCNT,
    00081                 }
    00082         return ret
    00083
    00084     ##
    00085     # Setting of a channel is is unsupported for a file
    00086     def set_channel(self, channel):
    00087         self.error("can not set channel on a file")
    00088
    00089     ## read a frame f
    00090     def read_packet(self):
    00091         try:
    00092             #print "read frame %d" % self.frameidx
    00093             ret = self.frames[self.frameidx]
    00094             self.frameidx += 1
    00095             self.FCNT += 1
    00096             print map(hex,map(ord,ret))
    00097         except IndexError:
    00098             ret = None
    00099         except:
    00100             import traceback
    00101             traceback.print_exc()
    00102             ret = None
    00103         return ret
    00104
    00105
    00106 # special handling for windows
    00107 RxErrors = [Exception]
    00108 try:
    00109     import pywintypes
    00110     RxErrors.append(pywintypes.error)
    00111 except:
    00112     pass
    00113 RxErrors = tuple(RxErrors)
    00114
    00115
    00116 ##
    00117 # @brief Serial Interface Reader.
    00118 #
    00119 class PortIn(PcapBase, UtilBase):
    00120     TMO = 1
    00121     FCNT = 0
    00122     UNSYNC = 0
    00123     SYNCED = 1
    00124     IDXERR = 0
    00125     BAUDRATE = 38400
    00126     def __init__(self):
    00127         self.RxThread = None
    00128         self.RxQueue = Queue.Queue()
    00129         self.TextQueue = Queue.Queue()
    00130         self.channel = None
    00131         self.crate = None
    00132         self.clist = []
    00133         self.state = self.UNSYNC
    00134         self.maxpackets = -1
    00135     ## @brief Open the serial port.
    00136     def open(self, fname):
    00137         if self.RxThread:
    00138             self.RxThread.join()
    00139         self.fname = fname
    00140         self.sport = serial.Serial(fname, self.BAUDRATE, timeout=10)
    00141         self.sport.open()
    00142         self.RxThread=threading.Thread(target = self.__rx__)
    00143         self.RxThread.setDaemon(1)
    00144         self.RxThread.start()
    00145         self.init()
    00146
    00147     def reset(self):
    00148         self.sport.write("\n\nidle\n")
    00149
    00150     def init(self):
    00151         self.sport.write("\n\nidle\n")
    00152         self.update_hw_status()
    00153         t = int(time.time())
    00154         self.sport.write("timeset %s\n" % t)
    00155         self.timebase = t
    00156         self.sport.write("sniff\n")
    00157
    00158     def update_hw_status(self):
    00159         self.sport.write("\n\nidle\n")
    00160         self.sport.write("parms\n")
    00161         time.sleep(.5)
    00162         for l in self.get_text_queue():
    00163             if l.find("PLATFORM")>=0:
    00164                 self.platform = l.split(":")[1].strip()
    00165             if l.find("SUPP_CMSK")>=0:
    00166                 self.clist = self.get_channels(eval(l.split(":")[1]))
    00167             if l.find("CURR_CHAN")>=0:
    00168                 self.channel = eval(l.split(":")[1])
    00169             if l.find("TIMER_SCALE")>=0:
    00170                 self.tscale = eval(l.split(":")[1])
    00171             if l.find("TICK_NUMBER")>=0:
    00172                 self.ticknb = eval(l.split(":")[1])
    00173             if l.find("CURR_RATE")>=0:
    00174                 self.crate = l.split(":")[1].strip()
    00175             if l.find("SUPP_RATES")>=0:
    00176                 self.rates = l.split(":")[1].strip()
    00177
    00178
    00179     def get_text_queue(self):
    00180         ret = ""
    00181         while not self.TextQueue.empty():
    00182             ret += self.TextQueue.get()
    00183         ret = ret.replace('\x0b',"")
    00184         return ret.split("\n")
    00185
    00186     def get_channels(self,cmask):
    00187         ret = []
    00188         cidx = 0
    00189         while cmask != 0:
    00190             if cmask & 1:
    00191                 ret.append(cidx)
    00192             cidx += 1
    00193             cmask /=2
    00194         return ret
    00195
    00196     def close(self):
    00197         if self.RxThread != None:
    00198             self.RxThread.join(self.TMO)
    00199             self.RxThread = None
    00200         self.sport.close()
    00201
    00202     def info(self):
    00203         self.update_hw_status()
    00204         ret = {'type'   : 'port',
    00205                'chan'   : self.channel,
    00206                'port'   : self.sport.port,
    00207                'clist'  : self.clist,
    00208                'tscale' : self.tscale,
    00209                'ticknb' : self.ticknb,
    00210                'crate'  : self.crate,
    00211                'rates'  : self.rates,
    00212                'platform' : self.platform,
    00213                 }
    00214         self.sport.write("sniff\n")
    00215         return ret
    00216
    00217     def __rx__(self):
    00218         frm = ""
    00219         while 1:
    00220             try:
    00221                 sdata = self.sport.read(1)
    00222                 n = self.sport.inWaiting()
    00223                 if n:
    00224                     sdata += self.sport.read(n)
    00225             except RxErrors:
    00226                 break
    00227             except:
    00228                 traceback.print_exc()
    00229                 break
    00230             frm += sdata
    00231             if self.state == self.UNSYNC:
    00232                 self.TextQueue.put(sdata)
    00233                 p = self.sync_search(frm)
    00234                 if type(p) != None:
    00235                     frm = frm[p:]
    00236                     self.state = self.SYNCED
    00237             if self.state == self.SYNCED:
    00238                 self.state,frm = self.packetizer(frm)
    00239                 self.message(2,"state sync after packetizer(), state=%d, len_frm=%d",self.state, len(frm))
    00240
    00241     def sync_search(self,frm):
    00242         ret = None
    00243         p = 0
    00244         nbstartpos = frm.count('\x01')
    00245         dlen = len(frm)
    00246         if nbstartpos:
    00247             self.message(2,"syncsearch : dlen=%d, nbstarts=%d" ,dlen,nbstartpos)
    00248             for idx in range(nbstartpos):
    00249                 try:
    00250                     p += frm[p:].index('\x01')
    00251                     plen = ord(frm[p+1])
    00252                     pe = p + plen + 2
    00253                     self.message(2,"syncing : idx=%d, packet=%d:%d, plen=%d dlen=%d", idx,p,pe,plen,dlen)
    00254
    00255                     if pe <= dlen:
    00256                         self.message(2,"packet : %s " , str(map(lambda i,f=frm: hex(ord(f[i])), (p,p+1,pe-1,pe) )))
    00257                         if(frm[pe] == '\x04'):
    00258                             ret = p
    00259                             self.message(1,"synced : idx=%d, packet=%d:%d, plen=%d dlen=%d", idx,p,pe,plen,dlen)
    00260                             raise Exception, "Synced"
    00261                     p += 1
    00262                 except IndexError:
    00263                     # this catches the blind access in line "l = ord(frm[p+1])"
    00264                     break
    00265                 except Exception:
    00266                     break
    00267                 except:
    00268                     self.exc_handler("sync_search")
    00269                     break
    00270         return ret
    00271
    00272     def packetizer(self,frm):
    00273         state = self.SYNCED
    00274         while 1:
    00275             frmlen = len(frm)
    00276             if len(frm) < 3:
    00277                 # incomplete data
    00278                 break
    00279             if frm[0] != '\x01':
    00280                 state = self.UNSYNC
    00281                 break
    00282             pktlen = ord(frm[1])
    00283             if (pktlen+3) > frmlen:
    00284                 # incomplete data
    00285                 break
    00286             if frm[pktlen+2] != '\x04':
    00287                 state = self.UNSYNC
    00288                 break
    00289             packet,frm = frm[:pktlen+3],frm[pktlen+3:]
    00290
    00291             ## XXX refactor this hack
    00292             # convert frame from serial line to pcap format
    00293             # u8   length
    00294             # u64  ts
    00295             # u8[] frm
    00296             #
    00297             # packet data is prefixed with byte STX (1) + length (1)
    00298             # and suffixed with EOT char (4)
    00299             fl = pktlen - 8
    00300             ticks,pkt = packet[2:10], packet[10:-1]
    00301             ticks = struct.unpack('LL',ticks)
    00302             tstamp = ((ticks[0]*self.ticknb) + ticks[1]) * self.tscale
    00303             t_sec = int(tstamp)
    00304             t_usec = int((tstamp-t_sec)*1.0e6)
    00305             ts = struct.pack('LL',(t_sec+self.timebase),t_usec)
    00306             lenfield = struct.pack("LL",fl,fl)
    00307             packet = ts+lenfield+pkt
    00308
    00309             if self.FCNT < self.maxpackets or self.maxpackets == 0:
    00310                 self.RxQueue.put(packet)
    00311                 self.FCNT += 1
    00312                 self.message(1,"Found Packet l=%d qsize: %d:\npkt: %s",
    00313                         pktlen, self.RxQueue.qsize(),
    00314                         " ".join(map(lambda s: '%02x' %s,map(ord,packet))))
    00315             else:
    00316                 self.message(1,"Discard Packet l=%d qsize: %d", pktlen,self.RxQueue.qsize())
    00317         return state,frm
    00318
    00319     def set_channel(self, channel):
    00320         if channel in self.clist:
    00321             self.channel = channel
    00322             time.sleep(0.1) # this sleep is somehow needed for rzusb stick.
    00323             self.sport.write("chan %d\n" % channel)
    00324         else:
    00325             print "Unsupported channel %d not in %s" % (channel,self.clist)
    00326
    00327     def set_rate(self, rate):
    00328         if rate in self.rates:
    00329             self.rate = rate
    00330             self.sport.write("\nidle\ndrate %s\nsniff\n" % self.rate)
    00331         else:
    00332             print "Unsupported data rate %s not in %s" % (rate, self.rates)
    00333
    00334     def read_packet(self):
    00335         if self.RxQueue.empty():
    00336             ret = None
    00337         else:
    00338             ret = self.RxQueue.get()
    00339         return ret
    00340
    00341 # === init ====================================================================