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

2012年10月25日 星期四

autoEM project: 系列一:ssh遠端指令

實際開始做電路之後,遇到很多不太乾淨的東西,跑em算是其中的一個。
跑em中文曰電磁模擬,將電路的實體架構畫出來,去解出其中電磁的實際分佈狀況;這通常要用到大量的電腦資源,在自己電腦上跑會把你資源佔光幾小時,連東方或minecraft都不能打,這當然是無法接受的,這時就要上工作站跑。
而上工作站跑EM就是十足髒髒的事情,大略描述為:
1. 在本地端畫好電磁模擬檔
2. 利用filezilla, scp等把檔案丟上工作站 ,找到你的模擬檔,還要看哪個工作站還在正常運作,不然連一連斷線就GG了。
3. 利用ssh連上工作站,刪除上次的模擬資料,跑模擬
4. 等模擬結束,這時候還不能斷線(這個問題可以用nohup, screen去解決)
5. 用filezilla下載模擬結果,還要再事模擬結果放到原資料夾
整個過程就髒髒的…(這個詞是從BonoGod那邊學來的),最近受到柏翰學長的啟發,決定來寫一個可以自動管理這個流程的小程式,用Shell script譔寫,之後再改用python的譔寫。

程式功能與目標:
1. 免去手動上傳檔案與上工作站執行指令,在本地程式下達upload, run, download指令即可。
2. 支援一次批次大量模擬,自動監視各工作站的使用量,選擇負擔最低的工作站上傳。
3. 監視進行中的模擬,在結束時發出通知。

為這個小程式遇到不少值得記錄的東西,就把它寫一個系列文,這是第一篇,記錄shell 和python上,透過ssh執行指令的各種方式。

1. shell script:

上傳檔案的部分我一開始是選用rsync的方式,使用方式:
rsync -av -e ssh user@hostname:path
後來發現rsync要求兩邊都要有rsync,而工作站上沒有安裝rsync…orz。
所以還是要回去用scp,其實功能不算差很多:
scp file user@server:file //upload
scp file user@server:file //download

下指令的部分則是使用:
ssh user@hostname 'instruction'
這樣的問題是每一次指令都需要使用者輸入密碼,加上上傳下載檔案,跑一趟em都要使用者輸入3次密碼,這樣還是很沒效率。
解法方式是設定ssh公鑰,或者用類似expect的方式來模擬使用者輸入。我是用前者的作法,在code裡加上一個add worstation 的指令,自動產生公鑰並上傳到指定的工作站上,之後即不會要求每次執行都要輸入密碼,這部分詳述資料請見參考資料一。

另外如何在ssh被切斷之後,執行的程序還能繼續?
這部分有滿多解決方案的,最簡單的是用ssh本身就有的功能:
ssh -f user@hostname 'instruction'
這樣指令就會自動在遠端背景執行。
或者如柏翰學長使用的方式,用at來執行:
echo "em -v $file > em.log 2>&1 &" > sub_file
at now +$delay minutes < sub_file
或者就要透過nohup或是screen/tmux兩種方式:
ssh user@hostname 'nohup instruction &'
ssh user@hostname 'screen -S autoEM -d -m instruction'
這兩個方式略有不同,nohup會讓程式忽略所有SIGHUP(hangup)的信號,ssh登出時的hangup信號就不會把該指令給刪除。
Screen則是直接創造一個virtual console(其實我也不確定是不是這樣叫orz),-d -m會detach這個screen,指令就在裡面繼續跑,之後再用screen -r將程式給要回來;不過又發現有工作站上沒有screen或tmux(......),如果遇到這個問題,可能就真的只能跟管理員反映了...。

2. python:

