2008年12月20日星期六

关于分析网络传输速率(UMTS)

之前在国外的测试团队发回报告说,我们的产品(DL7.2M/UL2M)比另一款对比机(DL3.6M/UL2M)的下载速度要慢许多,还给了我们几组对比数据。一看,我们的速率只有别人的几分之一。这可是个大问题啊,产品的最大卖点都成最大缺点了。后来他们又发邮件说,他们之前的报告中有笔误,Kbps写成KBps了。两个单位有8倍的换算关系,难怪我们的速率只有别人的几分之一。虚惊一场!不过也是有存在一些问题的,就是我们的速率不稳定,忽高忽低。造成的结果是,下载同样大小的文件,需要的时间比其它产品多。
一、分析UMTS下的网络速率可以用QXDM的一些工具(用QXDM分析物理层速率似乎只能用于UMTS下,GSM下还没发现有对应的工具),如下所示:
1)WCDMA EUL Link Statistic:
这个工具可以查看的信息很多,每个信息都有两组数据,分别是上一秒的数据和总的平均数据。

2)WCDMA HSDPA Decoding Statistic:
这个工具可以查看每个传输块大小对应的误码率(BLER)等信息,有一个很重要的信息是,物 理层的传输速度(列表框下的第二栏数据)。

3)WCDMA HSDPA Link Statistic:
这个工具可以用于动态地查看传输速率的变化情况,主要是从整体上来观察(因为上面的信息是动态变化的,而且速度比较快,一般用来查看宏观的信息)。可以在QXDM中右击菜单里把"Cursor"勾上,然后把中间的黄色竖线拉到最右边,这样右上角的信息会动态显示当前的一些值。

虽然这次并不是真的速率上出了问题,但如果速率上出了问题,可以检查以下的几个方面:
1)CQI值
2)HS_SCCH
3)TCP Window Size and MSS (MTU) --可以参考文档80-VF064-1_C_Rec_Param_Settings_Thruput_Num_HSDPA_HSUPA.pdf里的推荐配置

二、如果用QXDM查看到物理层的传输速率正常,那可以用Ethereal/Wireshark抓包来分析(这跟无线网络无关,纯属Internet协议,所以UMTS和GSM下都可以)是否是上层应用的问题,如TCP/IP包本身出了问题。
下载过程中会有很多这样的包:
......
347 01:35:03.706054 82.94.164.162 10.11.230.200 TCP [TCP segment of a reassembled PDU]
-->查看解析后的数据:
@1: Window Size : 6432 (这个值跟TCP segment data size有何关系?)
@2: TCP segment data (1460) --> 看值是否为1460,一般1460为最佳传输大小,小于这个值可能造成速率低
@3: [The RTT to Ack the segment was : 0.059570000 seconds] --> RTT表示发送端发送数据包到接收到响应的时间(Round-Trip Time),这个值越大,说明响应越慢,也会造成速率低。
@4: 是否经常有包被重发(这里没有相应的例子作说明),包被重发也会影响速率。

348 01:35:03.706054 10.11.230.200 82.94.164.162 TCP sweetware-apps > http [ACK] Seq=492 Ack=286161 Win=17520 Len=0
-->这是Ack包。这个包比较有用的信息应该也是RTT。不过似乎Ack消息包含的信息有好几种(不管哪种ACK都会包含"This is an Ack to the segment in frame: xxx"的信息),具体可以看一下包的解析。
......

Ethereal/Wireshark解析出的信息非常丰富,除了以上提到的信息只不过是TCP层的其中一部分。

三、(对数据卡而言)串口的通讯速率
以下引自微软的官方网站:
该速度代表程序向调制解调器传输数据的最大速度,通常比调制解调器的速度快。例如,对于 33.6 Kbps (V.34) 的调制解调器,通常设为 57,600 bps。
更改此设置的同时也设置了调制解调器当前的速度。要将当前速度更改为其他值,请参阅“相关主题”来了解更改数据连接首选项的过程。”
这是很容易被忽略的一点。默认的串口(modem)通讯速率一般是115200bps,对于DL7.2M/UL2M的modem,最大端口速度要比这个值大,以免成为传输瓶颈。

2008年12月17日星期三

去读文件的只读属性

在Windows下要删除只读文件会弹出一个提示文件为只读,是否确定要删除之类的消息框,用Python删除文件时碰到只读属性则会删除失败,解决的办法就是把文件的只读属性去掉再删除。
1. 调用DOS命令
import os
os.system('attrib -R %s' %filename)
os.remove(filename)
这种方法在带UI的程序中会弹出一个cmd窗口,随即又消失,影响用户体验。

