2012年12月14日 星期五

使用python自動回應php網頁

一、緣起:

這篇文的緣起是這樣的:修了一門課要交作業,要從server上把資料下載下來,剖析資料之後建立模型,把模型回傳給server,server會再產生資料。
以上的上傳下載要重複幾次,server的介面是利用php寫的。
這時候又回到我的哲學了:Working hard, after you know you are working smart.
手動下載、建模型、上傳,這並不是個smart的工作方式其一:來回數次用人做本來就慢;其二:也是最重要的一點,人做容易出錯,出錯就會GG要重做。Smart的工作方式不應該是這樣。


二、解決方案:

經過一些調查,我使用的是python urllib module,這是個超完整的python web module,於是就用這個來寫寫看。

問題一:用python來取得 html?
這部分urllib老早就寫好了,用如下的code,下面一行就可以把整個html以bytes的方式取出來: html = urllib.request.urlopen(hw_url).read()
如果要寫入檔案,在開檔的時候指定為'wb'就可以直接將html 寫到檔案中而不怕格式錯誤。 否則就要呼叫.decode(“utf-8”)的方式先轉換html的格式。
針對form filling的部分也有相對應的方式,可以用下面的方式把POST的訊息編碼,在urlopen的時候附加在後面。
data = {}
data['id'] = ID
form_data = urllib.parse.urlencode(data)
form_data = form_data.encode('utf-8') # data should be bytes
response = urllib.request.urlopen(php_url, form_data)
得到是相同可以用read()取得資料的方式,實地驗證過,會得到一個相同的html file。

問題二:取得最新的資料 - 使用正規表示法:
現在有了取得的html檔之後,要怎麼找出其中的連結? 這裡用上了regular expression module re的方式,這大概是地表上最強大的字串比對工具。 該網頁中的檔案下載連結類似這樣的格式
<a href=\'LOL/XD.txt\'>
只要parse <a href>的標籤,很容易就能找到所有的連結檔了
links = re.findall(“<a\s+href\s*=\s*'(.*)'”, data, re.DOTALL)\
其中()就是我們要尋找的目標,會被存入links之中,成為一個list object,取出最後一個連結,就是我們要的最新版本的檔案了。
另一方面,當使用者要使用這個程式時,會需要輸入他們的學號以供使用,這部分也是使用re module來進行檢查:
re.match(“\s*[A-Za-z][0-9]{7,7}\s*”, string)
不match的話就會回傳None,讓程式再進行一次問話。

問題三:如何執行本地程式?
這個倒比較簡單,使用python subprocess即可簡單完成:
command = "./{0}".format(program_name)
 stdout = subprocess.check_output(command.split())

問題四:如何透過php上傳檔案? 這地方問題就頭大了,查了很久一直不知道該怎麼寫比較好。後來是經強者我同學的提示,用的解決方案是httplib的解決方式: http://stackoverflow.com/questions/1270518/python-standard-library-to-post-multipart-form-data-encoded-data
很有趣的是,httplib的運作是先讀入根目錄,再由selector去要求根目錄下的某個檔案。
fields的部分為二元tuple組成的table,tuple前都為要填入form的name attribute,請愛用「觀看原始碼」自行找出對應的name attribute;
後者為要填入的值,例如fields = [(“name”,”yodalee”),(“nickname”,”garbage”)]
files的部分則是三元的tuple,依序為form的name attribute、filename、file的binary;第三個用open(file, 'rb')傳入即可;例如files = [(“model”, “test.txt”, open(“test.txt”, “rb”))] 

當個例子的code如下:
fields = [("id",ID), ("model_submit","")]
files = [("model", filename, open(filename, "rb").read())]

content_type, body = encode_multipart_formdata(fields, files)
h = httplib.HTTP(host)
h.putrequest('POST', selector)
h.putheader('content-type', content_type)
h.putheader('content-length', str(len(body)))
h.endheaders()
h.send(body)
errcode, errmsg, headers = h.getreply()
return h.file.read()
在id的欄位填入ID,model_submit欄位填入空白。
檔案則上傳filename所指的檔案。
上傳檔案,並取得結果存入h之中。

三、結論:

我只需要下個指令,就能把作業所有需要的檔案都準備完成,打包一下,作業就被秒殺了。雖然說我寫python code的時間其實遠超過我手動執行的時間,但整體而言輕鬆很多,這才是working smart!


 四、參考資料:

python urllib manual:
http://docs.python.org/3/howto/urllib2.html
httplib upload files(answer 2):
http://stackoverflow.com/questions/1270518/python-standard-library-to-post-multipart-form-data-encoded-data