如果改成用python來寫呢?
Python裡已經有SSH2 protocal的套件庫paramiko,比較可惜的是這個套件目前載不到python3 的版本,只能先用2.7寫。
Python 寫起來就直覺多了,paramiko已經把大部分ftp的指令都整合在套件庫裡,包括sftp跟ssh,document頁面在:http://www.lag.net/paramiko/
大家可以載一下它們的安裝檔,裡面的demo.py就有很多範例可以用,包括sftp, ssh:
ssh = paramiko.SSHClient()  
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) 
ssh.connect(info.hostname, username=info.username, password = info.password)  
stdin, stdout, stderr = ssh.exec_command('source ~/.bashrc; nohup em ~/autoEM/%s em.log 2>&1 &' % filename)  
stdout.readlines() 
stderr.readlines()
因為paramiko已經把ssh包在裡面,我沒辦法像shell一樣,用ssh -f的方式去下背景指令,因此就用nohup的方式;比較有趣的是,指令中,我一定要重導向輸入跟標準/錯誤輸出(不能讓nohup自行產生nohup.out),在下完指令後也一定要stderr.readlines(),否則我ssh切斷之後nohup還是會被切掉,這部分原因不明。

3. 小結:

這篇主要記錄如何透過ssh,在BASH, python上對遠端工作站下達指令,目前這部分已經完成,下一篇可能會談一下主程式譔寫時遇到的問題,或中途寫下的筆記。

4. 參考資料:

1. 鳥哥的網頁,有關ssh的部分:
http://linux.vbird.org/linux_server/0310telnetssh.php
2. python paramiko package:
http://www.lag.net/paramiko/

5. 致謝:

本篇文章感謝:
陳柏翰學長在自動執行EM的shell script上的分享。
黃偉寧前輩在nohup, screen程式譔寫上的指導,雖然在討論過程中,前輩提供了無數種解決方案,模擬使用者行為,其他指令等,但我最後都沒有用上Orz(應該說…有聽沒有懂,我們之間至少差了20dB左右)。

2012年10月17日 星期三

archlinux下使用jnc連接學校VPN

從學校的宿舍搬出來後,為了要從學術資料庫裡載paper下來看,對VPN連線的需求日益孔急,一時之間找不到連線辦法,只好使出最智障的方式:在Vbox裡面開windows然後從windows裡面連VPN…。 但這個方法實在太腦包了,最近經過一番嘗試,總算成功在Archlinux下連接學校VPN,在這裡記錄一下解法的各步驟:

1.程式配置: 安裝jnc,這個簡單從AUR裝就OK惹
https://aur.archlinux.org/packages.php?K=jnc&SeB=x
從學校VPN載到juniper client的檔案,學校已經壓成rpm檔,於是用rpmextract.sh解開它,裡面有幾個程式包括:NC.jar, ncsvc, libncui.so之類的
把解壓縮的檔案放到~/.juniper_networks/network_connect裡面

2.修改jnc: jnc的執行程式是用perl寫的,位置應該會裝在/usr/bin/內,用超級使用者權限做一些修改:
my $ncdir = "$ENV{'HOME'}/.juniper_networks/network_connect";
my $configdir = "$ncdir/config";
這兩個指定VPN執行檔與設定檔的的位置,覺得這兩個地方風水不好的話,可以修改這個地方。
my $gui = 1;
可以設定要不要有gui介面,這部分需要java,我懶得管什麼gui就把它改成0 XD

3.設定檔: 如上,預設的設定檔為~/.juniper_networks/network_connect/config/default.conf
格式如下:
host=foo.bar.com
user=username
password=secret
realm=very long realm with spaces
cafile=/etc/ssl/bar-chain.pem
certfile=
下面則是我的設定檔,為了保護學校的校譽,在這裡把學校名字蓋住:
host=sslvpn.XXX.edu.tw
user=garbage
password="Garbagepass"
realm=XXX Email Account
cafile=/home/yodalee/.juniper_networks/network_connect/XXX.pem
certfile=
在realm的部分,就是登入vpn時除了帳號密碼外的那欄,我是用chromium F12看原始碼找到相對應的地方
<select size="1" name="realm">
注意這裡大小寫要全對才會過。

4.VPN認證檔: 這地方要從VPN提供者的網頁載下它的認證,使用下面的指令:
openssl s_client -connect url:443 > test.pem
然從編輯該pem檔,把-----BEGIN CERTIFICATE-----到 -----END CERTIFICATE-----以外的部分都刪掉,把這個pem檔放在設定檔中,cafile指定的位置

