生態環境即服務(Ecosystem as a Service; EaaS)對於使用智慧生活科技來打造智慧園區或智慧小鎮而言是一項重要技術之一,該技術的架構如下:
我國工研院和IBM成立跨國合作的實驗室,有興趣社員可以閱讀下面文章:
Smart Living Services(SLS)智慧生活跨國合作實驗室
對於生態環境即服務而言,有下列三項重要的服務模式:
1. 基礎架構即服務(IaaS, Infrastructure as a Service)
2. 平台即服務(PaaS, Platform as a Service)
3. 軟體即服務(SaaS, Software as a Service)
本社群由Nantou.py使用者社群以及國立虎尾科技大學電機資訊學院負責維護,它是一群熱愛智慧生活科技以及Python的專業教師所組成,大家一同快樂地研究有關數位生活中人工智慧、大數據、物聯網、雲端服務、APPS、福祉科技、感知網路服務、車載網路服務、及網際網路等資通訊技術,並運用這些資通訊以及Python技術來提升我們的日常生活品質,建立更好的生活環境。
2011年3月28日 星期一
2011年3月19日 星期六
[ Android ] 如何使用 GDB 除錯 (3)
Android NDK 同樣可以使用 GDB 來除錯。
當我們的 APK 內包含了 JNI 時,我們可以利用 Eclipse 搭配 GDB 來除錯。
# 編譯 NDK
# 執行 GDB
# 使用 DDD
當我們的 APK 內包含了 JNI 時,我們可以利用 Eclipse 搭配 GDB 來除錯。
# 編譯 NDK
$./ndk-build NDK_PROJECT_PATH=project_path
# 執行 GDB
$./ndk-gdb --project=project_path
# 使用 DDD
$cp ndk-gdb ndk-ddd
$vi ndk-ddd
#修改最後一行
ddd --debugger "$GDBCLIENT -x $GDBSETUP" $APP_PROCESS
$./ndk-ddd --project=project_path
[ Android ] 如何使用 GDB 除錯 (2)
在把編譯好且帶有 debugging symbols 的 binary 檔案放到 target device 上後,接下來就可以開始使用 GDB 除錯。
因為不是在 local 端上除錯,所以會需要一些額外的設定。
# 首先要在 target device 上把 GDB Server 叫起來,指令如下。
gdbserver :port /the_path_of_an_executable_file
e.g.
# 然後是 local 端上的設定。
adb forward <local> <remote>
e.g.
# 接著在 local 端執行 GDB,並且設定相關項目。可使用 gdb、gdbtui 或 gdb --tui。
# GDB 的設定也可以另外寫成一個檔案。
# 使用 DDD
因為不是在 local 端上除錯,所以會需要一些額外的設定。
# 首先要在 target device 上把 GDB Server 叫起來,指令如下。
gdbserver :port /the_path_of_an_executable_file
e.g.
$sudo adb shell
#gdbserver :5039 /system/bin/bootanimation
* 指定 GDB Server 監聽的埠號為5039# 然後是 local 端上的設定。
adb forward <local> <remote>
e.g.
$sudo adb forward tcp:5039 tcp:5039
# 接著在 local 端執行 GDB,並且設定相關項目。可使用 gdb、gdbtui 或 gdb --tui。
$ln -s prebuilt/linux-x86/toolchain/arm-eabi-4.4.0/bin/arm-eabi-gdb ./gdb
$ln -s prebuilt/linux-x86/toolchain/arm-eabi-4.4.0/bin/arm-eabi-gdbtui ./gdbtui
$./gdb ${PWD}/out/target/product/product_name/symbols/system/bin/bootanimation
(gdb)directory ${PWD}/frameworks/base/cmd/bootanimation
(gdb)set solib-absolute-prefix ${PWD}/out/target/product/product_name/symbols
(gdb)set solib-search-path ${PWD}/out/target/product/product_name/symbols/system/lib
(gdb)target remote :5039
# GDB 的設定也可以另外寫成一個檔案。
$vi gdb.setup
directory /absolute-source-path/frameworks/base/cmd/bootanimation
set solib-absolute-prefix /absolute-source-path/out/target/product/product_name/symbols
set solib-search-path /absolute-source-path/out/target/product/product_name/symbols/system/lib
target remote :5039
$./gdb -x gdb.setup ${PWD}/out/target/product/product_name/symbols/system/bin/bootanimation
# 使用 DDD
$sudo apt-get install ddd
$ddd --debugger "${PWD}/gdb -x gdb.setup" ${PWD}/out/target/product/product_name/symbols/system/bin/bootanimation
[ Android ] 如何使用 GDB 除錯 (1)
不管是在 Android 上或是在 Linux 上,想 run-time debugging 的話,大概只能靠 GDB 或 ICE。
用 GDB 是最省錢又快速的方法,而且 Android 已經有稍微做過整合,幾乎不太需要什麼額外的設定。
要使用 GDB,首先在編譯時就必須先加入 "-g" 這個參數,例如:"gcc -g -o helloworld helloworld.c"。
但這一段,Android 其實已經先幫我們處理掉了,帶有 GDB Debugging Symbols 的 binary 檔都會在 "out/target/product/product_name/symbols/" 之下。
由於 debug 的對象並不是在 local 端,所以當然不能像在 PC 上直接使用 GDB。
一般來說,target 端(Device)必須先啟始 GDB Server,然後透過網路或 serial port 跟 host 端(PC, Laptop)的 GDB Client 做溝通。
GDB 有三種介面可以使用:
1) "gdb",terminal 下的純文字介面,想看檔案內容還要另外下 command。
2) "gdbtui" or "gdb --tui",terminal 下會顯示目前檔案內容的文字模式。
3) "ddd",圖形介面。
用 GDB 是最省錢又快速的方法,而且 Android 已經有稍微做過整合,幾乎不太需要什麼額外的設定。
要使用 GDB,首先在編譯時就必須先加入 "-g" 這個參數,例如:"gcc -g -o helloworld helloworld.c"。
但這一段,Android 其實已經先幫我們處理掉了,帶有 GDB Debugging Symbols 的 binary 檔都會在 "out/target/product/product_name/symbols/" 之下。
由於 debug 的對象並不是在 local 端,所以當然不能像在 PC 上直接使用 GDB。
一般來說,target 端(Device)必須先啟始 GDB Server,然後透過網路或 serial port 跟 host 端(PC, Laptop)的 GDB Client 做溝通。
GDB 有三種介面可以使用:
1) "gdb",terminal 下的純文字介面,想看檔案內容還要另外下 command。
2) "gdbtui" or "gdb --tui",terminal 下會顯示目前檔案內容的文字模式。
3) "ddd",圖形介面。
[ Kinect ] Enabling Kinect on Linux (1)
最近 XBox Kinect 打得火熱,賣到缺貨。
很不巧的,Kinect 剛出來一兩個禮拜的時候,我就買了。
被這種新世代的體感玩法所吸引,吸引我的不是遊戲,是這個技術。
這種技術將來一定會被廣泛的應用,絕對不只是用在遊戲主機上。
短期內,我們會看到被應用在電腦上,尤其是在 Presentation 方面。
長期看來,適合整合進數位家庭,可以取代一些既有的控制器,比如電燈開關或遙控器。
但以目前來說,我覺得這個技術還有改良的空間。
現在網路上已經有不少 hacker 分享出 Kinect hacking 的成果,目前 Windows、Linux、Mac OS 都有辦法使用 Kinect。
筆者 Linux 環境為 Ubuntu 10.04,在這邊分享一下手動安裝的作法。
OpenKinect Project:http://openkinect.org/wiki/Main_Page
安裝方式也可以參考:http://openkinect.org/wiki/Getting_Started
# 安裝必要套件
# 下載 libfreenect
# 編譯並安裝 libfreenect
# 新增 udev rule
# 編譯並安裝Python wrapper
# run C/C++ examples
# run Python example
# 如果無法執行的話,請把 demo_cv_sync.py 修改成以下的樣子。
很不巧的,Kinect 剛出來一兩個禮拜的時候,我就買了。
被這種新世代的體感玩法所吸引,吸引我的不是遊戲,是這個技術。
這種技術將來一定會被廣泛的應用,絕對不只是用在遊戲主機上。
短期內,我們會看到被應用在電腦上,尤其是在 Presentation 方面。
長期看來,適合整合進數位家庭,可以取代一些既有的控制器,比如電燈開關或遙控器。
但以目前來說,我覺得這個技術還有改良的空間。
現在網路上已經有不少 hacker 分享出 Kinect hacking 的成果,目前 Windows、Linux、Mac OS 都有辦法使用 Kinect。
筆者 Linux 環境為 Ubuntu 10.04,在這邊分享一下手動安裝的作法。
OpenKinect Project:http://openkinect.org/wiki/Main_Page
安裝方式也可以參考:http://openkinect.org/wiki/Getting_Started
# 安裝必要套件
$sudo apt-get install git libusb-1.0-0-dev freeglut3-dev libxmu-dev libxi-dev \
cmake cython python-dev python-numpy python-opencv
# 下載 libfreenect
$git clone https://github.com/OpenKinect/libfreenect.git
# 編譯並安裝 libfreenect
$cd libfreenect
$cmake CMAKE_PREFIX_PATH=/usr
$make
$sudo make install
# 新增 udev rule
$sudo cp udev/51-kinect.rules /etc/udev/rules.d/
# 編譯並安裝Python wrapper
$cd wrappers/python
$python ./setup build
$sudo python ./setup install --prefix=/usr
# run C/C++ examples
$glview
$cppview
$glpclview
# run Python example
$./demo_cv_sync.py
# 如果無法執行的話,請把 demo_cv_sync.py 修改成以下的樣子。
#!/usr/bin/env python
import freenect
from opencv.cv import *
from opencv.highgui import *
#import cv
#import highgui
import numpy as np
cvNamedWindow('Depth')
cvNamedWindow('RGB')
while 1:
depth, timestamp = freenect.sync_get_depth()
rgb, timestamp = freenect.sync_get_rgb()
cvShowImage('Depth', depth.astype(np.uint8))
cvShowImage('RGB', rgb[:, :, ::-1].astype(np.uint8))
cvWaitKey(10)
import freenect
from opencv.cv import *
from opencv.highgui import *
#import cv
#import highgui
import numpy as np
cvNamedWindow('Depth')
cvNamedWindow('RGB')
while 1:
depth, timestamp = freenect.sync_get_depth()
rgb, timestamp = freenect.sync_get_rgb()
cvShowImage('Depth', depth.astype(np.uint8))
cvShowImage('RGB', rgb[:, :, ::-1].astype(np.uint8))
cvWaitKey(10)
[ Android ] Library 開發流程 (1)
若想在 Android 上開發或新增一套 Library,要怎麼做呢?
這套 Library 可能涵蓋 C/C++、JNI、Java 這幾個部份,可能是上層 Applications 必須使用到這個功能,或是 Framework 必須整合這個功能,甚至會跟硬體有關。
現在假設,我們寫了一套 C/C++ Library,用途先不管。
在 Android 上,如果要讓上層 Java Applications 可以用到這個 library 所提供的功能,我們必須實作 JNI 跟 Java Library 這兩層,然後以 Shared Library 的形式供複數隻 Java Applications 所使用。
先大概的描述一下作法:
1. 將 C/C++ Library 編成 Shared Library,也就是 .so 檔,放到 /system/lib/ 底下。
2. 將 JNI 也編成 Shared Library,同樣放到 /system/lib/。這邊的 JNI 也有可能是跟 C/C++ Library 編成同一個 .so。
3. 將 Java Library 編譯成一個 .class 格式的 .jar 檔,供 Java Applications 開發時使用。
4. 將 Java Library 編譯成一個 .dex 格式的 .jar 檔,放到 /system/framework/ 底下。把這個 .jar 檔的路徑加入 CLASSPATH 環境變數,並加入 preloaded-classes 或在 /system/etc/permissions/ 下新增 your_library.xml 並設定其內容 。
這樣就完成了!
這樣你的 Java Applications 就能正確的使用到 C/C++ Library。
這套 Library 可能涵蓋 C/C++、JNI、Java 這幾個部份,可能是上層 Applications 必須使用到這個功能,或是 Framework 必須整合這個功能,甚至會跟硬體有關。
現在假設,我們寫了一套 C/C++ Library,用途先不管。
在 Android 上,如果要讓上層 Java Applications 可以用到這個 library 所提供的功能,我們必須實作 JNI 跟 Java Library 這兩層,然後以 Shared Library 的形式供複數隻 Java Applications 所使用。
先大概的描述一下作法:
1. 將 C/C++ Library 編成 Shared Library,也就是 .so 檔,放到 /system/lib/ 底下。
2. 將 JNI 也編成 Shared Library,同樣放到 /system/lib/。這邊的 JNI 也有可能是跟 C/C++ Library 編成同一個 .so。
3. 將 Java Library 編譯成一個 .class 格式的 .jar 檔,供 Java Applications 開發時使用。
4. 將 Java Library 編譯成一個 .dex 格式的 .jar 檔,放到 /system/framework/ 底下。把這個 .jar 檔的路徑加入 CLASSPATH 環境變數,並加入 preloaded-classes 或在 /system/etc/permissions/ 下新增 your_library.xml 並設定其內容 。
這樣就完成了!
這樣你的 Java Applications 就能正確的使用到 C/C++ Library。
[ Android ] 開機音效 (1)
若是在Android 2.1,init.rc裡面應該有這幾行:
service bootsound /system/bin/playmp3
user media
group audio
oneshot
這就是開機音效,可是你一定找不到playmp3這隻程式。
但Android裡面的確已經有現成可用的程式了,只不過它不叫playmp3,它叫sound。
source code在"android_source/system/extras/sound/"。
只要將上述的playmp3更名為sound,並將一隻WAV音訊檔複製到手機或開發板上的"/data/out.wav"即可。
Android 2.2的話,上述幾行可能要自己加。
service bootsound /system/bin/playmp3
user media
group audio
oneshot
這就是開機音效,可是你一定找不到playmp3這隻程式。
但Android裡面的確已經有現成可用的程式了,只不過它不叫playmp3,它叫sound。
source code在"android_source/system/extras/sound/"。
只要將上述的playmp3更名為sound,並將一隻WAV音訊檔複製到手機或開發板上的"/data/out.wav"即可。
Android 2.2的話,上述幾行可能要自己加。
[ Android ] 開機畫面 (4)
bootanimation 的另一個開機動畫實現方法在 BootAnimation::movie()。
這邊是以播放圖片的方式,來呈現動畫效果。
使用這個方法的話,必須遵守它的規則。
1. 檔案 desc.txt
2. 目錄 part0/
desc.txt 是用來設定 bootanimation 如何去播放 part0/ 下的圖片,圖片必須使用PNG。
當然,你可以有 part1/, part2/, ..., partN/ 多個目錄,來存放你的動畫圖片。
bootanimation 在播放 part0/ 目錄裡面的圖檔,是依照檔案名稱的排序方式在播放,
所以檔案的命名最好是類似 xxx_001.png ~ xxx_nnn.png。
desc.txt 的內容大致上類似:
256 256 30
p 1 0 part0
p 0 0 part 1
...
第一行是表示:
width height fps
設定圖片寬高跟每秒要播放幾張。
第二行以後的:
p count pause path
count 代表要播放 count 次,0為無限。
pause 代表每播放完一個循環後,要進入下個循環之前,中間要不要停頓一下下。
path 代表目錄名稱,part0, part1, ...。
東西準備完備後,把這些東西壓縮成一個 bootanimation.zip 檔。
zip -0r bootanimation.zip desc.txt part0/
最後把這個檔案放到 /data/local/bootanimation.zip 就好了。
這邊是以播放圖片的方式,來呈現動畫效果。
使用這個方法的話,必須遵守它的規則。
1. 檔案 desc.txt
2. 目錄 part0/
desc.txt 是用來設定 bootanimation 如何去播放 part0/ 下的圖片,圖片必須使用PNG。
當然,你可以有 part1/, part2/, ..., partN/ 多個目錄,來存放你的動畫圖片。
bootanimation 在播放 part0/ 目錄裡面的圖檔,是依照檔案名稱的排序方式在播放,
所以檔案的命名最好是類似 xxx_001.png ~ xxx_nnn.png。
desc.txt 的內容大致上類似:
256 256 30
p 1 0 part0
p 0 0 part 1
...
第一行是表示:
width height fps
設定圖片寬高跟每秒要播放幾張。
第二行以後的:
p count pause path
count 代表要播放 count 次,0為無限。
pause 代表每播放完一個循環後,要進入下個循環之前,中間要不要停頓一下下。
path 代表目錄名稱,part0, part1, ...。
東西準備完備後,把這些東西壓縮成一個 bootanimation.zip 檔。
zip -0r bootanimation.zip desc.txt part0/
最後把這個檔案放到 /data/local/bootanimation.zip 就好了。
[ Android ] 開機畫面 (3)
之前有說過,Android 內建的 bootanimation 這隻程式可用兩種方式來呈現開機動畫。
其中一種方式是以兩張圖片來呈現掃光的效果。
你可以在 frameworks/base/core/res/assets/images 這個目錄下,找到 android-logo-mask.png 和 android-logo-shine.png 這兩個檔案。
上圖 android-logo-mask.png 有著 android 鏤空字樣,就是透明、transparent。
下圖 android-logo-shine.png 就是一張有著不同顏色的圖。
若要自己設計這兩張圖,圖檔格式必須為 PNG,而且圖的寬高建議為2的冪次方
pixels。
為什麼會建議為2的冪次方?因為在某些機器上 OpenGL ES 會有無法顯示非2冪次方大小的圖的問題。
這個功能,我們可以在 frameworks/base/cmds/bootanimation/BootAnimation.cpp 中的 BootAnimation::android() 看到。
可以看到,這個 function 會將兩張圖置中並重疊在一起,然後慢慢移動底圖,也就是 android-logo-shine.png 這張圖,最後我們看到的效果就是 android 這幾個閃閃發光的字。
改完後,記得重新編譯 framework,"mm framework"。
然後把編出來的 framework-res.apk,替換掉原本在手機內的 /system/framework/framework-res.apk,就大功告成了。
其中一種方式是以兩張圖片來呈現掃光的效果。
你可以在 frameworks/base/core/res/assets/images 這個目錄下,找到 android-logo-mask.png 和 android-logo-shine.png 這兩個檔案。
上圖 android-logo-mask.png 有著 android 鏤空字樣,就是透明、transparent。
下圖 android-logo-shine.png 就是一張有著不同顏色的圖。
若要自己設計這兩張圖,圖檔格式必須為 PNG,而且圖的寬高建議為2的冪次方
pixels。
為什麼會建議為2的冪次方?因為在某些機器上 OpenGL ES 會有無法顯示非2冪次方大小的圖的問題。
這個功能,我們可以在 frameworks/base/cmds/bootanimation/BootAnimation.cpp 中的 BootAnimation::android() 看到。
可以看到,這個 function 會將兩張圖置中並重疊在一起,然後慢慢移動底圖,也就是 android-logo-shine.png 這張圖,最後我們看到的效果就是 android 這幾個閃閃發光的字。
改完後,記得重新編譯 framework,"mm framework"。
然後把編出來的 framework-res.apk,替換掉原本在手機內的 /system/framework/framework-res.apk,就大功告成了。
[ Android ] 開機畫面 (2)
如何製作 initlogo.rle ?
1. 先準備一張與目標板上LCD解析度相符的圖,例如:480x800,格式為JPEG或PNG。
2. 確認Linux系統中有安裝imagemagick這個套件,因為需要用到'convert'這個指令來轉換圖片格式。沒有的話請用apt-get或yum安裝。
3. 假設圖片名稱為:initlogo.png。
$convert initlogo.png rgb:intlogo.rgb
4. 在Android source code的目錄下,可以找到'rgb2565'這隻程式(out/host/linux-x86/bin/rgb2565)。
$rgb2565 -rle < intlogo.rgb > initlogo.rle
5. 最後把 initlogo.rle 加到 ramdisk 裡面就好了。
1. 先準備一張與目標板上LCD解析度相符的圖,例如:480x800,格式為JPEG或PNG。
2. 確認Linux系統中有安裝imagemagick這個套件,因為需要用到'convert'這個指令來轉換圖片格式。沒有的話請用apt-get或yum安裝。
3. 假設圖片名稱為:initlogo.png。
$convert initlogo.png rgb:intlogo.rgb
4. 在Android source code的目錄下,可以找到'rgb2565'這隻程式(out/host/linux-x86/bin/rgb2565)。
$rgb2565 -rle < intlogo.rgb > initlogo.rle
5. 最後把 initlogo.rle 加到 ramdisk 裡面就好了。
[ Android ] 開機畫面 (1)
1. Boot Loader
2. FrameBuffer Driver
3. init
4. bootanimation
若要開機畫面在使用者按下Power Button後的第一時間就顯示,Boot Loader必須先把畫面輸出到LCD上。
當Boot Loader帶起Linux Kernel後,FrameBuffer Driver initialize hardware時,可再輸出一張畫面。
當kernel結束後,會去執行"/init"。Android就是在這邊將"/initlogo.rle"這張圖輸出到LCD上。
在Android上,"/init"會去讀取"/init.rc"。如果仔細看看init.rc的話,應該會發現有這麼一行:service bootanim /system/bin/bootanimation。沒錯!這時候就會執行bootanimation這隻程式。
bootanimation有兩種方式來呈現開機動畫。
其一,確認系統中是否存在"/data/local/bootanimation.zip"這個檔案,以播放圖片方式來產生動畫效果。
其二,若不存在前述檔案,則使用系統中的"android-logo-mask.png"和"android-logo-shine.png"來產生文字 掃光的動畫效果。這兩個檔案在"android_source/frameworks/base/core/res/assets/images/"。
當然,你可以使用這兩種方式,改改圖片來達成你想要得效果,或者自己重寫一隻更炫的bootanimation。
上述的方法,若要達到seamless的話,還會有一些技術上的細節。
開機畫面這種東西,其實是在開機時間長的機器上才有其必要,為了不要讓user一直盯著黑畫面,還以為手機壞掉。
若開機時間只要一秒,開機畫面其實是多餘的。
2011年3月17日 星期四
將長高 Demo 程式 import 到 Eclipse 中
2011年3月16日 星期三
[教學] Android C2DM
Android 2.2釋出C2DM(Android Cloud to Device Messaging Framework)框架。主要用途是開發者可以透過「雲端」將訊息傳送給Android裝置。
教學影片:
教學影片:
[教學] Android Internals - Building a Custom ROM
轉貼一個不錯教學影片,影片課程:
Building from source
Source code overview
Adding applications
Adding native Libraries
Changing startup process
Changing permissions
Modifying Framework
Linux kernel
Building from source
Source code overview
Adding applications
Adding native Libraries
Changing startup process
Changing permissions
Modifying Framework
Linux kernel
2011年3月10日 星期四
Dalvik JIT 測試
Google在Android 2.2導入Just-In-Time(JIT)編譯,JIT主要在程式執行的時把byte code轉換成native code,所以能讓執行速度更快。表現當然還無法和C/C++相比,但很明顯提升Android程式的速度。
2011年3月8日 星期二
淺談 build/envsetup.sh
在Android build system過程,經常會使用到 " source build/envsetup.sh " 這個指令(註1),"淺談"的話...相對的會對於這部分我也尚在摸索當中,今天找個時間整理了一下關於這部分的相關學習資訊與筆記分享一下。
2011年3月4日 星期五
車用虛擬儀表設計(二)
在2010年3月23日星期二我們提到設計車用虛擬儀表,今天我們要談到四個觀念:
1.利用invalidate()函式來畫面更新
2.使用成員變數來儲存指針角度的資訊
3.利用亂數來提供隨機的增減量
4.利用if來做指針邊界的判斷
其步驟如下:
1.先按照在2010年3月23日星期二我們提到設計車用虛擬儀表完成畫面
2.在onDraw()函式最後一行加上invalidate()函式。
3.使用angle++改變指針的角度。
4.把angle變數由區域變數改變成成員變數,注意區域變數在事件處理函式(如onDraw())內是無法保留其內容。
5.利用亂數來改變指針的增減量
6.利用if來控制指針的範圍,避免跑到非刻度的區域
1.利用invalidate()函式來畫面更新
2.使用成員變數來儲存指針角度的資訊
3.利用亂數來提供隨機的增減量
4.利用if來做指針邊界的判斷
其步驟如下:
1.先按照在2010年3月23日星期二我們提到設計車用虛擬儀表完成畫面
2.在onDraw()函式最後一行加上invalidate()函式。
3.使用angle++改變指針的角度。
4.把angle變數由區域變數改變成成員變數,注意區域變數在事件處理函式(如onDraw())內是無法保留其內容。
5.利用亂數來改變指針的增減量
6.利用if來控制指針的範圍,避免跑到非刻度的區域
Android圖片切換教學
2011年3月1日 星期二
Android在平版電腦上的Activity設計和智慧型手機上有些不同
訂閱:
文章 (Atom)