2. stat模块
import os
import stat
os.chmod(filename, stat.S_IWRITE)
os.remove(filename)

如果要删除整个文件夹,而其中包含有只读文件,那就先将文件夹下所有文件的只读属性去掉再删除。

import os
import stat
import shutil

def _remove_readonly(filename):
    os.chmod(filename, stat.S_IWRITE)

def rmdir(dir):
    # if there're sub-directories under 'dir', the following line need some modification
    map(_remove_readonly, [os.path.join(dir, filename) for file in os.listdir(dir)])
    shutil.rmtree(dir)

2008年12月14日星期日

关于SCons模块和高通的编译系统

SCons是一个非官方自带的模块,号称是下一代的make程序(即取代make)。对这个模块了解还不多,据说使用起来比make要简单,找个时间好好研究一下。
SCons有几个特点:
1) 采用Python语法,不像make有自己的语法规则;
2) 建立依赖关系时采用MD35算法,而make则采用时间戳对比;
3) 缺点可能就是速度还太慢,这点没有数据说明,我也没做过对比测试;
高通的编译系统是比较复杂的,自己想要添加些东西要对整个系统都比较清楚,碰到跨模块的调用可能就尤为复杂(所谓“跨模块”指,例如:在AMSS中调用BOOT中的代码,或者反过来调用),之前想做一个跨模块的调用,修改makefile文件一直都有问题。另外,现在考虑修改编译系统,尽可能地缩短编译时间。等研究完SCons后看看可行性,是否可以大胆地做一次试验,把整个编译系统替换掉。不过这个风险很大,毕竟商业化的东西不能采用试验性的东西,况且就SCons的第三个特点,这个也可能行不通。过段时间再回头看看吧,这里很作个记号。
之前下载工具我大胆采用Python来开发,这个已经有点冒险了,不过结果也表明Python并不是玩具,而是真的可以用于正规的开发中的。如果不是用Python来开发的话,时间估计要耗费多得多。
以后的多通道下载工具是否还是可以考虑仍然用Python进行开发呢?且不说我会不会参与这部分工作,就算会的话可能也不应该用Python,毕竟周围没几个人会用Python,以后程序没人维护,我岂不是得绑死在这个工具上了,而且生产那边一旦出什么问题,那责任我就得担了。哎,还是我自己用用就好了,不过开发内部的工具我就坚持用Python。我的目标是,让每个工程师都自动安装上Python,那样才能用我所开发的东西,哼哼!

2008年12月11日星期四

工作杂谈

昨天硬件那边说6290在CMU200下从2100频段切换到900的时候会重启,今天到实验室一看才知道原来不是重启,而是切换后掉网了,同时在900频段下无法注册上去。抓了QXDM的Log分析完后发现,用于实验的板子不支持900频段,我想两个问题其实是一个问题,根源就是不支持900频段。本想分析代码看问题点在哪里(猜想应该是NV项的设置值可能有问题),不过涉及到的代码相当之多,而且之前这块代码看得不多,未果。后来硬件重新整理了一下NV项,顺利通过了。高兴的是,这证明我的分析是对的,不痛快的是,这个问题不是在我手头上解决的。
很长一段时间没看协议了,这段时间都费在处理死机和重启的问题,荒废了真正重要的东西。接下去两个重点。一是继续看协议,分析代码;二是Python要加强。虽然表面上看是不相关的东西,但当要写一些工具时,Python的便利和C的强大结合起来真是天下无敌。
今天看了下上个月部门提交的文档积累,忍不住笑起来。一同事从别的地方抄了篇文档,修改一下提交了,另一同事把之前写给生产部门的一个操作说明提交上去了,项目经理提交的是之前已经发出的一篇文档(估计略作修改),我和部门经理则比较没有创意,竟然写的是同一个内容。呵呵,这样的文档有价值吗?看来认真对待的不多,捣浆糊的不少。

关于Logging模块

之前费了不少力气去写打印LOG的库,当时还被一些问题困扰了一段时间,当这个库写得足够用的时候才发现,原来库里面早已提供了一个功能强大的Logging模块。一分钟前还为自己的成果得意洋洋,一分钟后发现原来这些都是白费力气。常常是绕了一个大圈之后回到原点,抬头一看,原来你就在这里,呵呵。得花时间好好来研究一下Python自带的这些库,绝大部分的时候这些已经够用了。先作个标记,回头再用Logging重写个适用自己需要的库。


# 添加于11 Jan, 2009 (中光棍节?)
import os
import logging
import logging.handlers