5.特殊權限: 這部分主要是參考資料2中所提到的: 好像是因為jnc呼叫的ncsvc中,會更動/etc/resolve.conf的內容還是怎麼樣,要對ncsvc的權限做特別設定,我也沒有試著不設定然後連結VPN,不知道不設會怎麼樣:
$ sudo chown root:root ~/.juniper_networks/network_connect/ncsvc
$ sudo chmod 6711 ~/.juniper_networks/network_connect/ncsvc
$ chmod 744 ~/.juniper_networks/network_connect/ncdiag 

6.連結vpn: 使用jnc就可以直接連結惹 沒圖沒真相,用vpn連結學校資料庫,可以看得到只有學校網域才享有的download full text:

再用jnc stop就可以切斷vpn連線。

參考資料:
1. https://wiki.archlinux.org/index.php/Juniper_VPN
2. http://www.rz.uni-karlsruhe.de/~iwr91/juniper/

致謝: 本篇文章感謝黃偉寧(AZ Huang)同學的指導

2012年10月5日 星期五

傳輸線穩態解與暫態解



這幾天在讀電磁學,發現一個自己沒想過,卻滿基本的問題:
我們先拿一個最芭樂的傳輸線模型,如下所示:
現在假設Vg=1VZs = 50ΩZ0=50ΩZL=25Ω,線長λ/4(懶…選個簡單一點的數字)
請問從傳輸線入射的功率為?

解法一:
Zs和傳輸線的交點分壓
P = ½ * ( 1 * 50/(50+50) )^2 / 50 = 2.5mW
解法二:
25Ω經過傳輸線得到Zin = 100Ω,與Zs=50Ω分壓去解,得到2.222mW

問題:為什麼兩個答案是不一樣的?

--

答案:
兩個解法其實是在不同的條件下去解的。
在第一個解法時,我們看的是輸入的電壓在遇到兩個不同的介面時,電壓的分佈狀況。
第二個解法時,我們是把整個傳輸線和load,等效成一個電阻,去計算功率的分配。
不一樣在哪?
在解法二裡面,我們省略了傳輸線用電阻等效時,已經把電磁波經過不同介面的反射穿透都考慮進去,看看多少的功率被傳到傳輸線和load端(因為傳輸線為無損耗,所以就是在算load消耗的功率);但在解法一,我們只考慮了瞬時的功率分配,後面的效應還沒有考慮。

我們可以把解法一繼續解下去。
V=1/2, ΓL = -1/3,在輸入端無反紙,功率消耗在Zs上,反射功率為(1/6)^2 /2/50 = 0.278mW
所以Vg從傳輸線打入2.5mW,反射回來0.278mW,在傳輸線(或load)上消耗的功率為:
2.5-0.278 = 2.22mW
解法二實際上是在解整個電路的穩態解,解法一只是電路打開一瞬間,在Zs和傳輸線上的暫態。

所以說,哪個是真的?
在現今的電路模擬中多是利用頻域模擬,將一塊電路變成某個S參數、Z參數…的陣列,所做就是將電路的延遲效應給排除,只看電路在穩態下的特性,也就是解法二所做的事情。
但課本中的圖形,有正反向電壓波反彈反彈直到穩態解的,就需要第一種解法才能表示。

--

第二個是同樣概念的問題:
同樣是上面那張圖,現在Zs=50Ω, Z0=25Ω, ZL=50Ω,線長λ/4(懶…一樣選個簡單一點的數字)
input端的反射係數:
解法一:先算ΓL=1/3經過λ/4線後,Γin=-1/3
解法二:Zin = 25^2/50 = 12.5,得到Γin = (12.5-50)/(12.5+50) = -3/5
問題:為什麼兩個結果不一樣,哪一個是真的?

同樣,第一個解法只是考慮在load端的一個暫態下,每個入射的電壓波會有多少反射;但我問到Γin時,就是要把整個電路等效成一個阻抗,然後看看入射的電壓波有多少會反射。
這時,因為ZsZ0的不匹配,從ZL看到的阻抗其實不是25Ω,而是12.5Ω,算到Γ=3/5,再經過了λ/4的傳輸線,可得到輸入處的反射係數為-3/5

結論:
一般微波電路在討論時,是針對穩態解的參數如Z, S matrix下去討論的,這樣的結果和只討論暫態的結果當然不一樣,計算時要稍微注意。