五、致謝:

有關python httplib的使用,感謝AZ Huang同學的提點。
作業的部分,感謝Godgodmouse同學的指導。

2012年11月25日 星期日

將sonnet檔轉換到virtuoso上

做高頻電路討厭的就是,頻率太高的情況下,寄生的電容電感會在電路各處藏汙納垢(XD)。
所以在設計的過程,就需要跑很多電磁模擬,把這些寄生的效應一併考慮到模擬之中。
(會吃你一堆電腦的記憶體跟花一堆時間,這也是我為什麼有時間打這篇文章的原因,哈哈)
麻煩就在於,每次在電磁模擬軟體(實驗室用Sonnet)上畫完架構,然後開始跑,確定結果沒錯之後,又要在電路下線的軟體(實驗室用Virtuoso)上畫一遍。
最近學到,該怎麼把sonnet上的檔案匯到virtuoso上面,在這裡記錄一下。

理論上,我們只要把sonnet檔案匯出到GDSII檔,這是IC layout通用格式,幾乎所有的工具都支援;之後再從virtuoso讀入GDSII檔案即可。
遇到的麻煩是,在進行電磁模擬時,我們會在sonnet裡面建好電路一層一層的架構,但Virtuoso的環境卻是依所用的製程而定,它完全看不懂我們是在講什麼?
所以如果直接匯入的話,就會變成有些層virtuoso不知道該對應給誰,變得完全無法編輯,總之會出現各式各樣的問題。
幸好Virtuoso並沒有這麼笨,在匯入檔案的選項中,可以自行指定層與層的Map file,透過這個,理當能「無損的」將sonnet檔案匯入virtuoso中。

1. sonnet to GDSII:
在sonnet中,是將電路板切成一層一層不同的介質,然後可以在每層介質中填入金屬,去摸擬其中的電磁效應,我們要的就是這金屬的pattern。
下面是一個sonnet層數設定的截圖,從最上面的空氣……到最底層的substrate

合理的推斷,GDSII也是將一層一層的資料給記錄下來,但要怎麼知道?
我用的是開源的klayout打開gds下去看,得到如下的畫面:

發現到sonnet轉gds的規則很簡單,原本在sonnet設在第幾層的金屬,就會被轉到gds第幾層;很可惜的,sonnet中層與層間連接的via的資訊全都消失了,不過很多金屬不用重畫還是可以省下很多時間。

 2. GDSII to Virtuoso:
有了上面的資料之後,再來就是編寫map file: 一個map file 的格式如下:
#Cadence layer name  Cadence layer purpose Stream layer number Stream data type
#
TopMetal2 drawing 0-63 0-63

每一行分為四個子資料,分別是:在此製程中的某層的名稱;該層的用途;對應在stream檔中的層數;該層的資料。
像上面這種寫法,會把0-63層的資料都變成TopMetal2。
purpose的部分,大概有drawing, pin, net三種可以選擇,但從sonnet過來的話大概都是要要轉成drawing的。
stream layer number就是方才用klayout看到的layer編號;stream data type這部分則不是很清楚,但sonnet轉出來好像也不會特別去設定。
根據上面的轉出來的資料,可以自行編寫對應的map,data type不清楚就用0-63全部截下來:
#Cadence layer name  Cadence layer purpose Stream layer number Stream data type
#
TopMetal2 drawing 2 0-63
TopMetal1 drawing 3 0-63
MIM drawing 4 0-63
Metal5 drawing 6 0-63
Metal1 drawing 10 0-63
這樣各層就能夠轉換成功了:


不過另一方面,據說從sonnet匯入virtuoso時,有機會發生一些off-grid的問題,這就是一時之間無法解決的問題了。

結論:
這篇介紹了如何將電磁模擬軟體Sonnet上的繪圖,直接轉換到Cadence Virtuoso上面,我很喜歡 「work hard, after you know you are working smart」類似的話
如果可以直接匯入圖形(work smart),就不要花時間重畫(work hard)了。
理論上如果你用的是Sonnect V13以上的話,其實Sonnet已經含有和Virtuoso的介面了。 http://www.sonnetsoftware.com/products/sonnet-suites/ef_translators_cvbridge.html

參考資料:
1. GDSII format description: http://www.buchanan1.net/stream_description.shtml
2. Layermap format description:
http://www-bsac.eecs.berkeley.edu/~cadence/tools/layermap.html
3. open source gds editor, klayout:
http://www.klayout.de/

2012年11月16日 星期五

放大器的級間穩定