def CreateLogger(filename, logname='', maxbytes=0, backupcount=0):
    currdir = os.getcwd()
    if not os.path.exists(os.path.join(currdir, 'Log')):
       try:
         os.mkdir(os.path.join(currdir, 'Log'))
       except:
         raise Exception, 'Can\'t create Log directory'
   filename = os.path.join(currdir, os.path.join('Log', filename))
   logger = logging.getLogger(logname)
   logger.setLevel(logging.DEBUG)

   formatter = logging.Formatter('%(asctime)s %(lineno)04.0f %(levelname)-5s --> %(message)s')

   sh = logging.StreamHandler()
    sh.setLevel(logging.DEBUG)
    sh.setFormatter(formatter)
    logger.addHandler(sh)

   rh = logging.handlers.RotatingFileHandler(
filename, maxBytes=maxbytes, backupCount=backupcount)
   rh.setLevel(logging.DEBUG)
   rh.setFormatter(formatter)
   logger.addHandler(rh)
   return logger

def CreateLoggerEx(filename, logname, maxbytes=0, backupcount=0):
    currdir = os.getcwd()
   if not os.path.exists(os.path.join(currdir, 'Log')):
       try:
         os.mkdir(os.path.join(currdir, 'Log'))
       except:
         raise Exception, 'Can\'t create Log directory'
   filename = os.path.join(currdir, os.path.join('Log', filename))
    logger = logging.getLogger(logname)
    logger.setLevel(logging.DEBUG)
    formatter = logging.Formatter('%(asctime)s %(lineno)04.0f %(levelname)-5s --> %(taskname)s :: %(message)s')

   sh = logging.StreamHandler()
    sh.setLevel(logging.DEBUG)
    sh.setFormatter(formatter)
    logger.addHandler(sh)

    rh = logging.handlers.RotatingFileHandler(
filename, maxBytes=maxbytes, backupCount=backupcount)
   rh.setLevel(logging.DEBUG)
   rh.setFormatter(formatter)
   logger.addHandler(rh)
    return logger

2008年12月9日星期二

How to print __LINE__ and timestamp in Python log

import inspect
import datetime
import time

def log(filename):
   import os
   if not os.path.exists('Log') or not os.path.isdir('Log'):
      os.mkdir('Log')
   f = open('Log/%s.log'%filename, 'w')
   return f

def PrintMsg(msg):
    timestamp = _gettime()
    msg = '%s %5s --> ## %s' %(timestamp(), inspect.getouterframes(inspect.currentframe())[1][2], msg)
    print msg

def PrintLog(fp, msg, flag=None):
    ''' Use to print log for each task. We can use some flag to add extra
    information to the output message.
   "inspect.getouterframes(inspect.currentframe())[1][2]" can't be put
   in a nested subcall, otherwise the __LINE__ will be the nested subcall's
   call line.
   '''
   timestamp = _gettime()
   if not flag:
      printlog(fp, msg)
   elif flag == 'DT':
      msg = '%s %5d --> Download Task :: %s' %(timestamp(),  inspect.getouterframes(inspect.currentframe())[1][2], msg)
   elif flag == 'MT':
       msg = '%s %5d --> Main Task :: %s' %(timestamp(), inspect.getouterframes(inspect.currentframe())[1][2], msg)
   elif flag == 'ST':
      msg = '%s %5d --> SrchPort Task :: %s' %(timestamp(), inspect.getouterframes(inspect.currentframe())[1][2], msg)
   elif flag == 'CT':
      msg = '%s %5d --> Command Task :: %s' %(timestamp(), inspect.getouterframes(inspect.currentframe())[1][2], msg)
   else:
      pass
   _print_log(fp, msg)

def _gettime():
   '''To get the timestamp. 'lambda' is a perfect way to do this kind of job.
   '''
   return lambda: datetime.datetime.fromtimestamp(time.time())

if __name__ == '__main__':
   _msg = 'hello devan'
   f = open('testtttt.log', 'a')
   PrintMsg(_msg)
   PrintLog(f, _msg, 'DT')

How to get module's dir info in code

import sys
import os

def usage():
   print '''Use example:
python dirinfo.py ctypes
'''

def main(mod):
   mod_file = os.path.join(os.getcwd(), '%s.txt'%mod)
   fp = open(mod_file, 'w')
   # use __import__ to import a module
   # that passed as a string
   mod = __import__(mod)
   for item in dir(mod):
      print item
  print >> fp, item
   fp.close()

if __name__ == '__main__':
   for arg in sys.argv:
        print arg
   if len(sys.argv) != 2:
        usage()
    else:
       mod = sys.argv[1]
      main(mod)