2018年1月3日 星期三

2017 年第 52 周記事

這是 2017 年的最後一周了, 人生就是一串數字, 串起各種回憶, 但遺忘的多, 記得的總是比較少. 時間就像我房外不遠處小圳中川流不息的水, 它不斷地流逝, 在桌前靜靜讀書的時候就可以聽到那潺潺的水流聲, 看似平淡, 但那就是生命之流啊!

姊姊週五 12/29 晚上坐自強號回到高雄, 聽她頻頻咳嗽, 原來聖誕節第二天感冒請假沒去上課, 拖到回高雄才要去看醫生, 結果楊醫師卻休診四天. 週六回到鄉下採了一些魚腥草燉麥芽糖酸梅湯給她喝, 但似乎效果不顯著. 冬天的魚腥草不像夏天時那麼茂密, 品質可能也不甚好.

元旦連假雖說休三天, 但週六我要上課, 周一中午姊姊又要搭車回台北, 所以也是如往常只休一天而已. 元旦過後就準備迎接舊曆年了, 一整年大部分的時間都在忙, 這時除了要來個大掃除, 大整理外, 應該放慢腳步來回顧一下做了甚麼, 想做又沒做的有哪些, 也要來個大盤點.

2018年1月2日 星期二

Arduino + LoRa 模組測試

這篇是去年 10 月底對 LoRa 模組進行初步測試的紀錄, 後來忙著幫二哥準備 APCS 而暫停下來. 昨天有網友問我空曠處實測距離多少, 才想起這篇還沒完成的草稿, 稍作整理如下備查. 在傳送距離方面, 網友與我的實測距離都約 200~250 米而已, 據說使用 IPEX 天線似乎沒有好到哪裡去, 實在殘念! 我還抱著一絲希望的說.

會接觸 LoRa 是因為去年十月初有另一位網友詢問我有無用過 LoRa, 當時我連買了放在零件箱好久的 nRF24L01 模組都還沒動哩! 為此我除了找出 nRF24L01 先測試一番以產生比較資料外, 還上網對此新興無線射頻技術做了初步研究, 整理了一篇知識摘要, 參見 :

# 長距離低功耗無線通訊技術 LoRa

接著在露天購買了一對最便宜的 LoRa 模組來測試 (跟 nRF24L01 一樣要成對地買, 一個傳送, 一個接收, 或者互相傳送接收), 此板用的是 SX1278 晶片 (433 MHz)  :

# [史塔克實驗室][Arduino/RPi]每個180元2個一組販售SX1278 Lora module 模組433MHz $180

由於這是 2.0mm 腳距的裸板, 所以特地跑去禾樺買了 2.0 mm 的排針與排母, 在焊接前試試看到底要用排針或排母比較適合, 結果發現排針根本不能用, 因為 2.0mm 的排針比較細, 杜邦線母頭插上去鬆鬆的會接觸不良, 應該要用排母才對, 因為 2.0mm 的排母上的洞與一般 2.54mm 者一樣, 插杜邦線沒問題.

我買的這組 LoRa 模組每邊 8 隻腳, 其腳位布局如下 :


 DIO2 GND 
 DIO1 MISO 
 DIO0 MOSI 
 3.3V SCK 
 DIO4 NSS 
 DIO3 RESET 
 GND DIO5 
 ANT GND 


由於 ANT 腳要焊彈簧天線, 因此左邊的排母只要 7 隻腳, 而右邊的則需 8 隻腳, 使用小型電鑽前面裝上鑽石圓盤分別切出 7 支與 8 支腳的排母各一對, 然後用三秒膠少許加以固定後進行焊接. 注意, 三秒膠不可用太多, 否則萬一滲入排母內會讓排針插不下去.


Lora 板子焊好後就可以進行測試了, 我使用 Arduino 當作 MCU 主控, 首先是到下列 GitHub 網站下載 Arduino 的 Lora 函式庫 :

# https://github.com/sandeepmistry/arduino-LoRa

按右邊的 "Clone or download" 鈕, 選 "Download ZIP" 下載壓縮檔 arduino-LoRa-master.zip, 將其複製到 Arduino IDE 安裝目錄的 libraries 下解開即可. 重新開啟 IDE 就可存取到此函式庫了. 此函式庫的 API 說明如下 :

# https://github.com/sandeepmistry/arduino-LoRa/blob/master/API.md

此函式庫使用 SPI 協定與 LoRa 模組通訊, 預設傳輸速率是 10 MHz. 接下來是硬體接線, 此 LoRa 模組採用 SPI 介面 (也有用 Serial 介面的), Arduino 有特定的 SPI 接腳, 與 LoRa 的預設對應如下 :


不過其中 NSS, RESET, 與 DIO0 這三隻腳不一定要對應到 D10, D9, 與 D2 這三支 Arduino 腳, 可以連接到除 D13, D12, D11 以外的任何 DIO 腳位, 但在程式中要用 LoRa.setPins(NSS, RESET, DIO0) 指令指定.

測試 1 :

傳送端程式 :

#include <SPI.h>
#include <LoRa.h>

int counter=0;  //傳送次數計數器

void setup() {
  Serial.begin(9600);
  while (!Serial);  //等待序列埠起始完畢
  Serial.println("LoRa Sender");
  if (!LoRa.begin(433E6)) {  //起始 433MHz LoRa
    Serial.println("Starting LoRa failed!");
    while (1);
    }
  }

void loop() {
  Serial.print("Sending packet: ");
  Serial.println(counter);
  LoRa.beginPacket();  //封包傳送開始
  LoRa.print("hello ");  //封包內容
  LoRa.print(counter);  //封包內容
  LoRa.endPacket();  //封包傳送結束
  counter++;  //計數器增量 1
  delay(5000);
  }


接收端程式 :

#include <SPI.h>
#include <LoRa.h>

void setup() {
  Serial.begin(9600);
  while (!Serial);  //等待序列埠起始完畢
  Serial.println("LoRa Receiver");
  if (!LoRa.begin(433E6)) { //起始 LoRa
    Serial.println("Starting LoRa failed!");
    while (1);
    }
  }

void loop() {
  int packetSize=LoRa.parsePacket(); //讀取剖析 LoRa 封包大小
  if (packetSize) { //若有封包進來
    Serial.print("Received packet '");
    while (LoRa.available()) { //若接收緩衝器有內容
      Serial.print((char)LoRa.read());  //讀取緩衝器內容並輸出階收到的封包
      } 
    Serial.print("' with RSSI="); //輸出接收封包之 RSSI
    Serial.print(LoRa.packetRssi());  //顯示接收信號強度
    Serial.print(" and SNR="); //輸出接收封包之 SNR (信噪比)
    Serial.println(LoRa.packetSnr());  //顯示接收信號信噪比
    }
  }

序列埠監控視窗輸出結果如下, 收到訊號時會顯示 "Received packet ... RSSI= .... SNR=...." :
.....
irqFlags 0
irqFlags 10
irqFlags 40
Received packet 'hello 29' with RSSI=-39 and SNR=16.00
irqFlags 0
irqFlags 0
irqFlags 0
irqFlags 0
irqFlags 0
irqFlags 0
irqFlags 0
irqFlags 0
irqFlags 0
irqFlags 0
irqFlags 80
irqFlags 0
irqFlags 0
irqFlags 0
irqFlags 0
irqFlags 0
irqFlags 0
irqFlags 0
irqFlags 0
irqFlags 80
irqFlags 0
.....
irqFlags 10
irqFlags 40
Received packet 'hello 30' with RSSI=-51 and SNR=15.75
irqFlags 0
irqFlags 0
irqFlags 0
irqFlags 0
irqFlags 0
irqFlags 0
irqFlags 0
irqFlags 0
irqFlags 0
irqFlags 0
irqFlags 80
irqFlags 0
.....