做多級的RF的放大器時,除了看一般的stability factor K值外,還需要去測試電路每級放大器的級間穩定。
實驗室一直遵照學長流傳下來的投影片:將電路從中切開,一邊看stability circle,另一邊看ADS裡面的「Map1 circle」跟「Map2 circle」這個東西,然後看看兩個圓有沒有相交在一起,可是這個Map 1/2 circle是啥鬼東西?極間穩定又是什麼?

1. 極間穩定的意義:


先考慮上圖的模型,在一個主動的電晶體兩旁接上matching的電路,stability circle的定義是,在什麼樣的$\Gamma_L$(or $\Gamma_S$)下,會讓$\Gamma_{in}$(out) >1,在$\Gamma_{in}$(out) > 1時,電路幾乎確定會不穩定,將$\Gamma_{in}$(out)=1的邊線畫出來的圓,就是穩定圓(stability circle)。
電路設計完後,一定會看看stability factor K是否全頻帶都大於1,就是要確認電路整體的$\Gamma_{in}$(out)都在穩定的範圍內。
可是若我們把電路切開,這個模型就不適用了,前後接的不是被動的matching電路,這時候我們就要確保,前端的load stability circle,和向後端可能看到任何一點阻抗,都不會相交;否則很不巧的該阻抗出現,電路就不穩定了;這就是極間穩定背後的原理。

所以說map1/2 circle是啥鬼?從上面的定義來看,我認為它所畫的是:當source(load)呈現任何被動阻抗時(也就是實部小於一,落在smith chart上的任何阻抗),會在另一邊load(source)上呈現的阻抗。
實際用複數變換來驗證看看:
$\Gamma_{in} = w = \frac{a \Gamma_L+b}{c\Gamma_L+d}$
改換上式為下式,兩行的abcd並不是相同的:
$\Gamma_L = \frac{a \Gamma_{in}+b}{c \Gamma_{in}+d}$
a=1, b= $-S_{11}$, c= $S_{22}$, d= $-\Delta$
其中 $\Delta = S_{11}S_{22}-S_{12}S_{21}$
這個 $|\Gamma_L|=1$經轉換後會畫出一個圓,其圓心和半徑為:
$C_L = \frac{c^*d-a^*b}{a^2-c^2} = \frac{-S_{11}|S_{22}^2|+S_{12}S_{21}S_{22}^*+S_{11}}{1-|S22|^2} = S_{11}+\frac{S_{12}S_{21}S_{22}^*}{1-|S22|^2}$
$r = \frac{ad-bc}{a^2-c^2} = |\frac{S_{12}S_{21}}{1-|S22|^2}|$
若兩圓相交,就表示有load會讓$\Gamma_L$呈現某阻抗,而該阻抗會落在不穩定圓中,也就使電路在這級不穩定(新細明體:你死了Q_Q)

2. 實際資料:

Linux有問題先查manpage,ADS遇到鬼先問F1 help,結果help裡面解釋是這麼寫的:
Used in Small-signal S-parameter simulations: The function maps the set of terminations with unity magnitude at port 1 to port 2. The circles are defined by the loci of terminations on one port as seen at the other port. A source-mapping circle is created for each value of the swept variable(s). This measurement is supported for 2-port networks only.
大概的意思是一樣的,依著help的註解找到了計算的ael原始碼,下面是map2的原始碼:
defun map2_center_and_radius(sParam, center, radius)
{
    decl S12xS21 = sParam(1,2)*sParam(2,1);
    decl s11MagSq = pow(abs(sParam(1,1)),2);
     *center = sParam(2,2)+S12xS21*conj(sParam(1,1))/(1-s11MagSq);
     *radius = abs(S12xS21)/(1-s11MagSq);
}
可見所做的是以$S_{11}+\frac{S_{12}S_{21}S_{22}^*}{1-|S22|^2}$為圓心,$|\frac{S_{12}S_{21}}{1-|S22|^2}|$為半徑的圓,和我們所計算的結果相符。

附件:

如何在blogger裡面插入方程式?目前找到兩種解決方案
第一種是用這篇文章中所建議的方式:
http://www.codecogs.com/latex/integration/blogger/install.php
照著裡面的步驟做即可,不過它是將LaTeX轉成圖片,再替換掉原本的文字,所以很醜...
而且測試發現Firefox會當掉(媽的連IE都可以了…),原因不明。
第二種是現在的作法:
方法同上,但code是在下面這個blog裡面找到的
http://aishuxue.blogspot.tw/2012/01/latex-blogspot.html

