Displaying Raspberry Pi CPU temperature in Emacs mode-line
ラズベリーパイで実行している Emacs の mode-line に CPU 温度を表示する方法
Emacs Lisp Code
(defun cpu-temp-string () (substring (shell-command-to-string "vcgencmd measure_temp") 5 11)) (setq battery-status-function #'(lambda () (cons (cons ?D ; add "%D" as CPU temperature (cpu-temp-string)) (battery-upower)))) (setq battery-mode-line-format "%D ") (display-battery-mode 1)
背景
Raspberry Pi Zero 2 W をモニター・キーボードなしで運用しています。メンテナンスは PC やスマホから SSH でログインして行っています。Raspberry Pi Zero 2 は CUI モードの Emacs が利用できるくらいの速度は出るので、それでコードを書き、github に保存しています。重いコードを走らせた時など CPU の温度が気になりますが、いちいちコマンドで確認するのでは大変なので、Emacs のモードラインに表示できるようにしました。
解説
display-time の方を改造しようと思ったのですが、display-battery-mode の方が簡単だったのでこちらを使用します。
display-time と display-battery-mode のどちらを先に実行するかで、モードライン上の左右の位置が変わるようです。
display-battery-mode
バッテリーの状態をモードラインに表示したり止めたりする Emacs の関数です。
モードラインに表示されるテキストは battery-mode-line-format と battery-status-function で制御できます。
battery-mode-line-format
モードラインに表示する文字列をフォーマットする制御文字列を持つ Emacs の変数です。
%d がバッテリーの温度に割り当てられてるので、%D を CPU の温度として使います。
battery-status-function
バッテリーの状態情報を取得する関数を持つ Emacs の変数です。
その関数は変換の定義の alist を返さなければなりません。
その各々のコンスセルは (CONVERSION . REPLACEMENT-TEXT) という形式です。
CONVERSION は変換指定の文字のコードで、制御文字列の中で % に続けられるものです。
この変数にはデフォルトでは battery-upower 関数が指定されていますので、その結果の先頭に %D として CPU の温度を付け足すラムダ式表記の関数をセットします。
文字 D のコードは Emacs Lisp では ?D で表されます。
vcgencmd measure_temp
Raspberry Pi のコマンドで、CPU (SoC)の温度を出力します。
temp=61.2'C のような文字列を出力します。
Emacs Lisp からは shell-command-to-string 関数でこのコマンドを実行し、出力を文字列として受け取ります。
その文字列の (0 から数えて) 5文字目 から 11文字目(最後の直後)の前までを substring 関数で切り出すと "61.2'C" となります。
それを返すように独自の関数 cpu-temp-string を定義します。
℃ヲタ用
℃ーしても 'C じゃなくて ℃ がいい人は cpu-temp-string を下記に差し替えてください。
(defun cpu-temp-string () (concat (substring (shell-command-to-string "vcgencmd measure_temp") 5 9) "℃"))
華(Fahrenheit)ヲタ用
hint: format, string-to-number
Windows で cmake ポリシー CMP0167 後の boost ライブラリディレクトリの取得方法
Windows で boost と cmake を使っています。以下のように boost のライブラリディレクトリを取得してリンクディレクトリに追加していました。
find_package(Boost 1.87 REQUIRED COMPONENTS program_options)
link_directories(${Boost_LIBRARY_DIRS})
cmake を 3.31 にアップグレードしたところ、CMakeLists.txt で find_package(Boost ...) にて、 boost のライブラリディレクトリが、 cmake の変数 Boost_LIBRARY_DIRS に取得できないという問題が発生しました。
cmake のポリシー CMP0167 に関係するらしく、cmake_policy(SET CMP0167 OLD) で互換モードに設定すれば回避できるのですが、互換モードは cmake の将来のバージョンでは削除されるかもしれないということで他の回避方法を探しました。
cmake 変数 Boost_DIR が BoostConfig.cmake のあるディレクトリのパス(../lib/cmake/Boost-1.xx.0/) を持っているので、その親の親のディレクトリを使用すれば良いという結論になりました。
念のため、その親の親のディレクトリ内の libboost_*.lib にマッチするファイルの数をカウントして 0 でないときだけ採用します。
find_package(Boost ...) の後に以下を追加すれば OK です。
if (WIN32)
if ("${Boost_LIBRARY_DIRS}" STREQUAL "")
message(STATUS "Check grandparent of Boost_DIR (${Boost_DIR})...")
get_filename_component(Boost_PARENT_DIR ${Boost_DIR} DIRECTORY)
get_filename_component(Boost_GRANDPARENT_DIR ${Boost_PARENT_DIR} DIRECTORY)
message(DEBUG "Boost_GRANDPARENT_DIR: ${Boost_GRANDPARENT_DIR}")
file(GLOB FileList "${Boost_GRANDPARENT_DIR}/libboost_*.lib")
message(TRACE "FileList: ${FileList}") # 長いよ
list(LENGTH FileList FileCount)
message(STATUS "libboost file count: ${FileCount}")
if (FileCount)
list(APPEND Boost_LIBRARY_DIRS ${Boost_GRANDPARENT_DIR})
message(STATUS "Boost_LIBRARY_DIRS: ${Boost_LIBRARY_DIRS}")
endif()
endif()
if ("${Boost_LIBRARY_DIRS}" STREQUAL "")
message(FATAL_ERROR "Boost_LIBRARY_DIRS is empty.")
endif()
endif (WIN32)
Windows 上の Emacs で M-x compile により、最近の Visual Studio 2022 のコマンドライン版 C++ コンパイラを使用すると文字化けするので対策
Windows 上で Emacs で M-x compile でコンパイルしてます。
Visual Studio 2022 の最近のバージョンではコマンドラインツール出力の文字コーディングが UTF-8 になりました。
CP932 (ShiftJIS) でバッチファイルを組んであり、そこから vsvarsall.bat と msbuild.exe を呼び出すようにしているので、メッセージのコーディングが混在し Emacs では文字化けするようになってしまいました。
その対策です。
以下を ~/.emacs 等に追加して再起動します。
;; Visual Studio 2022 でのビルドのためにコンパイルプロセスのコーディングシステムを切り替える ;; ;; 切り替えは以下の文字列パターンによる ;; ;; /Visual Studio 20[2-9][0-9]/ (vcvarsall.bat が出力するメッセージ) ;; /^INFO: The following lines are encoded in utf-8-dos./ (自作スクリプトに埋め込む用) ;; /^INFO: The following lines are encoded in cp932-dos./ (自作スクリプトに埋め込む用) ;; ;; 切り替えは瞬時には行われないため、直後にメッセージを出力するときは "sleep 1" を実行するなどして ;; 少し待って欲しい。 ;; (defun vs2022-compilation-filter () (let ((process (get-buffer-process "*compilation*"))) (when (save-excursion (goto-char compilation-filter-start) (re-search-forward "Visual Studio 20[2-9][0-9]" nil t)) (set-process-coding-system process 'utf-8-dos 'utf-8-dos)) (save-excursion (goto-char compilation-filter-start) (when (re-search-forward "^INFO: The following lines are encoded in " nil t) (save-excursion (when (re-search-forward "utf-8-dos" nil t) (set-process-coding-system process 'utf-8-dos 'utf-8-dos))) (save-excursion (when (re-search-forward "cp932-dos" nil t) (set-process-coding-system process 'cp932-dos 'cp932-dos))))))) (add-hook 'compilation-filter-hook 'vs2022-compilation-filter)
vcvarsall.bat が出力するメッセージ中の "Visual Studio 2022" に反応して、コンパイルプロセスのコーディングシステムを UTF-8 に切り替えるようにしました。前のバージョンの Visual Studio 2019 も使用しているのですが、こちらは従来通りですので、年号部分は 2020 から 2099 までに反応するように正規表現で指定しています。
また、msbuild の呼び出しから戻り、その後は CP932 でメッセージを出力することがあるため、以下の文字列をバッチファイルから出力することにし、これに反応してコンパイルプロセスのコーディングシステムを CP932 に戻すようにしました。
INFO: The following lines are encoded in cp932-dos.
git stash save -u で保存した stash を消しちゃったときに、そこに入っている untracked なファイルを復元する方法
git stash を消しちゃったときの復元方法は書いてる人がいますが、それだと git stash save -u で保存した untracked なファイルまでは復元できなかったので、復元できる方法を書いておきます。
git fsck --unreachable | grep commitで unreachable な commit の ID を得る。それらの中から
git show --summary <ID>で各 commit の 日付やメッセージを見て探す。沢山あるならツールを駆使して探しましょう。見つけたら
git cat-file -p <ID>で parent と書いてある ID を取得する。それらの中から
git show --summary <ID>で概要を見て探す。 untracked files on ... というメッセージがあればそれ。ファイル名も分かる。git show <ID>:<file>で表示できるので、ファイルにリダイレクトすればOK。 Windows の場合は文字コードや改行コードの変換が必要かもしれない。
あなたの log 関数は何ですか?
プログラミング言語などで log が常用対数(底は10)なのか自然対数(底はe)なのかという問題です。
導入
は
の逆関数で、この b は底(てい)と呼ばれます。英語では base です。
底は 1 以外の正の数でなければなりませんが、実際に使われるのは 10, e, 2 くらいです。
x は正の数です。複素数にも拡大できますが、多価関数となりややこしくなりますので、ここでは止めておきます。
は常用対数、
は自然対数と呼ばれます。
この e はネイピア数と呼ばれる定数で、定義は
あるいは
で、16桁の近似値は 2.718281828459045 (鮒一鉢二鉢一鉢二鉢至極美味しい)です。
"自然対数の底" という循環定義的な呼び方をされることも多いです。
10 や e の底はよく省略されます。省略されているとき、それが常用対数であるか、自然対数であるかは分野によります。
常用対数を log で表したとき、自然対数は ln で表されます。
本題
ほとんどのプログラミング言語では log は自然対数です。FORTRAN, COBOL, Common Lisp, PL/I, C, VBA を確認しました。最近の言語は C に合わせているので、それらも同じです。
常用対数は log10 で用意されていることも、log(x, 10) が使用できる場合もあります。そうでないときでも底の変換公式
により、log(x) / log(10.0) で計算できます。
例外的なのは Pascal で、自然対数が ln で用意されています。Pascal の先祖の Algol も同様のようです。
一方 Excel ワークシート関数では LOG(x,b) で b の省略時 10 となるため、LOG は常用対数に見えます。自然対数 LN も用意されています。google スプレッドシートでも同じでした。
SQL は統一されてません。注意してください。
atof の桁数限定高速版
atof をどうしても高速化したくて、桁数を限定すれば高速化できるのではないかと思い、整数部9桁、小数部9桁に限定したものを作ってみました。
9桁というのは int や unsigned int で完全に表現できる最大の桁数です。なるべく整数で演算を行って速度を稼いでいます。
isdigit_fast, pow10n は先に投稿したものを使用しています。
std::atof より 37% 速いという結果が出ています。
実際は 14桁.10桁に対応し、オーバーフローは例外を投げ、さらにコンマをスキップする機能を持ったものを使用しています。
#include "isdigit.h" // isdigit_fast #include "pow10n.h" // pow10n /// 整数部9桁、小数部9桁までの小数点表記(fフォーマット)文字列を double に変換する double atof99(const char* str) { int sign = 1; // 符号 unsigned int u = 0; // 整数部 unsigned int l = 0; // 小数部 int lc = 0; // 小数部桁数 while (*str == ' ') ++str; if (*str == '-') { sign = -1; ++str; } while (isdigit_fast(*str)) { u = u * 10 + *str - '0'; ++str; } if (*str == '.') { ++str; while (isdigit_fast(*str)) { l = l * 10 + *str - '0'; ++lc; ++str; } } return sign * (u + l * double(pow10n(-lc))); }
P.S. lc は小数部桁数の -1倍にした方が neg 1個分速そうな気がしてきた。
10 の整数乗
10 の整数乗を求める関数で、[-32, 31] 内の int n に対して高速なものを作りました。
if ((np32 & ~63) == 0) のところですが、普通は if (0 <= np32 && np32 <= 63) と書くところです。後者だと cmp (整数比較)と条件ジャンプの組2つにコンパイルされるのに対し、前者は test (ビットand)と条件ジャンプの組1つで済みます。ちょっとでも速くしたかったので、前者を使用しています。テーブルの範囲もビット演算1回で済むように決めました。
コンパイラが最適化してくれてもいいと思うのですが、Visual Studio 2019 の C++ ではやってくれませんでした。もしかしたらコンパイルオプションで指定できるのかもしれません。
// <pow10n.h> #ifndef pow10n_h #define pow10n_h #include <cmath> //------------------------------------------------------------ // // pow10n - 10 の整数乗 // /// [-32, 31] 内の int n に対する pow(10, n) のテーブル extern const double pow10n_table[31-(-32)+1]; /// [-32, 31] 内の int n に対して高速な 10 の整数乗 inline double pow10n(int n) { extern const double pow10n_table[31-(-32)+1]; int np32 = n + 32; if ((np32 & ~63) == 0) // (0 <= np32 && np32 <= 63) return pow10n_table[np32]; return std::pow(10, n); } //------------------------------------------------------------ #endif // pow10n_h // end of <pow10n.h>
// <pow10n.cpp> #include "pow10n.h" //------------------------------------------------------------ // // pow10n - 10 の整数乗 // // [-32, 31] 内の int n に対する pow(10, n) のテーブル const double pow10n_table[31-(-32)+1] = { 1e-32, 1e-31, 1e-30, 1e-29, 1e-28, 1e-27, 1e-26, 1e-25, 1e-24, 1e-23, 1e-22, 1e-21, 1e-20, 1e-19, 1e-18, 1e-17, 1e-16, 1e-15, 1e-14, 1e-13, 1e-12, 1e-11, 1e-10, 1e-9, 1e-8, 1e-7, 1e-6, 1e-5, 1e-4, 1e-3, 1e-2, 1e-1, 1e+0, 1e+1, 1e+2, 1e+3, 1e+4, 1e+5, 1e+6, 1e+7, 1e+8, 1e+9, 1e+10, 1e+11, 1e+12, 1e+13, 1e+14, 1e+15, 1e+16, 1e+17, 1e+18, 1e+19, 1e+20, 1e+21, 1e+22, 1e+23, 1e+24, 1e+25, 1e+26, 1e+27, 1e+28, 1e+29, 1e+30, 1e+31, }; //------------------------------------------------------------ // end of <pow10n.cpp>