其中 RSSI (Received Signal Strength Indicator) : 接收信號强度指示.

用彈簧天線於空曠地區實測距離約 250 公尺, 似乎只比 nRF24L01 的 100 公尺長兩倍多而已, 與產品說明中所說的 1 公里相差達 4 倍. 不過信號穿透力可達 2~3 層樓, 在電梯中亦可收到信號, 這就比 nRF24L01 出色多了, nRF24L01 只要 5 公尺外轉個牆角就 GG 了, 參考 :

# Arduino 無線傳輸模組 NRF24L01 測試

測試距離方面, 有空我還是會買支 IPEX 或其他廠家的 LoRa 模組來驗證看看.

參考 : 

# Arduino Arduino module communication by Lora Sx1278
# https://github.com/matthijskooijman/arduino-lmic
# 【Acsip LoRa實作1】快速開發LoRa通訊功能-SPI介面篇
# An Arduino library for sending and receiving data using LoRa radios
# LoRa 的 Arduino 函式庫
# 【Acsip LoRa實作1】快速開發LoRa通訊功能-SPI介面篇
# WLK01S78-TH 無線模組 LORA模組 SX1278數傳模組 w7 056 [5059647] $260
# LoRa 測試資料下載  (測試地圖快照) (測試地形圖)
# <微控制器科技> Pycom LoPy IoT 三合一開發板 (LoRa+BLE+WiFi) $1350
# Pycom LoPy - LoRa+WiFi+Bluetooth MicroPython IoT Platform £32.5
# https://github.com/a3rd/lora-inair4
# SX1278 LoRa擴頻無線模組 433Mhz $220
# 大陸模組資料下載
# https://drive.google.com/drive/folders/0BywdxdAWyMLcWXpFNF9QVWhHZHc
# SX1278 LOra擴頻 電力抄表模組 5km無線收發模組SX1276模組 $200
# iFrogLab iL-LORA1272 超遠端15公里LoRa資料傳遞和接收模組 for Arduino 和 樹梅派 $850
# iFrogLab iL-LORA1272-A LoRa 資料傳遞和接收模組 韌體全面更新為1.1版 $850
# 98 Mailbag #8: LoRa, ESP8285, ESP32 in a WiPy Python board, A6C with camera
# LoRa 範例程式下載
# Setting Up the Modtronix InAir4 with Arduino

2018-01-03 補充 :

下面這部影片測試 LoRa 的耗電情形 :

#120 LoRa on batteries: How long does it last?



2018年1月1日 星期一

城邦購書四本

城邦網路書店每年會員生日有 65 折優惠 (我是 VIP 會員), 加上累積紅利已 411 點 (2018-08-03 到期), 因此上網挑了下面四本書 :
  1. 情感運算革命:下一波人工智慧狂潮,操縱你的情緒、販售你的想法,將是威脅還是機會?
  2. 從人到人工智慧,破解AI革命的68個核心概念
  3. 台灣股神教你看懂贏家選股必賺的財報祕技
  4. 把60萬變3億!不買主流股,我賺500倍



原總價 1520 元, 65 折優惠價 988, 扣除紅利換書 300 點 (用情感運算革命這本兌換最划算, 只損失 1 點紅利), 應付 689 元, 實際折扣 689/1520  約 45 折, 紅利換書實在很優惠.