<script src="http://cdn.mathjax.org/mathjax/latest/MathJax.js" type="text/javascript">
MathJax.Hub.Config({
 extensions: ["tex2jax.js","TeX/AMSmath.js","TeX/AMSsymbols.js"],
 jax: ["input/TeX", "output/HTML-CSS"],
 tex2jax: {
     inlineMath: [ ['$','$'], ["\\(","\\)"] ],
     displayMath: [ ['$$','$$'], ["\\[","\\]"] ],
 },
 "HTML-CSS": { availableFonts: ["TeX"] }
});
</script>
這個結果超漂亮的啊!!!,實在太強了!
在chrome跟firefox裡面都可以用,IE我倒不知道,反正懶得理他。

致謝:

本篇文章感謝503實驗室強者我同學曾奕恩,以及強者我學長陳柏翰學長在複數變換方面的指導。

2012年11月13日 星期二

c++ static 修飾詞

最近寫基因演算法的project,有需要使用到C++ class的static variable,因為我之前這個東西怎麼寫都會出錯,這次好不容易在強者陳仕勳(GodGodMouse)的指導下成功寫出可以用的static,在這裡記錄一下怎麼使用C++的Static(是說這些文章也多到滿出來了)。

靜態物件static的標記包括:在主體程式中使用,在函式中使用,和在class中作為靜態資料使用。
總歸三種使用方式,都是要在程式的各處中使用共有的一份資料,下面針對三種使用方式,分別寫明使用方法及使用的時機: 

1. Main程式:

在global 的地方使用static,可以讓該變數由程式的全域變數變成檔案內的全域變數,使用static的全域變數幾乎永遠是好習慣,畢竟其他程式設計師在用你的程式時只想把你的程式當黑箱子,要是哪個全域變數名字相衝……那大概不是一兩個幹字可以解決的問題,例如下文中的debug變數,我每個檔案都想要有debug變數,這時就要加上static。
簡單的例子,我們幾個cpp檔裡面都有一個debug變數(總不能main裡的叫maindebug,gui裡的叫guidebug吧?),如果不加上static
//main.cpp
int debug  = 0; 
//src1.cpp
int debug  = 0; 
這樣在最後編譯的時候,g++ main.o src1.o的階段,就會回傳錯誤,因為debug在這裡被重複定義。 因此,請為全域變數加上static。
Static int debug = 0
或者,與static相對的就是extern,宣告變數是在別的檔案裡有定義,這時候不能再給初始值(因為初始值是從別的檔案來的):
extern char* filename;

2. 在函數裡使用:

在函數裡加上static的變數,不同於一般的變數「在函數執行時起始,於函數終結時清除」,static變數在程式一執行時即被宣告。我想這個已經芭樂到不能再芭樂的地步了,例子也不勝枚舉,如果我們要輸出某函數被call幾次,請愛用static。
Void function(){
 static int i=0;
 cout << ++i << endl;
}

3. 在class裡使用:

在class裡使用,其功用在於,這個class產生的任何一個object(or instance if you like),都可看到static的變數或函數。 比如說我project寫的基因演算法的程式,經由隨機產生了10000個子代,每個子代都要對某一筆測試資料進行處理,看看結果好不好;這時候當然不可能每一個子代都保留一份測試資料的副本(記憶體不用錢也不是這樣花的吧…),就要用static的方式保存資料,讓每個子代都能看到。
在class裡面用會複雜一點,假設現在有一個叫static_test的class,架構如下:
//static_test.h
class static_test { 
public:
    static int static_var;
    static void output();
    int i;
};
所以我們宣告了一個static的integer跟一個static 的function,在.h宣告中,大部分的東西是一樣的。
但在定義式裡,就有些不同,由於static物件在是class共有的一部分,宣告class物件時並不會初始化static的物件,因此在.cpp裡面第一件事就是先初始化static物件:
//static_test.cpp
int static_test::static_var;
要不要給初始值沒什麼關係,但這行指明:static_test中有一個static_var,不寫這行編譯時會出現undefined reference to static_var。 取用static物件時,也要用'::'指明:我要取用static_test的物件,例如:
cout << static_test::static_var << endl;
static_test::static_var = 10;
static function是類似的事情,每一個class都可以取用這個function,但static function裡面,只能用到static的member,因為它並非任何一個object的function,自然不能取用其中的內容,就如文中的output()不能取用 i 這個變數。
據說static member最常用的地方是統計目前程式中生成了幾個object,在contructor中對static member+1,destruct的時候-1,不過作者到目前還是沒遇到相關的場合,所以…就算了吧,能用就好。

 4. 致謝: 

本篇文章,感謝陳仕勳(GodGodMouse)強者的指導。

