網頁

2019年12月3日 星期二

生日問題

昨天老婆的弟弟問了一個問題,問題是這樣:
一個20人團體(沒有雙胞胎),在一年有365天的狀況下,至少有2人同一天生日的機率是?

我寫了算式給他(我寫的錯了),後來問數學系的兒子他說應該是:
1- (365*364*363 ....(365-20+1)/365^20

後來查了 google,才知道這是有名的生日悖論問題,公式如下:
我試著用 Julia 遞迴寫這段程式:


輸出結果:


因此,
一個20人團體(沒有雙胞胎),在一年有365天的狀況下,至少有2人同一天生日的機是:
41.14%
由上表可以看到當人數到23人時,2人同一天生日的機就超過 50%了。

相關的內容可以參考:維基百科-生日問題



2019年11月28日 星期四

安裝 Fish shell


fish 是 friendly interactive shell 的縮寫,也是一個 Linux shell ,專注於互動應用、易用性和友好的使用者體驗,其設計目的是通過容易發現,記住和使用的方式為使用者提供一套豐富、強大的功能。fish 於2005年在GNU通用公眾授權條款條款下正式釋出,是一款自由軟體。

安裝 Fish shell:
輸入下面指令
sudo apt-add-repository ppa:fish-shell/release-3
sudo apt-get update
sudo apt-get install fish


設定 Fish shell 為預設 shell:

安裝完 Fish shell 後,預設啟用還是原本的 Bash,
可以嘗試使用這個指令,來把預設的 shell 切換成 Fish:
chsh -s /usr/bin/fish username
(需登出再登入才生效)

每次開啟終端機都會先出現歡迎字串:

    Welcome to fish, the friendly interactive shell
    Type help for instructions on how to use fish

如要關閉歡迎字串,編輯 ~/.config/fish/config.fish 檔案
加上 set fish_greeting 即可。

改變提示字串 prompt:
輸入 fish_config 會開啟預設瀏覽器來設定 fish 的基本功能

*如果執行 fish_config 出現:
distutils.spawn import find_executable

請安裝 python-distutils
sudo apt install python3-distutils -y




點選 prompt 功能,選擇一個 prompt 的種類,選好後按下 Set Prompt
我選了一個 simple_Pythonista 的樣式:

開啟終端機後提示字串就成為這樣:

改變 prompt 顏色:
當前的 prompt 設定檔位於 ~/.config/fish/functions/fish_prompt.fish
輸入 vim fish_prompt.fish,或是用文字編輯器開啟

#5~6 行:是設定使用者的提示字串顏色
#10~#11行:是設定 hostname 的提示字串顏色

基本色的 Color Table 如下:
可以輸入顏色名稱或是 16進制值來改變顏色

 延伸的顏色表如下:

我的 prompt 改成如下:
set_color FFCC00
printf '%s' (whoami)

set_color FF99FF 
echo -n (prompt_hostname)

原本的 pwd:
printf '%s' (prompt_pwd)
改為:
printf '%s' $PWD|sed 's|'$HOME'|~|'

顯示如下:
(修改前)
 (修改後)
 


這樣就可以自己動手打造一個屬如自己風格的提示字串。

 

2019年11月21日 星期四

Linux 的 PDF-Shuffler

PDF-Shuffler 是一個使用 python-gtk 寫成的小工具,他可以協助使用者合併或分割 PDF 檔,
另外也可以對 PDF 的每一頁做旋轉、切割或重新排序。事實上他就是 python-pyPdf 的一個
圖形化使用者介面。

在 Ubuntu Linux 下可以用 apt 直接安裝:

sudo apt-get install pdfshuffler

其使用者介面很簡單,只有幾個按鈕而已,使用者一開始可以使用 Import pdf 功能匯入要編輯的 PDF 檔,匯入之後 PDF-Shuffler 就會將 PDF 檔的內容顯示出來:


這個時候使用者就可以開始編輯了,若要調整每一頁的順序,可以直接使用滑鼠將要調整的
那一頁拖到想要的位置:


另一個常用的就是分割 PDF,
選擇要分割的頁面,將滑鼠移至 pdf 的頁面上按下滑鼠右鍵,選擇 Export selection


還有像是旋轉頁面,刪除頁面操作都很直觀簡單,有需要的人可以下載來試試。


2019年11月12日 星期二

Julia 字典 (Dictionary) 的應用

有次看到工程師在用別人做的 Excel巨集跑 data,這個資料是 wafer 測試完成後將需要做
Laser Trim 的座標檔匯入Excel,然後檢查是不是有重複座標,有時候一片wafer 少則幾千個 dies,多則幾萬個 dies,就算電腦等級再高跑起來也是很慢。
這時如果你是主管你該怎麼做? 該說: 好認真,辛苦了。還是: 笨死了,動作這麼慢!
這種都是典型的嘴砲主管,自己沒本事只會要求員工,我最唾棄這種人啦,偏偏老闆最喜歡
這樣的人。
好啦,上面都是廢話,今天要講的是 Julia 的內建型別:字典(Dictionary)。
字典和陣列很像,在陣列裡,索引必須是整數;但在字典裡幾乎可以是任何型別。


字典包含一組稱為鍵(key)的索引,以及一組值(value)。每個鍵都被賦予一個單一的值,就像上述例子wafer的座標是唯一的,查找是否重複這種工作就很適合用 dict 來解決。
首先,建立一個字典:
person = Dict()

將 "Peter" 這個 key 對應到值 25 存入字典:
 person["Peter"] = 25

keys函數可以傳回字典所有的鍵:
ks = keys(person)

也可以用 ∈ (輸入 \in Tab) 運算子來查看某個鍵是否出現在字典中:
"Peter" ∈ ks
true


知道了怎麼操作 Dict,現在來看一下實際的例子:

下圖左邊數字用逗號分隔是座標值,如 143,9:後面是測試資料

我將每行資料從檔案讀出後放入 array 變數中,建立一個 coord_dict
對 array 每一筆(每行)資料用正規式做判斷是否為資料行,
if occursin(r"(^\d*,\d*)(:.*$)", line)
如果是用 split 函數以 ":" 分割成座標和資料
(coord, data) = split(line, ":")
143,9:die_type=1 die_test=1 bin=1

將座標(143,9) 和值(:die_type=1 die_test=1 bin=1) 分別存入 coord,data變數

檢查 coord 是否出現在 coord_dict 字典中,如果是就印出座標與值;
如果沒有就放入字典中。

if coord ∈ keys(coord_dict)
    重複的座標資料
else
    coord_dict[coord] = data
end

程式碼如下:

我將一筆重複資料放入做測試:

執行的結果:

74024 筆資料中找出一筆重複資料,花費時間 0.138420 sec

這樣只要將重複資料刪除再存檔就可以,剩下時間就可以去喝杯咖啡了。
(之後將這程式放上 server,當測試完後自動檢查並匯出結果省去了人工檢查)

完整程式碼:
https://glot.io/snippets/fhrmy2ytzs


2019年11月9日 星期六

使用 Julia 讀取TSK map 檔案(二)

接著要讀取第237 byte 的資料一直到最後
先來看一下規格書是怎麼說的:
根據下圖每一個 die的測試結果存放在6個 bytes 資料裡面,

例如:第一個 byte 的14,15 bit是存放測試的結果,
0:表示沒測試1表示Pass die
2:表示Fail 1die3:表示Fail 2die
那要麼取出這個 bit 呢? 很簡單將這個 byte資料右移6次的結果就是第14,15 bit 的資料:
 die_test = Int(data[idx]) >> 6
 
 再來看另外一組,如第三個 byte 和第二個 byte 的第1個 bit是存放 Y座標值,要怎麼取出呢?
 第三個 byte全部是值只要將資料轉成數字,第二個 byte的第1 bit要取出只要對它和1做 and
運算即可:(因為是高位元所以要乘上 256)
die_y = (Int(data[idx+2]) & 1)*256 + Int(data[idx+3])

最後1個 byte (0-5 bit)是存放 bin 的結果:
bin_n = Int(data[idx + 5])

看你需要讀取哪些資料就用上述方式取出,
接著再建立一個2D的陣列存放資料,由於資料會轉成字串形式因此我宣告為字串型態:
map_txt = ones(String, row, col)

這裡要注意的是通常在C或是Python陣列都是如下的格式,要儲存(取出)都是行(col),列(row)
如下圖,取出 3 的資料會寫成 a = array[2,0] ,a = 3
但在 Julia 是先 列(row),再 行(col)

這樣就可以寫個代碼來表示一下流程:
map = ones(String, row, col)
idx = 237
for r = 1 to row
    for c = 1 to col
        map[r,c] = read_data
        idx += 6
    end
end

執行後匯出的結果如下:


應該不難吧?!
這個程式會用到 檔案I/O,格式化輸出,2維陣列存取,邏輯運算子,正規式,型態轉換,
如果都能運用,基本上這個語言的基礎就都具備了。
 

程式碼:

using Printf

function to_str(data_s, s_pos, e_pos)
    str = ""
    for idx = s_pos:e_pos
        str = str*Char(data_s[idx])  #combine str
    end
    return str
end

function time_format(time_s)
    time_stamp = "20"*time_s[1:2]*"/"*time_s[3:4]*"/"*time_s[5:6]*" "*
                time_s[7:8]*":"*time_s[9:10]
end

function read_0_235(data)
    run_card = replace(to_str(data, 1, 20), r"\s" => "")
    device_name = replace(to_str(data, 21, 36), r"\s" => "")
    wafer_size = Int(Int(data[37]) * 256 + Int(data[38]) * 0.1)
    global angle = Int(data[49]) * 256 + Int(data[50])
    global col = Int(data[53]) * 256 + Int(data[54])
    global row = Int(data[55]) * 256 + Int(data[56])
    wd = replace(to_str(data, 61, 81), r"[\s+|\0+]" => "")
    lot_no = replace(to_str(data, 83, 100), r"\s" => "")
    cassette_no = Int(data[101]) * 256 + Int(data[102])
    slot_no = Int(data[103]) * 256 + Int(data[104]
    s_time = time_format(to_str(data, 149, 158))
    e_time = time_format(to_str(data, 161, 170))
    l_time = time_format(to_str(data, 173, 182))
    u_time = time_format(to_str(data, 185, 194))
    status = Int(data[209])
    global header_array = []
    push!(header_array, "Run_Card:"*run_card)
    push!(header_array, "Device_Name:"*device_name)
    push!(header_array, "Wafer_Size:"*string(wafer_size))
    push!(header_array, "Angle:"*string(angle))
    push!(header_array, "Wafer_ID:"*wd)
    push!(header_array, "Lot_No:"*lot_no)
    push!(header_array, "Cassette_No:"*string(cassette_no))
    push!(header_array, "Slot_No:"*string(slot_no))
    push!(header_array, "Start_Time :"*s_time)
    push!(header_array, "Finish_Time:"*e_time)
    push!(header_array, "Load_Time  :"*l_time)
    push!(header_array, "Unload_Time:"*u_time)
    push!(header_array, "Status:"*string(status))
end

function read_236(data)
    global map_txt = ["" for i in 1:row , j in 1:col]
    #global map_txt = ones(String, row, col)
    global pass_dies = 0
    global fail_dies = 0
    global total_dies = 0
    idx = 237
    for r = 1:row
        for c = 1:col
            die_test = Int(data[idx    ]) >> 6
            die_type = Int(data[idx + 2]) >> 6
            dummy = Int(data[idx + 2]) >> 1
            dummy = dummy & 1
            die_x = (Int(data[idx]) & 1) * 256 + Int(data[idx + 1])
            die_y = (Int(data[idx + 2]) & 1) * 256 + Int(data[idx + 3])
            bin_n = Int(data[idx + 5])
            if die_test == 0
                if (die_type == 0) && (dummy == 1)
                    map_txt[r, c] = "."
                elseif (die_type == 1) && (dummy == 0)
                    map_txt[r, c] = "M"
                elseif (die_type == 2) && (dummy == 0)
                    map_txt[r, c] = "M"
                end             
            end
            if die_test == 1  #pass die
                map_txt[r, c] = string(bin_n)
                pass_dies += 1
            end
            if die_test == 2  #fail die
                fail_dies += 1
                if bin_n == 1
                    map_txt[r, c] = string(bin_n)
                else
                    map_txt[r, c] = string(bin_n)
                end    
            end
            idx += 6
        end  #col loop end
    end  #row loop end
    total_dies = pass_dies + fail_dies
end

function print_header(array)
    for i in array
        println(i)
    end
end

function print_map()
    #map_txt2 = []
    #map_txt2 = permutedims(map_txt)
    #show(stdout, "text/plain", map_txt2)
    if angle == 90
        for i = 1:col
            for j = row:-1:1
                @printf "%2s" map_txt[j, i]
            end
            println()
        end
    end  #if end  
end

function print_summary()
    println("\n\nSummary\n===========================")
    @printf "Total Pass:%6d\n" pass_dies
    @printf "Total Fail:%6d\n" fail_dies
    @printf "Total Test:%6d\n" total_dies
end

function open_file(fname)
    println("Map File: ", fname, "\n")
    fin = open(fname, "r")
    map_data = read(fin)
end
#-----main Program-----
for i in eachindex(ARGS)
    map_data = open_file(ARGS[i])
    read_0_235(map_data)
    read_236(map_data)
    print_header(header_array)
    print_map()
    print_summary()
end  
 

上一篇:使用 Julia 讀取TSK map 檔案(一)

2019年11月6日 星期三

使用 Julia 讀取TSK map 檔案(一)

學習一種新的語言最快的方式莫過於實際的應用,TSK map 是晶片測試結果的紀錄檔,裡面
紀錄了每一個 die 的測試結果還有相關的資訊,檔案是以Binary的方式紀錄,今天就介紹用
Julia 來讀取並將內容展開。
下面是 map format 的說明書,其中map file 分為2個部分,第 0~235 byte是map header,
236~end 是每一個 die 的資訊。

首先,對 map 作讀檔的動作,將讀進的資料放入 map_data 變數中:


接著,第一筆資料是 Operator Name,是20個位元組的字元型態
剛剛講過資料是以 binary 型態儲存,因此第一筆資料的20個位元組要轉換為字元,由於後面
還會用到因此我寫一個函數來將轉換,傳回結果是字串型態:


現在就針對第一筆資料處理:
operator_name = to_str(data, 1, 20)
operator_name 字串含有空白字元因此要用正規式將空白刪掉
replace(operator_name, r"\s" => "")
因此可以簡化成:
operator_name = replace(to_str(data, 1, 20), r"\s" => "")

這裡要注意的是 Julia的陣列索引起始是 1 開始,而不是一般電腦語言是從 0 開始,
其餘只要是字元的型態都依此類推,這點讓我很不習慣啊~

第三筆資料是 Wafer Size,從第37 byte 開始佔 2 個 byte,資料型態是 binary,
wafer_size = Int(data[37]) * 256 + Int(data[38])
記得高位元組要乘上 256 + 低位元組
這樣結果會是 80(表示8吋),或是 60(6吋) 或是 120(12吋),結果再乘0.1就好
wafer_size = Int(Int(data[37]) * 256 + Int(data[38]) * 0.1)

其餘 binary 型態的資料依此類推,這樣就可以將 0~235 bytes 資料展開。
結果如下:

上圖每筆資料結尾印出"-"是為了確定字串結尾不含空白字元。

很簡單吧!

下一篇: 使用 Julia 讀取TSK map 檔案(二)


2019年10月28日 星期一

設定 julia 編輯環境(二): Sublime 篇

我最喜愛用的編輯器除了 vim 另外就是Sublime,上篇介紹了 julia-vim 的安裝,
這篇就來說說 Sublime 的設定。 

根據 https://packagecontrol.io/installation 提供的方法在此說明 Sublime 增加Julia 語法的方式

1. 首先開啟 Sublime,在View功能選單下選擇 Show Console


將上述網址的代碼貼上:
import urllib.request,os,hashlib; h = '6f4c264a24d933ce70df5dedcf1dcaee' + 'ebe013ee18cced0ef93d5f746d80ef60'; pf = 'Package Control.sublime-package'; ipp = sublime.installed_packages_path(); urllib.request.install_opener( urllib.request.build_opener( urllib.request.ProxyHandler()) ); by = urllib.request.urlopen( 'http://packagecontrol.io/' + pf.replace(' ', '%20')).read(); dh = hashlib.sha256(by).hexdigest(); print('Error validating download (got %s instead of %s), please try manual install' % (dh, h)) if dh != h else open(os.path.join( ipp, pf), 'wb' ).write(by)

2. 這時在 Preferences 選單會多出一個 Package Control 的功能,點選它


3. 用滑鼠選擇 Install Package


4. 輸入要安裝的 Package,juia
這時會出現很多 julia 相關的 Package,就選第一個 julia,這是 for Sublime 2 / 3 版本,
功能是在編輯 julia 程式時會讓語法高亮(highlighting) 顯示。


5. 到 View 功能選單選擇 Syntax,選擇語法為 Julia,這樣就可以了。


接下來就可以開始使用了。


相關參考:
1. 設定 julia 編輯環境(一): vim 篇



設定 julia 編輯環境(一): vim 篇

安裝 julia-vim 開啟終端機輸入:
git clone git://github.com/JuliaEditorSupport/julia-vim.git
cd julia-vim
mkdir -p ~/.vim
cp -R * ~/.vim



安裝好 julia-vim之後就可以直接編輯 .jl 結尾的檔案了。

那安裝 julia-vim 的好處是什麼呢?
Julia語言有個很棒的特點就是它支援 unicode input,所以我們在命名變數時,
可以用各種unicode符號來作為變數名稱,其最大的好處就是讓我們的程式碼變得更直觀、
更容易看懂。
可以很簡單打出 ∑ 這個符號 (\sum TAB),看起來是不是很像數學公式更直觀呢?
例如:


相關參考:
1. 設定 julia 編輯環境(二): Sublime 篇



2019年10月27日 星期日

使用 Julia 讀取TSK log 檔案

安裝好 Julia後第一個作業就是練習檔案實作,我用TSK的 log 檔案來練習。
TSK的 log 檔案是一個2進制檔,每筆紀錄用 0x00 當作結束,如下圖:

接下來就是開啟檔案的寫法
fin = open(filename)
fin 是一個用於輸入的檔案串流,當不使用的時候可以用 close(fin)來關閉它。

接著用 read指令將 fin 指定的檔案讀取進來,轉成字串型態,放入 str 變數:
str = read(fin, String)

由於剛剛講過,log檔案每筆資料是以 0x00結束,因此要將字串中的 0x00 換成 \n (換行)
str2 = replace(str, r"\0" => "\n")

然後將最後的結果str2印出
println(str2)

程式碼如下:
fin = open("LOG_1.DAT")
org_s = read(fin, String)
aft_s = replace(org_s, r"\0" => "\n")
println(aft_s)


執行結果:

因為這樣每次只能開啟一個固定檔案名稱,下面就用 ARGS 外部指定檔案名稱當作參數
傳入程式,用一個迴圈去遍歷ARGS陣列,代碼如下:
for i in ARGS
    ...
    ...
end

實作的程式很簡單就原本的程式外再加上迴圈
for i in ARGS
    println(i)
    fin = open(i)
    org_s = read(fin, String)
    aft_s = replace(org_s, r"\0" => "\n")
    println(aft_s)
end


這樣我就可以同時輸入多個檔案依序讀取並印出:
julia prog.jl LOG_1 LOG_2 LOG_3 . . . 

執行結果如下:

因為檔案很大可以用重新導向到一個文字檔:
julia prog.jl LOG_1 LOG_2 LOG_3 . . .  > text_filename

程式碼可以參考:
https://glot.io/snippets/fha91jgr01

間單吧!



 

2019年10月23日 星期三

ubuntu 安裝 Julia

最近有個新興的電腦語言,叫做Julia。你可能會問Julia有什麼特別之處?
這是一種特別適合科學家,工程師和學生的語言。與通用編程語言相比,
Julia 內置了您在科學中需要的那些複雜數據類型。例如,Julia內置了多維矩陣。
今天就來安裝這 Julia。

1. 首先到 Julia 官網下載,目最新的版本是 1.2 ,https://julialang.org/


2. 下載後會在 ~/Download 目錄下,檔案 julia-1.2.0-linux-x86_64.tar.gz
執行 tar 解壓縮
tar xvfa julia-1.2.0-linux-x86_64.tar.gz


3. 將解開的目錄 julia-1.2.0 搬到家目錄下


4. 設定執行檔的路徑
$ echo PATH=\$PATH:~/julia-1.2.0/bin/ >> ~/.profile
$ source ~/.profile


5. 在終端機輸入 julia,出現下面畫面就表示安裝成功,輸入 exit() 或是 ctrl-D 離開。


6. 設定 package軟體包
在我們使用 Julia 操作之前,還有一個蠻重要的細節。
Julia 1.0 後具有一個新的軟件包管理器來安裝Julia軟件包。該軟件包管理器以前稱為Pkg3,
但現在它已替換了舊軟件包管理器Pkg,並重新使用了舊名稱Pkg。
在更改過程中,當你執行 Julia 時不會自動加載Pkg軟件包,需要在運行Julia時加載它。
因此我們編輯 Julia 1.0 啟動文件startup.jl,並在其中添加使用Pkg。

$ mkdir -p ~/.julia/config/
$ echo "using Pkg" >> ~/.julia/config/startup.jl


7. 試試安裝 UnicodePlots 軟體包,輸入 Pkg.add("UnicodePlots")


現在就來畫個 sin 和 cos 的圖看看,輸入:
using UnicodePlots
myPlot = lineplot([sin, cos], -pi/2, 2pi)


以上這是Julia的簡介。您可以使用on line IDE 來執行Julia程序,也可以在Ubuntu上執行。
學習看看這種新興的語言是否對你合用囉。