網路訂單須注意 :

  • 如您的網路訂單有使用折價券優惠,線上取消訂單後,該折價券優惠將無法再使用!
  • 完成訂單交易贈送的折價券,請務必於訂單日期後三個月內索取,否則將自動失效!
  • 如您的網路訂單有使用紅利點數兌換書籍,線上取消訂單後,該筆訂單使用的紅利點數會立即加回。


  • 樹莓派自動偵測網路斷線時重開機的方法

    這兩周因為在學 R 語言, 無暇玩樹莓派. 不過最近有注意到, 奇怪, 為何都沒收到鄉下樹莓派主機傳送的 IP Changed 的郵件? 難道這個禮拜 ADSL 的浮動 IP 都沒變? 不太可能啊! 周六晚上回到鄉下檢查樹莓派才知是沒有連上無線基地台, 我猜是信舟這台 AP 有時會卡住, 我叫爸遇到 WiFi 無法上網時就去重開 AP 電源, 但 AP 重啟後樹莓派不是應該會自動再連線嗎? 有待細究. 總之, 我將樹莓派重開機後就正常連線了, 馬上就收到 IP Changed 郵件通知.

    看來只有自動偵測 IP 變化是不夠的, 還得偵測網路連線是否正常. 如果因為 AP 重啟, 而樹莓派又沒有自動去連線, 就抓不到外網 IP, 當然也送不出郵件來. 經爬文找到下面這篇文章, 剛好就是我遇到的問題及解決辦法, 呵呵, 得來實在不費工夫, 感謝原作之分享 :

    # Rebooting the Raspberry Pi when it loses wireless connection

    作者的解決辦法其實很簡單, 就是寫一個如下的 shell 程式放在 /usr/local/bin 底下, 賦予可執行權限, 然後在 crontab 中加一條定時工作週期性執行此程式來檢查網路連線, 如果發現連線有問題就重啟樹莓派 :

    ping -c4 192.168.2.2 > /dev/null
    if [ $? != 0 ]
    then
      sudo reboot
    fi

    程式第一行是用來 ping 無線網路上的某個主機, 通常是無線基地台的 IP, 這也是程式中唯一要改的地方. 此 ping 指令若網路連線正常將無回應值, 使得 $? 為 0 而不會去執行 then 的 reboot; 反之當連線異常時就會有回應值, $? 即不為 0 導致 reboot 指令被執行.

    首先用 nano 編輯 checkwifi.sh 程式, 輸入上述程式後按 Ctrl+O 與 Ctrl+X 存檔 :

    pi@raspberrypi:~ $ nano checkwifi.sh   

    然後將此指令複製到 /usr/local/bin 下, 並賦予可執行權限 :
    pi@raspberrypi:~ $ sudo cp checkwifi.sh /usr/local/bin/checkwifi.sh    
    pi@raspberrypi:~ $ sudo chmod 775 /usr/local/bin/checkwifi.sh   

    但是執行此程式卻出現語法錯誤 :

    pi@raspberrypi:~ $ sudo /usr/local/bin/checkwifi.sh
    /usr/local/bin/checkwifi.sh: 6: /usr/local/bin/checkwifi.sh: Syntax error: end of file unexpected (expecting "then")

    查詢 shell script 語法確認程式語法正確, 怎會如此呢? 以錯誤訊息查詢發現 stackoverflow 已有人遇到過此問題並提出解答, 參考 :


    "I have met the same problem. And the problem is the format of the file is "dos", but in linux shell requires "unix", so I install the "dos2unix""

    原來即使用 nano 編輯的 shell 程式還是 DOS 格式, 須安裝 dos2unix 來將其改為 Unix 格式 :

    pi@raspberrypi:~ $ sudo apt-get install dos2unix     
    Reading package lists... Done
    Building dependency tree
    Reading state information... Done
    The following NEW packages will be installed:
      dos2unix
    0 upgraded, 1 newly installed, 0 to remove and 198 not upgraded.
    Need to get 75.9 kB of archives.
    After this operation, 204 kB of additional disk space will be used.
    Get:1 http://mirrordirector.raspbian.org/raspbian/ jessie/main dos2unix armhf 6.0.4-1 [75.9 kB]
    Fetched 75.9 kB in 6s (12.0 kB/s)
    Selecting previously unselected package dos2unix.
    (Reading database ... 123331 files and directories currently installed.)
    Preparing to unpack .../dos2unix_6.0.4-1_armhf.deb ...
    Unpacking dos2unix (6.0.4-1) ...
    Processing triggers for man-db (2.7.0.2-5) ...
    Setting up dos2unix (6.0.4-1) ...

    安裝完畢後用 dos2unix 將 checkwifi.sh 改成 Unix 格式即可正常執行了 :

    pi@raspberrypi:~ $ dos2unix checkwifi.sh   
    dos2unix: converting file checkwifi.sh to Unix format ...
    pi@raspberrypi:~ $ sudo cp checkwifi.sh /usr/local/bin/checkwifi.sh    
    pi@raspberrypi:~ $ sudo chmod 775 /usr/local/bin/checkwifi.sh   
    pi@raspberrypi:~ $ sudo /usr/local/bin/checkwifi.sh     
    pi@raspberrypi:~ $

    接下來就是修改 crontab, 設定每小時執行一次 checkwifi.sh, 若連線異常就重啟樹莓派 :

    pi@raspberrypi:~ $ crontab -e   
      GNU nano 2.2.6        File: /tmp/crontab.HDz47N/crontab

    # daemon's notion of time and timezones.
    #
    # Output of the crontab jobs (including errors) is sent through
    # email to the user the crontab file belongs to (unless redirected).
    #
    # For example, you can run a backup of all your user accounts
    # at 5 a.m every week with:
    # 0 5 * * 1 tar -zcf /var/backups/home.tgz /home/
    #
    # For more information see the manual pages of crontab(5) and cron(8)
    #
    # m h  dom mon dow   command
    */10 * * * * /usr/bin/python2 /home/pi/reportip2.py
    0 * * * * sudo /usr/local/checkwifi.sh    

    這樣應該就萬無一失了.  查看 crontab 日誌表可知 checkwifi.sh 在整點時有正常執行 :

    pi@raspberrypi:~ $ sudo cat /var/log/cron.log 
    .....
    Jan  1 12:50:02 raspberrypi CRON[23625]: (pi) MAIL (mailed 101 bytes of output b                                                                             ut got status 0x0001 from MTA#012)
    Jan  1 13:00:01 raspberrypi CRON[23682]: (pi) CMD (/usr/bin/python2 /home/pi/rep                                                                             ortip2.py)
    Jan  1 13:00:01 raspberrypi CRON[23684]: (pi) CMD (sudo /usr/local/checkwifi.sh)
    Jan  1 13:00:01 raspberrypi CRON[23674]: (pi) MAIL (mailed 49 bytes of output bu                                                                             t got status 0x0001 from MTA#012)
    Jan  1 13:00:02 raspberrypi CRON[23675]: (pi) MAIL (mailed 101 bytes of output b                                                                             ut got status 0x0001 from MTA#012)
    Jan  1 13:09:01 raspberrypi CRON[23708]: (root) CMD (  [ -x /usr/lib/php5/sessio                                                                             nclean ] && /usr/lib/php5/sessionclean)
    Jan  1 13:10:01 raspberrypi CRON[23751]: (pi) CMD (/usr/bin/python2 /home/pi/rep                                                                             ortip2.py)
    Jan  1 13:10:02 raspberrypi CRON[23747]: (pi) MAIL (mailed 101 bytes of output b                                                                             ut got status 0x0001 from MTA#012)
    Jan  1 13:17:01 raspberrypi CRON[23837]: (root) CMD (   cd / && run-parts --repo                                                                             rt /etc/cron.hourly)
    Jan  1 13:20:01 raspberrypi CRON[23855]: (pi) CMD (/usr/bin/python2 /home/pi/rep                                                                             ortip2.py)
    Jan  1 13:20:02 raspberrypi CRON[23851]: (pi) MAIL (mailed 101 bytes of output b                                                                             ut got status 0x0001 from MTA#012)
    Jan  1 13:30:01 raspberrypi CRON[23897]: (pi) CMD (/usr/bin/python2 /home/pi/rep                                                                             ortip2.py)
    Jan  1 13:30:02 raspberrypi CRON[23893]: (pi) MAIL (mailed 101 bytes of output b                                                                             ut got status 0x0001 from MTA#012)
    Jan  1 13:39:01 raspberrypi CRON[23920]: (root) CMD (  [ -x /usr/lib/php5/sessio                                                                             nclean ] && /usr/lib/php5/sessionclean)
    Jan  1 13:40:01 raspberrypi CRON[23965]: (pi) CMD (/usr/bin/python2 /home/pi/rep                                                                             ortip2.py)
    Jan  1 13:40:02 raspberrypi CRON[23961]: (pi) MAIL (mailed 101 bytes of output b                                                                             ut got status 0x0001 from MTA#012)

    關於 crontab 日誌紀錄開啟方式參考 :

    # 從外網以 SSH 存取樹莓派的方法

    2018-01-02 補充 :

    早上 07:00 手機收 mail 就收到樹莓派傳來的最新 IP 為 114.27.99.134 :




    這兩天觀察 IP 變換頻率不定, 昨天一天一次, 今天則已變換兩次. 每當我要連線操控鄉下的 Pi 3 時就在手機信箱中找最近一次的 [RPI] IP CHANGED 標題的郵件, 再開啟手機 App JuiceSSH 1編輯連線 IP 即可, 不須開啟電腦也能透過 Internet 控制 Pi 3, 操作如下 :

    首先按 JuiceSSH 最上方的連線 (管理您的連線) :




    按住所要編輯的連線 (此處為中間的 Raspberry Pi 3) :



    選編輯 :



    更改主機欄為最新的 IP, 然後務必按右上角的勾勾才會儲存進去 :




    按左上角的 <- 更新連線回到 JuiceSSH 首頁, 點 "常用" 中剛剛編輯好新 IP 的連線 (此處為 Raspberry Pi 3) 就會建立連線了 :



    有 JuiceSSH 真好用.

    2018-01-08 補充 :

    觀察本周收到的 IP_CHANGED 郵件如下 :




    有時候一天變化多次 (1 月 3 日就三次), 有時好幾天都不變, 例如 1/4~1/5 都沒變. 或許 1/3 的多次變化是因為自動重開機, 但上面的 Shell script 沒有紀錄重開機事件, 因此無法印證. 有空要修改程式, 在 reboot 前把時間紀錄到一個文字檔中.

    2017年12月31日 星期日

    如何更新樹莓派的 Node.js

    因為週五在明儀買的 "實戰聊天機器人Bot開發-使用Node.js" 使用 Node.js, 這個我兩年前才學一點點就沒時間學的 Javascript 聖品又重回我的眼簾, 參考 :

    # 關於 Node.js

    早上去市場時經過圖書館把 "不一樣的 Node.js" 這本書借回來, 這本書寫得不錯, 非常適合初學者, 已經出第二版了 (2015) :


    Source : Taaze


    檢查我的 Raspbian Pixel 發現已經安裝 Node.js :

    pi@raspberrypi:~ $ node -v 
    v0.10.29

    但官網可下載的 Windows 版本卻是 9.3.0 版 :

    # https://nodejs.org/en/

    會不會差太多啊! 我參考下面這篇試圖去更新 Node.js :

    # Upgrading to more recent versions of Node.js on the Raspberry Pi

    首先是更新套件清單 :

    pi@raspberrypi:~ $ sudo apt update 
    Get:1 http://mirrordirector.raspbian.org jessie InRelease [14.9 kB]
    Get:2 http://archive.raspberrypi.org jessie InRelease [22.9 kB]
    Get:3 http://mirrordirector.raspbian.org jessie/main armhf Packages [9,536 kB]
    Get:4 http://archive.raspberrypi.org jessie/main armhf Packages [171 kB]
    Get:5 http://archive.raspberrypi.org jessie/ui armhf Packages [58.9 kB]
    Get:6 http://archive.raspberrypi.org jessie/ui armhf Packages [58.9 kB]
    Ign http://archive.raspberrypi.org jessie/main Translation-en_US
    Ign http://archive.raspberrypi.org jessie/main Translation-en
    Ign http://archive.raspberrypi.org jessie/ui Translation-en_US
    Ign http://archive.raspberrypi.org jessie/ui Translation-en
    Get:7 http://mirrordirector.raspbian.org jessie/contrib armhf Packages [43.3 kB]
    Get:8 http://mirrordirector.raspbian.org jessie/non-free armhf Packages [84.2 kB]
    Get:9 http://mirrordirector.raspbian.org jessie/rpi armhf Packages [1,356 B]
    Ign http://mirrordirector.raspbian.org jessie/contrib Translation-en_US
    Ign http://mirrordirector.raspbian.org jessie/contrib Translation-en
    Ign http://mirrordirector.raspbian.org jessie/main Translation-en_US
    Ign http://mirrordirector.raspbian.org jessie/main Translation-en
    Ign http://mirrordirector.raspbian.org jessie/non-free Translation-en_US
    Ign http://mirrordirector.raspbian.org jessie/non-free Translation-en
    Ign http://mirrordirector.raspbian.org jessie/rpi Translation-en_US
    Ign http://mirrordirector.raspbian.org jessie/rpi Translation-en
    Fetched 9,916 kB in 2min 41s (61.5 kB/s)
    Reading package lists... Done
    Building dependency tree
    Reading state information... Done
    198 packages can be upgraded. Run 'apt list --upgradable' to see them.

    然後檢查套件清單中最新的 Node.js 版本 :

    pi@raspberrypi:~ $ apt list nodejs 
    Listing... Done
    nodejs/oldstable,now 0.10.29~dfsg-2 armhf [installed,automatic]

    咦? 跟我已安裝的一樣都是 0.10.29? 不管它, 直接下 install 指令去更新 Node.js  (已安裝的話就是更新) :

    pi@raspberrypi:~ $ sudo apt install nodejs 
    Reading package lists... Done
    Building dependency tree
    Reading state information... Done
    nodejs is already the newest version. 
    nodejs set to manually installed.
    0 upgraded, 0 newly installed, 0 to remove and 198 not upgraded.
    pi@raspberrypi:~ $

    還真的已經是最新版, 原來樹莓派最新的套件清單還是 0.10.29 版啊! 如果要更新到 9.3.0 版可能要用該文後面那個 curl 指令去下載官網最新版本 :

    $ curl -sL https://deb.nodesource.com/setup_9.x | sudo -E bash -   

    然後再安裝 9.3.0 版了 :  

    $ sudo apt install nodejs

    不過我決定暫時不升版, 目前這樣夠用就好, 以後有需要再升.

    參考 :

    # Installing newer version of NodeJS on Pi 3

    2018-01-02 補充 :

    今天我用高雄家裡的 RPI B 執行了上面 curl 指令失敗, 結果如下 :

    pi@raspberrypi:~ $ curl -sL https://deb.nodesource.com/setup_9.x | sudo -E bash -

    ## Installing the NodeSource Node.js v9.x repo...


    ## You appear to be running on ARMv6 hardware. Unfortunately this is not currently supported by the NodeSource Linux distributions. Please use the 'linux-armv6l' binary tarballs available directly from nodejs.org for Node.js v4 and later.

    意思好像是 Node.js v9 還未支援 ARMv6 核心.

    2018-01-05 補充 :

    今日有熱心的 Node.js 先進留言指教, 原來上面我找到的方法不管用, 網友提供的更新方法我複製整理如下 :

    1. 下載
         wget https://nodejs.org/dist/latest-v9.x/node-v9.3.0-linux-armv6l.tar.gz
    2. 解壓縮
        tar -xvf node-v9.3.0-linux-armv6l.tar.gz
    3. 切換至資料夾
        cd node-v9.3.0-linux-armv6l
    4. 移動至/usr/local/
        sudo cp -R * /usr/local/
    5. 設定NODE_PATH變數(.bashrc)
        export NODE_PATH=/usr/local/lib/node_modules
    6. 檢查版本
        node -v
        npm -v

    我依此步驟進行, 果真將 Node.js 升版至 v9.3.0, 實在太感謝這位匿名的朋友啦!

    升版過程節錄如下 :

    pi@raspberrypi:~ $ wget https://nodejs.org/dist/latest-v9.x/node-v9.3.0-linux-ar
    mv6l.tar.gz
    --2018-01-05 19:21:38--  https://nodejs.org/dist/latest-v9.x/node-v9.3.0-linux-armv6l.tar.gz
    Resolving nodejs.org (nodejs.org)... 104.20.22.46, 104.20.23.46, 2400:cb00:2048:1::6814:172e, ...
    Connecting to nodejs.org (nodejs.org)|104.20.22.46|:443... connected.
    HTTP request sent, awaiting response... 200 OK
    Length: 16930799 (16M) [application/gzip]
    Saving to: node-v9.3.0-linux-armv6l.tar.gz
     (略)
    pi@raspberrypi:~ $ tar -xvf node-v9.3.0-linux-armv6l.tar.gz    
     (略)
    pi@raspberrypi:~ $ cd node-v9.3.0-linux-armv6l
    pi@raspberrypi:~/node-v9.3.0-linux-armv6l $ sudo cp -R * /usr/local/ 
    pi@raspberrypi:~/node-v9.3.0-linux-armv6l $ export NODE_PATH=/usr/local/lib/node_modules 
    pi@raspberrypi:~/node-v9.3.0-linux-armv6l $ node -v 
    v9.3.0
    pi@raspberrypi:~/node-v9.3.0-linux-armv6l $ npm -v 
    5.5.1
    pi@raspberrypi:~/node-v9.3.0-linux-armv6l $ cd ..
    pi@raspberrypi:~ $ node
    > console.log("Hello World");
    Hello World
    undefined  (為啥會有這個東東? Windows 上沒有)

    參考 :

    # Node入門

    2018-03-15 補充 :

    關於 undefined : 此為 console.log() 的傳回值, 若無就傳回 undefined.

    2017年12月30日 星期六

    買 R 語言深入淺出財經計量等三本書

    昨天明儀通知我所訂 "R 語言深入淺出財經計量" 已到貨, 晚上去市圖還書後順路去取書, 回來稍微翻閱了一下, 嗯, 這也是我今年所買的書裡最棒的一本之一.




    買好後逛了一下, 又找到兩本不錯的書 :


    # 實戰物聯網開發-使用ESP8266



    # 實戰聊天機器人Bot開發-使用Node.js



    但買回來才知金石堂竟然打七折!

    參考 :

    # Raspberry Pi 入門與機器人實作應用
    # Python資料科學學習手冊
    # 玩轉社群:文字大數據實作
    # Google Cloud雲端平台實作手冊:Google雲端功能一點就通
    # Data Science from Scratch中文版:用Python學資料科學
    # AI及機器學習的經脈:演算法新解

    太陽光電設置班第四課

    今天載二哥去上 APCS 後順路到正修上課, 8:15 出發大約 8:45 分就到了. 今天早上是室內課, 老師花了點時間複習上禮拜的併聯型配線圖, 然後講解獨立型配線圖. 獨立型比較複雜, 比並聯型多了充電控制器與電池, 電表也多了一個, 分流器多兩個, 但少了瓦時計與比流器, 因為不用跟台電並聯. 在檢定方面考量考試時間, 獨立型不考日照計與溫度計量測.

    下午則是上頂樓實做, 主要是檢定第一站的直流接線箱配線練習, 以及第一站到第二站的拉線. 其實第一站配線是最簡單的, 複雜的第二站, 特別是獨立型. 邊做邊聊才知道, 今天跟我一組的兩位同學是遠從台北與苗栗搭高鐵下來上課的, 原來台東還不是最遠, 真是佩服. 他們倆位都有參加回訓班 (上課時間 3/3, 3/4, 3/21, 3/22 週六日, 學費 12000), 也報名明年乙級技術士檢定 (1/2~1/12 報名, 3 月學科考試, 6/5, 6/6 術科考試).

    我雖然前天在全家也買了報名表, 但今天聽黃老師說檢定主要是考熟練度, 我們這班上課內容雖然主要是檢定為主, 但練習時間不夠達到熟練程度, 考量明年菁菁要參加會考, 我也沒時間準備檢定, 所以決定不報名檢定, 也不參加回訓, 就是純粹學習而已. 上黃宏欣老師的課收穫頗多, 今天老師強調 "觀念比技術重要" 很有道理, 因為技術只要練習就會進步, 但觀念錯思考方式就有問題, 要進步就很難.

    以下整理獨立型配線, 其單線圖如下 :




    注意, 虛線表示要使用 5.5mm 電力線, 而實線使用 3.5mm 電力線.


    首先是第一站的直流接線箱 (左邊第一個紅框). 與並聯型不同的是, 獨立型模組是三串兩並, 因為獨立型之交流負載為 110V, 所以只要三個串聯即可達到所需電壓, Voc=38.85*3=116.55v, 乘以 1.25 倍 : 116.55*1.25=145.7v.

    其元件配置圖與配線如下 :




    電力線因為模組是三串兩並, 故有 4 條 4mm 的耐候線 (紅黑紅黑) 從模組進來, 先到兩組 Fuse 後進 DCS, 再出箱體到第二站的獨立型配線箱. 注意, 獨立型因電流比並聯型大, 接線箱配線都使用 5mm 的電力線. 另外, 兩組 Fuse 底下各用一條較短的電力線並接.

    接下來是第二站的配電箱, 獨立型配電箱元件配置圖如下 :




    左半部是組列側配電箱, 主要是充電控制器 (將 116v 穩壓至 24v/48v), 兩個直流電表, 以及偵測直流電流的兩個分流器, 與 NFB, DCS 開關等. 主角是充電控制器, 用來將來自三串兩並模組的 100 多伏特直流電壓降壓成 24V 的穩定電壓, 以便向額定 24V 蓄電池充電. 左上角的直流電表 1 搭配左下角分流器測得之電流用來測量模組側功率 (充控輸入端); 右方的直流電表 2 則搭配右下角分流器測得之電流用來測量電池側的功率 (充控輸出端).

    充電控制器端點 :

    PV+, PV- : 模組電壓輸入
    BAT+, BAT- : 蓄電池輸出
    G : 接地

    直流電表端點 :

    DCS24V+, DCS24V- : 電表電源
    DC+, DC- : 電壓取樣
    A+, A- : 電流取樣
    RS-485+, RS485- : 通訊

    組列側的配線圖如下 :




    注意, 單線圖中虛線部分必須使用 5.5mm 電力線, 實線部分用 3.5mm 電力線. 這是因為 260W 模組短路電路 Isc=9A, 兩組並聯為 18A, 乘以 1.25 為 22.5A, 超過 3.5mm PV 線的耐流 (導線槽 20A), 須改用 5.5mm 才行.

    配線時先接電力線, 次接電表線, 再接 RS-485 通訊線與接地線. 從直流接線箱跨站而來的 5.5mm 耐候線先到 NFB 一次側, 然後進充電控制器的 PV+, 充控的 PV- 則先接分流器上端 (比較近), 然後接 NFB 的負端形成迴路. 由於電流是從分流器上方流入, 因此上方為正電壓, 要接電表的 A+; 下方為負電壓, 要接電錶 A-. 電壓取樣部分則從 NFB 下方取電, 接到電錶的 DC+ 與 DC-. 這樣充控輸入側就接好了.

    然後是充控的輸出側, BAT+ 接到 DCS 上端下接電池; BAT- 接分流器上端 (較近), 分流器下端則回到 DCS 上方的負端形成迴路. 由於充電時電流表要顯示正, 電流是從分流器下方往上流, 因此充電時分流器下方為正, 要接到輸出側電錶的 A+, 分流器上方為負接 A-. 電壓取樣則從共接點 BAT+ 與 BAT- 接到 DC+ 與 DC-, 同時分接到兩個直流電表的電源 DC24+ 與 DC24-, 亦即兩顆直流電表都是吃電池的電. BAT+ 與 BAT- 是組列側 (A), 電池側 (B), 與負載側 (C) 的共接點, 所以要分岐下自行配管的 PVC 管路到隔壁的負載側. 三方的功率有如下關係 :

    A(組列)=B(電池)+C(負載)

    如果組列供電 2000W, 負載吃 1500W, 則可向電池充電 500W (電表 2 電流為正); 若組列供電降至 1000W, 則 B=-500W, 亦即電池要放電 500W (電表 2 電流為負).

    最後是接充控接電點到接地板, 接地線也要隨電力線下 PVC 管. 而兩個電表的 RS-485 通訊線共接後下備妥之可撓金屬管路到隔壁的負載側.

    配電箱右半部是負載側配電箱, 主要是變流器與搭配分流器的直流電表 (量負載端功率). 負載側配線圖如下 :



    組列側的電力線紅線先到 NFB 上方, 下方直接到變流器的 RED (+), 黑線則接分流器下方 (較近), 分流器上方黑線接變流器 BLACK(-) 形成迴路. 跟組列側一樣, 因為分流器接在變流器後面, 所以電流是從分流器上方 (即變流器之 BLACK) 流下來, 故上方為正要接到電錶電流取樣的 A+; 下方接 A-. 電表電源從 NFB 下方分歧接到 DC24+ 與 DC24-, 同時分岐到 DC+ 與 DC- 做電壓取樣. 亦即三個電表之電源都是吃 BAT+ 與 BAT-, 只是負載側的電表被 NFB 隔離而已.

    接著是輸出至負載端, 直流電源從變流器輸入端的 RED 與 BLACK 分岐到直流負載 NFB3, 交流電源則從變流器之 L (紅) 與 N (黑) RU, 接到 NFB4. 最後接上共接的 RS-485 通訊線即可.

    完整的獨立型配電箱配線圖如下 :




    配線看起來很複雜, 其實從單線圖看, 電路非常簡單. 比較難理解的是分流器接法, 因為單線圖顯示 NFB 或充控紅線一出來就是接分流器, 實際配線分流器卻是接在迴路後端, 只要循著電流方向辨別正負, 就知道哪一端要接 A+ 了. 但要注意, 電池側的分流器要以充電方向來思考. 

    2017年12月28日 星期四

    R 語言學習筆記 (三) : 陣列

    測完矩陣接著測試陣列, 此前的測試紀錄如下 :

    # R 語言安裝
    # 在樹莓派上安裝 R 語言
    # R 語言學習筆記 (一) : 基本語法與向量
    # R 語言學習筆記 (二) : 矩陣

    R 語言的陣列是矩陣的維度擴展 (矩陣是陣列的一個特例, 它是二維的陣列), 因此其元素一樣也都是同質的 (即資料類型必須相同). 陣列也可視為多維度的向量 (注意, R 語言中向量並無維度), 使用多個索引如 A[i, j, k, ...] 來存取元素, 其中索引 i, j, k, ... 代表各維之位置. 最常用的是三維陣列, 在一些時間序列的應用中甚至會用到高維陣列.

    與幾何類比, 向量相當於線; 矩陣相當於面; 而陣列則相當於多維空間. 不管是向量, 矩陣或陣列, 它們的元素都必須同質, 即由相同類型的資料組成.


    一. 建立陣列的方法 :

    1. 呼叫 array() 函數 : 

    函數 array() API 如下 :

    array(data=NA, dim=length(data), dimnames=NULL)

    data : 向量或串列 (用來填滿陣列), 向量以外的資料會自動被 as.vector() 強制轉型.
    dim : 整數向量, 用來依序定義各維度之長度.
    dimnames : 字串列表 (list) 用來賦予各維度因次名稱 (例如列名, 行名, 表名)

    三維陣列的第一列相當於矩陣之列, 第二維相當於行, 第三維通常稱為表 (table).  例如以下的三維陣列用 1:24 的向量來填滿 3*4*2 個元素位置, 會逐行 (by column) 填入元素, 即先填滿第一行, 再填第二行 :

    > A <- array(1:24, c(3,4,2))    #建立一,二,三維長度各為 3,4,2 之陣列
    > A
    , , 1                          #顯示第三維第一個資料結構 (相當於 table 1)

         [,1] [,2] [,3] [,4]
    [1,]    1    4    7   10
    [2,]    2    5    8   11
    [3,]    3    6    9   12

    , , 2                          #顯示第三維第二個資料結構 (相當於 table 2)

         [,1] [,2] [,3] [,4]
    [1,]   13   16   19   22
    [2,]   14   17   20   23
    [3,]   15   18   21   24

    接著來看看陣列的資料結構是用甚麼組成的 :

    > class(A)          #檢查資料結構是 array
    [1] "array"
    > is.array(A)
    [1] TRUE
    > is.vector(A)    #不是向量結構
    [1] FALSE
    > is.matrix(A)    #不是矩陣結構
    [1] FALSE
    > is.array(A[, 1 ,])   # A[ , 1, ] 是二維陣列  (降維)
    [1] TRUE
    > is.matrix(A[, 1, ])     # A[, 1, ] 也是矩陣 (降維)
    [1] TRUE
    > class(A[,1,])    # A[, 1, ] 的真實結構是矩陣
    [1] "matrix"
    > A[,1,]             
         [,1] [,2]
    [1,]    1   13
    [2,]    2   14
    [3,]    3   15
    > A[1,1,]     
    [1]  1 13
    > is.matrix(A[1,1,])      #A[1, 1, ] 則降維成向量
    [1] FALSE
    > is.vector(A[1,1,])
    [1] TRUE

    上面降維後的 A[ , 1, ] 用 is.array() 與 is.matrix() 檢查, 結果它似乎既是陣列 (二維) 也是矩陣, 用 class() 去檢驗即知事實上 A[ , 1, ] 結構是矩陣而非陣列, 可見陣列確實是用矩陣堆起來的, 而矩陣又是用向量堆起來的.

    其實 R 語言中的二維陣列就是矩陣, 例如用 array() 建立一個二維陣列, 用 is.array() 與 is.matrix() 都回應 TRUE, 表示它既是陣列也是矩陣, 但用 class() 去檢查其結構類型卻歸類為是矩陣 :

    > A <- array(1:12, c(4,3))     #建立一個二維陣列
    > A
         [,1] [,2] [,3]
    [1,]    1    5    9
    [2,]    2    6   10
    [3,]    3    7   11
    [4,]    4    8   12
    > is.array(A)       #用 array() 建立當然是陣列
    [1] TRUE
    > is.matrix(A)     #也是矩陣
    [1] TRUE
    > class(A)           #結構上歸類為矩陣
    [1] "matrix"

    如果建立一個矩陣之後用 as.array() 強制轉成陣列會怎樣呢?

    > A <- matrix(1:12, nrow=4)    #建立一個矩陣
    > A
         [,1] [,2] [,3]
    [1,]    1    5    9
    [2,]    2    6   10
    [3,]    3    7   11
    [4,]    4    8   12
    > is.matrix(A)     #用 matrix() 建立當然是矩陣
    [1] TRUE
    > is.array(A)       #嘿! A 竟然也是陣列
    [1] TRUE
    > class(A)           #結構是矩陣
    [1] "matrix"
    > B <- as.array(A)      #用 as.array() 將 A 強制轉型為陣列 B
    > B
         [,1] [,2] [,3]
    [1,]    1    5    9
    [2,]    2    6   10
    [3,]    3    7   11
    [4,]    4    8   12
    > is.matrix(B)     #還是矩陣
    [1] TRUE
    > is.array(B)       #當然是陣列
    [1] TRUE
    > class(B)           #結構仍歸類為矩陣
    [1] "matrix"

    我們可以用 identical() 函數來檢驗用 matrix() 與 array() 建立的二維物件是否雷同 :

    > A <- matrix(1:12, nrow=4)    #建立一個矩陣 A
    > A
         [,1] [,2] [,3]
    [1,]    1    5    9
    [2,]    2    6   10
    [3,]    3    7   11
    [4,]    4    8   12
    > B <- array(1:12, c(4,3))         #建立一個二維陣列 B, 內容與 A 矩陣相同
    > B
         [,1] [,2] [,3]
    [1,]    1    5    9
    [2,]    2    6   10
    [3,]    3    7   11
    [4,]    4    8   12
    > class(A)         #矩陣 A 結構為 matrix
    [1] "matrix"
    > class(B)         #陣列 B 結構亦為 matrix
    [1] "matrix"
    > identical(A, B)    #呼叫 identical() 顯示兩者是同樣物件
    [1] TRUE

    總之, 在 R 語言中, 大於二維的資料結構是陣列, 二維陣列就被歸類為矩陣.

    如果傳入的向量不夠填滿陣列, 則會套用循環規則從頭抓資料, 例如 :

    > array(1:17, c(3,4,2))
    , , 1

         [,1] [,2] [,3] [,4]
    [1,]    1    4    7   10
    [2,]    2    5    8   11
    [3,]    3    6    9   12

    , , 2

         [,1] [,2] [,3] [,4]
    [1,]   13   16    2    5
    [2,]   14   17    3    6
    [3,]   15    1    4    7

    下面是用這 24 個元素來建立一個四維陣列 :

    > A <- array(1:24, c(3,2,2,2))
    > A
    , , 1, 1

         [,1] [,2]
    [1,]    1    4
    [2,]    2    5
    [3,]    3    6

    , , 2, 1

         [,1] [,2]
    [1,]    7   10
    [2,]    8   11
    [3,]    9   12

    , , 1, 2

         [,1] [,2]
    [1,]   13   16
    [2,]   14   17
    [3,]   15   18

    , , 2, 2

         [,1] [,2]
    [1,]   19   22
    [2,]   20   23
    [3,]   21   24

    五維陣列範例如下 :

    > A <- array(1:32, c(2,2,2,2,2))
    > A
    , , 1, 1, 1

         [,1] [,2]
    [1,]    1    3
    [2,]    2    4

    , , 2, 1, 1

         [,1] [,2]
    [1,]    5    7
    [2,]    6    8

    , , 1, 2, 1

         [,1] [,2]
    [1,]    9   11
    [2,]   10   12

    , , 2, 2, 1

         [,1] [,2]
    [1,]   13   15
    [2,]   14   16

    , , 1, 1, 2

         [,1] [,2]
    [1,]   17   19
    [2,]   18   20

    , , 2, 1, 2

         [,1] [,2]
    [1,]   21   23
    [2,]   22   24

    , , 1, 2, 2

         [,1] [,2]
    [1,]   25   27
    [2,]   26   28

    , , 2, 2, 2

         [,1] [,2]
    [1,]   29   31
    [2,]   30   32

    可見 R 語言在顯示高維陣列內容時, 是把它拆解到矩陣 (就是第一與第二維組成的二維陣列) 當作最小的單位, 然後從第三維起依序顯示 "每一張表".

    2. 呼叫 dim() 函數設定維度將向量或列表變成陣列 :   

    R 語言中向量 (vector) 是一群有序資料, 看起來像是一維, 但在 R 裡面是沒有維度的資料結構, 用 dim() 檢查會得到 NULL. 與建立矩陣一樣, 若呼叫 dim() 為向量指定大於 2 的維度, 則此無維度的向量將變成有維度的陣列結構, 例如 :

    > v <- 1:24                 #建立 24 個元素的向量
    > dim(v)                         #向量是無維度的資料結構 (NULL)
    NULL 
    > dim(v) <- c(4,3,2)   #指定維度給向量會使其變成矩陣 (2 維) 或向量 (大於 2 維)
    > v
    , , 1

         [,1] [,2] [,3]
    [1,]    1    5    9
    [2,]    2    6   10
    [3,]    3    7   11
    [4,]    4    8   12

    , , 2

         [,1] [,2] [,3]
    [1,]   13   17   21
    [2,]   14   18   22
    [3,]   15   19   23
    [4,]   16   20   24

    > class(v)                       #變成陣列了
    [1] "array"

    > dim(v)                         #查詢陣列維度是 4*3*2
    [1] 4 3 2


    二. 存取陣列元素 :

    存取陣列元素的方式與矩陣一樣, 也是用索引, 只是索引比較多而已.

    > A <- array(1:24, c(4,3,2))
    > A
    , , 1

         [,1] [,2] [,3]
    [1,]    1    5    9
    [2,]    2    6   10
    [3,]    3    7   11
    [4,]    4    8   12

    , , 2

         [,1] [,2] [,3]
    [1,]   13   17   21
    [2,]   14   18   22
    [3,]   15   19   23
    [4,]   16   20   24

    > A[1,1,1]
    [1] 1
    > A[1,3,1]
    [1] 9
    > A[1,3,2]
    [1] 21
    > A[1,1,]
    [1]  1 13

    > A[1,,]
         [,1] [,2]
    [1,]    1   13
    [2,]    5   17
    [3,]    9   21

    > A[,1,]
         [,1] [,2]
    [1,]    1   13
    [2,]    2   14
    [3,]    3   15
    [4,]    4   16

    > A[,,1]
         [,1] [,2] [,3]
    [1,]    1    5    9
    [2,]    2    6   10
    [3,]    3    7   11
    [4,]    4    8   12

    注意, 這些取出來的物件都降階變成了二維的矩陣了, 可用 class() 檢驗. 如果取出來的元件想保留 array 架構, 不要降階成矩陣, 可以在索引最後面加上 drop=FALSE (預設是 TRUE), 例如 :

     > A <- array(1:24, c(4,3,2))
    > A[1,,]               #只取第一維
         [,1] [,2]
    [1,]    1   13
    [2,]    5   17
    [3,]    9   21
    > class(A[1,,])     #降階為 matrix
    [1] "matrix" 
    > A[1,,,drop=FALSE]    #不降階 (保留陣列結構)
    , , 1

         [,1] [,2] [,3]
    [1,]    1    5    9

    , , 2

         [,1] [,2] [,3]
    [1,]   13   17   21

    > class(A[1,,,drop=FALSE])    #仍為 array 結構
    [1] "array"

    > identical(A[1,,], A[1,,,drop=FALSE])  #兩個是不同物件 (內容相同, 但結構不同)
    [1] FALSE 

    上面是存取三維陣列內容時的降維情形, 如果取得的內容是二維, 則降階為矩陣; 若小於二維則為向量. 從多維陣列取得的內容如果大於二維, 則仍然是陣列結構, 例如 :

    > A <- array(1:32, c(2,2,2,2,2))
    > A
    , , 1, 1, 1

         [,1] [,2]
    [1,]    1    3
    [2,]    2    4

    , , 2, 1, 1

         [,1] [,2]
    [1,]    5    7
    [2,]    6    8

    , , 1, 2, 1

         [,1] [,2]
    [1,]    9   11
    [2,]   10   12

    , , 2, 2, 1

         [,1] [,2]
    [1,]   13   15
    [2,]   14   16

    , , 1, 1, 2

         [,1] [,2]
    [1,]   17   19
    [2,]   18   20

    , , 2, 1, 2

         [,1] [,2]
    [1,]   21   23
    [2,]   22   24

    , , 1, 2, 2

         [,1] [,2]
    [1,]   25   27
    [2,]   26   28

    , , 2, 2, 2

         [,1] [,2]
    [1,]   29   31
    [2,]   30   32

    > A[,,,1,1]     #取得子陣列
    , , 1

         [,1] [,2]
    [1,]    1    3
    [2,]    2    4

    , , 2

         [,1] [,2]
    [1,]    5    7
    [2,]    6    8

    > class(A[,,,1,1])   #結構是陣列 (因為是三維)
    [1] "array"

    取得陣列內容時也可以使用負索引來排除不要的部分, 例如 :

    > A <- array(1:24, c(4,3,2))
    > A
    , , 1

         [,1] [,2] [,3]
    [1,]    1    5    9
    [2,]    2    6   10
    [3,]    3    7   11
    [4,]    4    8   12

    , , 2

         [,1] [,2] [,3]
    [1,]   13   17   21
    [2,]   14   18   22
    [3,]   15   19   23
    [4,]   16   20   24

    > A[-3, c(1,3), 2]     #第一維不要第三列, 第二維取第一與第三行, 第三維取第二張
         [,1] [,2]
    [1,]   13   21
    [2,]   14   22
    [3,]   16   24

    修改陣列的內容只要直接將向量或矩陣指派給陣列的指定部分即可, 例如 :

    > A <- array(1:24, c(4,3,2))   #建立陣列
    > A
    , , 1

         [,1] [,2] [,3]
    [1,]    1    5    9
    [2,]    2    6   10
    [3,]    3    7   11
    [4,]    4    8   12

    , , 2

         [,1] [,2] [,3]
    [1,]   13   17   21
    [2,]   14   18   22
    [3,]   15   19   23
    [4,]   16   20   24

    > A[3:4,c(1,3),1]    #第一維之 3~4 列, 第二維的 1 與 3 行, 第三維的第一張表
         [,1] [,2]
    [1,]    3   11
    [2,]    4   12
    > A[3:4,c(1,3),1] <- matrix(NA, nrow=2, ncol=2)   #將矩陣指派給陣列局部
    > A
    , , 1

         [,1] [,2] [,3]
    [1,]    1    5    9
    [2,]    2    6   10
    [3,]   NA    7   NA
    [4,]   NA    8   NA

    , , 2

         [,1] [,2] [,3]
    [1,]   13   17   21
    [2,]   14   18   22
    [3,]   15   19   23
    [4,]   16   20   24


     三. 陣列常用函數 : 


     函數 說明
     array(data, dim, dimnames) 建立維度為 dim 向量之陣列
     str(x) 顯示物件結構與內容
     length(x) 顯示陣列元素個數
     dim(x) 顯示物件 (矩陣, 陣列, 資料框) 維度
     is.array(x) 檢查是否為鎮ㄌㄧ類型
     as.array(x) 將物件強制轉型為陣列陣類型
     identical(x, y) 檢查兩個物件 x, y 是否為相同物件


    2017年12月27日 星期三

    糞金龜與聖甲蟲

    今天跟同事談到以前我們鄉下常見的糞金龜, 通常都躲在牛糞堆裡努力做糞球, 他們都市孩子根本沒見過. 我去維基查了一下, 原來牠竟然就是埃及人崇拜的聖甲蟲哩! 因為糞金龜會製作圓滾滾的糞球, 就好像他們信仰的太陽神會做出圓圓的太陽, 糞金龜因此被神化, 參考 :

    # https://zh.wikipedia.org/wiki/蜣螂

    令我驚訝的是, 糞金龜是自然界最大力的生物, 牠們可以舉起自己體重 1141 倍的糞球! 而且他們是利用月光太陽光的偏振來導航, 將糞球推回家. 甚麼叫偏振呢? 參考 :

    # https://zh.wikipedia.org/wiki/偏振

    總之, 縱波 (例如聲波與地震波) 沒有偏振, 橫波 (例如電磁波) 才會有偏振現象. 電磁波中電場與磁場與波前進方向垂直, 而兩者又互相垂直 (正交). 如果電磁場都像下面這樣不偏不倚以正交方式前進的話就沒有偏振 :




    如果電場與磁場會旋轉或轉向就叫做有偏振, 偏振的形式有多種, 有線偏振, 圓偏振, 或橢圓偏振, 端看旋轉方式在前進方向平面的投影形狀而定 :




    我在 Youtube 找到兩部很有意思的糞金龜影片 :

    # 糞金龜的真實面貌



    # 聖甲蟲傳奇 【下課花路米 286】




    原來雄的糞金龜有角 (三支), 而雌的沒有角. 而且製作與滾糞球的是雄糞金龜, 糞球做得大就容易求偶, 難怪牠們這麼努力!

    參考 :

    # 大自然的清道夫-糞金龜

    關於入聲調標註問題

    老張寄來一份關於入聲調標註的文件, 要我評估可行與否, 因最近較忙無暇細看, 故先將其中網上資料部分記錄如下 :

    # 【煨番薯】彭瑞珠原作
    # 關於入聲字表的說明
    # 入声字自动标注工具

    另有一篇應該是湘雲的作品 "石牯情緣" docx 檔, 須以客語發音才能了解.

    還有一篇中央大學客家語文研究所徐翠真的碩士論文 "四縣客家話入聲字研究", 有 200 頁之多, 要花一點時間來看.

    語言學與網頁設計都好久沒摸了, 坦白說有點吃力.

    對岸這個入聲字自動標註工具好用, 我把 "石牯情緣" 前三段貼上去 :

    "時間過還遽,埋頭佇研究室打電腦,無感覺到,一日又會過忒咧。 探頭看向窗門外背,日頭偏西,王椰 樹搖啊搖,像同捱揲手,喊捱毋好恁拼,好放下手頭任做都做毋忒个事情,出去外背行行啊仔。乜好,恁久無運動咧,這幾日都落水,落到人、鳥仔摎蟲仔通棚都囥嶐孔樣少行出來,趕今晡日下晝天時恁好,來出去曬一下日頭,行行看看仔乜好 。
     學校後山有一條路做得直直蹶到八卦山山頂,一路係上崎,一路係梯碫,爬到山崠頂,没汗流脈落, 也乜氣賒激激。毋過來到崗頂,鼻到花香草香,看到山下一大片恁闊 个風景,先先个腳軟身痶也無算麼介咧。
     來到飛機公園,一台老飛行機像雞嫲孵卵 樣跍佇草坪頂,仰頭看 向天頂,堵好兩台訓練用个現代飛行機飛等過,這隻「老雞嫲」也像緬懷當年樣仔,還想做過一飆沖天,飛到高高个雲頂看遍世界,毋過這下煞分大索綯核核鎖到地泥頂,樣般想飛也飛毋起來咧。"

    按第一個按鈕 (標準模式) 下方就出現標註結果, 入聲字底下會有紅色底線 :

    入声字自动标注 输出结果:
    時間過還遽,埋頭佇研究室打電腦,無感覺到,一日又會過忒咧。 探頭看向窗門外背,日頭偏西,王椰 樹搖啊搖,像同捱揲手,喊捱毋好恁拼,好放下手頭任做都做毋忒个事情,出去外背行行啊仔。乜好,恁久無運動咧,這幾日都落水,落到人、鳥仔摎蟲仔通棚都囥嶐孔樣少行出來,趕今晡日下晝天時恁好,來出去曬一下日頭,行行看看仔乜好 。
     學校後山有一條路做得直直蹶到八卦山山頂,一路係上崎,一路係梯碫,爬到山崠頂,没汗流脈落, 也乜氣賒激激。毋過來到崗頂,鼻到花香草香,看到山下一大片恁闊 个風景,先先个腳軟身痶也無算麼介咧。
     來到飛機公園,一台老飛行機像雞嫲孵卵 樣跍佇草坪頂,仰頭看 向天頂,堵好兩台訓練用个現代飛行機飛等過,這隻「老雞嫲」也像緬懷當年樣仔,還想做過一飆沖天,飛到高高个雲頂看遍世界,毋過這下煞分大索綯核核鎖到地泥頂,樣般想飛也飛毋起來咧。

    現在問題是入聲字雖然標出來了, 但卻無法連結發音, 紅色的底線如果能加上超連結就完美了. 老張要我評估能否將標註的結果加工一下, 可連結到萌典去聽發音, 如【煨番薯】所示.

    觀察此標註工具網頁原始碼, 輸入框是一個 id 為 mf_ipt 的 textarea 元件 :

    <textarea name=mf_ipt id=mf_ipt cols=50 rows=10></textarea>

    底下有三個按鈕, 按下去會分別呼叫 Javascript 函數 MarkWords(0), MarkWords(1), 以及 ResetWords()  :

    <div class="rsz_btn"><button onclick="MarkWords(0);">查询入声字(标准检查模式)</button></div>
    <div class="rsz_btn"><button onclick="MarkWords(1);">查询入声字(使用平水韵检查模式)</button></div>
    <div class="rsz_btn"><button onclick="ResetWords();">清除</button></div>

    底下則是 id 為 rsz_output 的輸出區塊 :

    <div class="rsz_opt"><div id="rsz_output"></div>

    呼叫 MarkWords(0) 時此函數會讀取輸入框 mf_ipt 之內容, 如果有輸入內容就呼叫 CheckRSZ() 函數, 它會依據傳入的第一參數 pi_iType 是 0 (標準) 或 1 (平水韻) 去搜尋存放在變數 g_strRSZ (標準) 或 g_strPS (平水韻) 中的入聲字表來辨認是否為入聲字, 如果是入聲字就給這個字加上底線與紅色樣式, 加工後的字串放在變數 t_strOpt 裡 :

    <div class=result>"+t_strOpt+"</div>

    然後再用 innerHTML 將此標註結果字串 t_strOpt 寫入輸出區塊 rsz_output 內 :

    document.getElementById("rsz_output").innerHTML = "<div class=output><div class=tips><strong>入声字自动标注 输出结果:</strong></div><div class=result>"+t_strOpt+"</div><div class=for_print>入声字自动标注工具: <span class=b>https://www.xiaohui.com/rsz</span></div></div><div class=print><a href='#' onclick='OnPrint();'>打印标注结果</a></div>";

    初步研究我覺得應該可行.

    至於第二個按鈕的平水韻, 我查了維基, 原來是宋代之後的詩韻系統 :

    # https://zh.wikipedia.org/wiki/平水韻

    平水之名據說源自金朝聲韻家劉淵所作 "壬子新刊禮部韻略",  劉淵為山西平水人因而得名; 又有一說為出自金國人王文鬱所作 "新刊韻略", 尚無定論.

    2018-08-17 補充 :

    老張邀我今晚登門一敘, 討論如何將入聲調標註後連結萌典, 因 9 月初要用, 近日集中時間解決此問題.

    2018-08-20 補充 :

    這個功能其實很簡單, 只要將 MarkWords() 中的變數 t_strOpt 改成如下即可 :

    t_strOpt += "<a href='https://www.moedict.tw/:" + t_strChar +
        "' target='checked_tone'>" + t_strChar + "</a>";

    修改後的網頁暫時放在下列免費伺服器測試 :

    # https://tony1966.000webhostapp.com/test/checked_tone.htm