2012年10月29日 星期一

autoEM project: 系列二:option parser

最近因為寫autoEM用到的了python optparse函數,就把相關用法整理了一下。
另外之前看一個BASH譔寫的圖形處理程式(接近700行的code就能完成許多驚人的功能,真的有一點點誇張),也發現到原來BASH內就有內建getopts的功能,雖然說查了manpage發現: All parsing is done by the GNU getopt(3) routines,根本就是直接用C的getopt來做的嘛…
不過getopt在寫大型程式時常用到,就整理一下 C、BASH跟python optparse函數,讓自己不要忘掉,也給大家作個參考:

1. C:
在c裡面用的是unistd.h的getopt,這個方式不接受--XD之類的寫法,getopt_long有提供相關功能,用法還滿相似的,就給大家自己去研究:
getopt(argc, argv, optstring) 並有四個外部變數
extern char* optarg
extern int optind, opterr, optopt optstring
為指定getopt要辨識的參數,大略上有3條規則:
1. 在其中的character,都是合法的參數選項。
2. 後面有“:”的character,需要有後面附加的參數,如-f filename,getopt會將optarg指向該串文字。
3. 加上”::”的character,可以用直接連接的方式加上長參數,如 -oargument,此時optarg會指向argument,但使用者忘記寫argument的話,optarg會指向空字串。
getopt每parse一個參數,就會回傳parse到的參數到opt,如果掃完了,就會回傳-1;遇到不合法的參數,會回傳?;加上:的參數卻沒有在之後寫上對應的參數,則回傳?,如果在optstring第一個字元是:,則上述無參數的狀況會回傳:。

看不懂對不對,看一下怎麼用就明白了:
while((opt=getopt(argc,argv,":o::e:"))!=-1){
  switch(opt){
    case'o':
    printf("%s\n",optarg);
    break;
  case'e':
    printf("%s\n",optarg);
    break;
  case':':
    printf("didn't specify the argument\n");
    break;
  case'?':
    printf("unknown argument\n");
    break;
 }
}
上面只是在範例,把選項輸出一下,實際上要做什麼就做什麼,那其實BASH的情況下是幾乎一樣的寫法:
while getopts o:e: OPTION
do
  case $OPTION in
    o)
      …
    e)
      …
    ?)
      …
  esac
done

2. python的部分:
python下有兩個可行的方法 optparse或argparse,不過optparse好像不再支援,以aptparse為主體。
因為我2.7用的還是optparse,之後有空再來整理aptparse,應該不會差太多…吧?
1. 首先是引入optparse:
from optparse import OptionParser make_option
parser = OptionParser(usage = 'Usage: autoEM [OPTION...] PAGE...',
 option_list=option_list)
usage的部分會自動出現在./program -h時的第一行,同時python會自動將現在的option 2. 定義options: 有兩種方法定義options,一種是呼叫parser
parser.add_option('-v', '--version', action='store_true', dest='version', default=False, help='show version information')
另一種個人比較喜歡,先用make_option把選項都寫在一個set option_list 裡
option_list = [ 
    make_option('-v', '--version', action='store_true', dest='version', default=False, help='show version information') ] 
然後如上面paser = OptionParser的地方,用option_list=option_list把選項一口氣寫進去。

上面這個option,會接下的選項為:
-v foo
-vfoo
--version foo
--version=foo store
會設定options.X的值,X依序為dest ->長參數(version) → 短參數(v)如果前一個沒有給定的話,例如上面會設定options.version。
後面的參數可以加上type=int, float,不設的話store會以string的方式儲存。
另外有store_true, store_false,此時會將dest設為true或false。

3. 處理options:
呼叫parser_args處理argument,default會使用sys.argv[1:]作為參數,當然我們傳自己設定的argument。
options, args = parser.parse_args() args會儲存parse到多少個options。
接著就可以透過options.X的方式,一一寫下各自的處理方式,例如:
if options.filename:
 filename=options.filename
一般來說如果使用者忘了傳入特定參數,如-f忘了寫檔名,optparse都會自動輸出上面的usage,跟相關的錯誤訊息。如果我們要自行呼叫error,可以用parser.error()呼叫,如:
if options.a and options.b:
    parser.error('-a and -b can exist together')
另外optparse還有不少功能,不過上面的對於一般程式來說應該相當夠用了,剩下的就請大家自己去研究了。

3. 參考資料:
1. C的部分:
Linux manpage:
man 1 getopt for BASH
man 3 getopt for C
beginning linux programming 4/e
2.Python部分:
http://docs.python.org/library/optparse.html