われがわログ

最適化アルゴリズムとかプログラミングについて書きたい

C++勉強メモ(文字コード変換&改行コード)

文字コード変換と改行コード周りで勉強したことをメモ。Windowsだけ、あるいはLinuxだけ使うならあまり考えなくてもよいが、両方対応しようとしたらちょっと面倒だったのでメモしておく。なお、本稿途中のコードでは、最後に掲載するコードをライブラリとして使っている。

UTF8のテキストを文字化けなくコンソールに出力する

Windowsだと多くの環境では文字化けするのでSJISに直す(ふと思ったが、海外版のWindowsだったらデフォルトエンコーディングどうなってるんだろう、、)。

utf8_to_term_enc(line)

Powershell Core (v.7.1.3)なら

[console]::OutputEncoding = New-Object System.Text.UTF8Encoding

とすればUTF8でも文字化けしないが、SJISで出力しておいた方が無難だろう。

ちなみに、コマンドプロンプトWindows Powershellのときはchcp 65001と入力するとUTF8を文字化けなく表示できる。

getlineの出力をtrimする

改行コードがCRLFのテキストをgetlineで読み込むと、Linuxの場合最後に'\r'がつくのでこれをtrimする。

std::ifstream input_file("input-utf8.txt");
std::string line;
while (getline_rtrim(input_file, line)) {
    cout << line << endl;
}

上2つの合わせ技

UTF8のテキストを読込&ターミナルに出力する場合は、以下のようにしておけばWindows, Linuxで同じ結果が得られる。

std::ifstream input_file("input-utf8.txt");
std::string line;
while (getline_rtrim(input_file, line)) {
    cout << utf8_to_term_enc(line) << endl;
}

LinuxでBOM付きのUTF-8を出力する

計算結果をCSVファイルに出力する際、LinuxだとエンコーディングUTF-8になってしまいExcelで開くと文字化けする。これを防止するために、CSVファイルの最初にBOMをいれる。

std::ofstream ofs("result.csv");
add_utf8_bom(ofs);

ライブラリでは、Linuxの場合ICUを使ってUTF8⇔SJIS変換を行うようにしているので、こっちを使ってもよいかも。

utf8_to_sjis("utf8 string");
sjis_to_utf8("sjis string");

210811_追記

ICUを使うと文字エンコーディングの判定ができたのでライブラリに入れてみた。

auto results =
    chardet("エンコーディングが判定できるくらい長い文章を入れる").value();
cout << "Detected encoding: " << results.enc << ", "
        << results.confidence << endl;
// Detected encoding: UTF-8, 100

ライブラリ

github.com

QMK Firmwareが焼けるマクロパッド「YMD09」を買った

自分でファームウェア(QMK Firmware)を焼けるマクロパッド「YMD09」を購入したのでメモしておく。

ja.aliexpress.com

Teamsのミュートショートカット(Ctrl+Shift+M)を押せるマクロパッドを探しており、最初はStream DeckかKoolertronのマクロパッドを買おうかと思っていた。だが、Stream Deckは値段が高く、かつ設定がPCに保存されるそうなので却下した。リモートワーク用のPCにも使いたいので、通常のキーボードとして認識してくれることが望ましい。

Koolertronのマクロパッドはその点申し分ないが、設定ソフトウェアがプロプライエタリなため購入に踏み切れなかった。

その後見つけたのがYMD09だ。YMD09は9キーのマクロパッドであり、キーボード向けのオープンソースファームウェア「QMK Firmware」をインストールできる。しかも、QMKの設定を簡単に変更できるVIA(or Remap)にも対応しており、使い勝手もよい。 さらに、完成品にもかかわらず約3000円と値段も安く、購入に踏み切った次第だ。

手元に届いた状態でキーボード用ファームウェアが既に焼かれていたが、今となっては何が入っていたのかよくわからない。 分解した後、Reset用のスルーホールをショートさせてブートローダーを起動し、以下のページからダウンロードできるVIAのファームウェアQMK ToolboxでYMD09にインストールした。

caniusevia.com

あとは、以下のページを読みつつ設定を進めた。

salicylic-acid3.hatenablog.com

salicylic-acid3.hatenablog.com

設定をしていく中で一つ問題が生じた。レイヤーのインジケータとしてRGB LEDを使いたかった(レイヤー1だったら青く光らせるなど)のだが、VIA、Remapでは設定不可だった。ググったところ、QMKの設定を変更すれば対応できるとのことだった。QMKの設定変更というと難しく感じるが、QMKの内部機能に手を入れる必要はない。設定ファイルを編集すればよいだけだ。具体的には、VIAが有効になっているファームウェアのkeymap.cを編集すればよい。詳細は下記ページに譲るが、レイヤー変更のたびに呼ばれる関数 layer_state_set_userの中で、LEDカラー設定関数rgblight_set_layer_stateを呼ぶ。上記設定の場合、LED(正確にはRGBLIGHT_LAYER_SEGMENTSで指定したLED)の色以外はVIAで(すなわちファームウェアの再インストールなしに)設定できるので、VIAによる柔軟性を維持できる。

beta.docs.qmk.fm

ymotongpoo.hatenablog.com

参考のため、編集したファイルを以下のgistにおいておく。 qmk_firmware/keyboards/ymdk/ymd09/keymaps以下に新しくディレクトリを作成し、以下のファイルを置いて書き込めば使用可能だ。デフォルトだとLEDがまぶしすぎたため、RGBLIGHT_LIMIT_VAL を128に設定している。

ymd09

言うまでもないが、マクロパッドはAutoHotkeyと親和性が高い。 マクロパッドを買うような人は既にインストールしていることと思うが、念のため宣伝しておく。

www.autohotkey.com

参考

f:id:estshorter:20210323202539j:plain 画像:https://mechboards.co.uk/shop/kits/ymd09-macropad/

いいかんじのLatexテンプレートをつくった

LaTexは慣れれば非常に良い技術文書作成ツールだが、残念ながら、その段階に至るまでの道のりが険しすぎる。文献リストから参考文献を自動生成するためにbibtexまわりを調べたり、キャプションをいいかんじにするためにcaptionパッケージやsubcaptionパッケージに与える引数を調整したりしていると、「文章を書く」という本来の目的を忘れそうになる。まるで、環境構築に非常に時間のかかるプログラミング言語を触っているようだ。

多くの人はLaTexの細かな設定には興味がなく、「それなりに整って見える文書」が欲しいだけだと思う。そこで「いいかんじのLatexテンプレート:PrettyLatexReport」を作った。

github.com

インストール後、

\usepackage{PrettyLatexReport} 

としてincludeするだけで、論文によくある「図1(a)」のような表示や、bibファイルからの参考文献リスト自動作成ができるようになる。PDFのしおりも挿入される。リポジトリには.latexmkrcも同梱しているので、Perlが動けば*1latexmkコマンドを打つだけでコンパイルできる。インストールの仕方等、詳細は上記ページのREADMEを参照のこと。

ちなみに、テンプレートと言ってもベースはbxjsarticleであり(template.tex参照)、本体であるPrettyLatexReport.styの中では、必要なパッケージをincludeしつつ、figureまわり、「PDFのしおり」まわり、bibtexまわり等の煩雑な設定をしているだけだ。ただ、そのような設定をすべてstyに押し付けたおかげで、メインファイル(texファイル)では文章を書くことに集中できる。

学位論文申請用のテンプレートも作ったのだが、こちらはもう使う予定がないのでアーカイブしてしまった。Reportの方は会社業務で使っているので、大学を離れた今でもメンテしている。

github.com

*1:WindowsではStrawberry Perl等をインストールする必要がある

M5Paper公式ライブラリのOSSライセンス違反について

M5Paperの公式ライブラリであるM5EPDのコードを読んでいたところ、ソフトウェアライセンス的にまずい点を見つけたので記録しておく。なお、issueは以下の通り約1か月前に投稿済みだが、返答なし。

github.com

まずい点は、MITライセンスで公開しているにもかかわらず

  1. GPL-3.0配布のソフトウェア、
  2. CC BY-SA 3.0配布のソフトウェア、
  3. LGPL-2.1配布のソフトウェア

を含んでいることである。いずれもコピーレフト型ライセンスなので、MITライセンスは使えない。

※ライセンス周りはあまり詳しくないので、間違っていたら指摘してください。

GPL-3.0配布のソフトウェア

SHT3x.hSHT3x.cppが該当すると思われる。元リポジトリはおそらくhttps://github.com/Risele/SHT3xGPL-3.0を含むということは、M5EPDだけでなくM5EPDに依存するソフトはすべてGPL-3.0で配布する必要がでてくるため、影響範囲が大きい。

CC BY-SA 3.0配布のソフトウェア

Button.hButton.cppCC-BY3.0SAの資料を改変したり、加工した場合には、同じくCC-BY3.0SAで配布する必要がある。CC-BY4.0SAはGPL-3.0に対して一方向の互換性があるが、CC-BY3.0SAには互換性はないので、上記のGPL-3.0と両立するのか不明。ちなみに、Arduino Button Libraryの最新版はGPL-3.0で配布されている。

GitHub - JChristensen/JC_Button: Arduino library to debounce button switches, detect presses, releases, and long presses

なお、CC-BY3.0SAをincludeしたコード(すなわち、GPLでいうところの「派生物」)はCC-BY3.0SAで配布する必要があるのか、私はよくわかっていない、、(CCはコードへの適用を想定していないのでグレーゾーンか?)

LGPL-2.1配布のソフトウェア

CommUtil.hM5Timer.h。他コードからincludeはされていない。なお、M5Stackライブラリは上記ファイルをincludeしている

M5Stack製品はハードの出来はものすごくよいと思うので、ソフトウェア周りも頑張ってほしいと思う次第である。

なお、私が公開しているM5Paper-Dashboardでは、自身でフォークしたM5EPDを使用している。

github.com

上記ファイルをすべて削除しているため、ライセンス的にはクリーン。ボタンにはezButtonライブラリを使用している。この影響で、ボタンに関するAPIが変わっている。また、画面描画にはLovyanGFXを使用することを想定し、もともとM5EPDに入っていた描画系コードも削除している。

中心力場における単一質点の軌道の可視化

最近、V.I.アーノルド「古典力学の数学的方法」を読んでいるのだが、かっこいい図があったので自分で描いてみた。

古典力学の数学的方法

古典力学の数学的方法

f:id:estshorter:20210202223752p:plain
円環の中、至るところ稠密な軌道

エディタのAtomみたいな画像である。 以下、どう上の画像を描いたかかの説明。

いま、平面上の中心力場(質点にはたらく力の向きが「原点から質点へ向かう単位ベクトルかその逆ベクトル」であり、力の大きさも原点からの距離のみに依存する場。例えば重力場。)における、質量1の質点の運動を考える。支配方程式は、

\displaystyle{
\ddot{\boldsymbol{r}} = - \dfrac{\partial U}{\partial \boldsymbol{r}}, \quad U=U(r)
}

である。この場合、原点からの距離 rは、次のポテンシャルエネルギー(有効ポテンシャルエネルギー)をもつ一次元の問題に従う。

\displaystyle{
V(r) = U(r) + \dfrac{M^2}{2r^2}
}

すなわち、

\displaystyle{
\ddot{r} = - \dfrac{\partial V}{\partial {r}} = - \dfrac{\partial U}{\partial r} +  \dfrac{M^2}{r^3}
}

である。 簡単にいうと、rは、有効ポテンシャルエネルギーVがなす「坂」を滑らかに転がったときの、地面に平行な方向への変位と同様に変化する。

また、今回のケースにおいては、極座標における角度\varphirは次の関係がある。

\displaystyle{
\dfrac{d\varphi}{dr} = \dfrac{M/r^2}{\sqrt{2(E-V(r))}}
}

ここで、Eは全エネルギー

\displaystyle{
E=\dfrac{\dot{r}^2}{2} + V(r) = \dfrac{\dot{\boldsymbol{r}}^2}{2} + U(r)
}

である。 したがって、ポテンシャルUの形状、Mおよび、Eを決めれば数値的に軌道 \boldsymbol{r}を求めることができる。なお、Uの形状によっては、軌道を解析に求められる。例えば、重力場 U(r)=-Gm/r)では円錐曲線が\frac{d\varphi}{dr}の式より導かれる。

ここまで説明してようやく画像の話に戻れる。 当該画像は、

\displaystyle{
U(r) = \dfrac{r^3}{2}, M=1, E=2
}

と設定したときの軌道である。 以下のPythonスクリプトにより、\frac{d\varphi}{dr}をRunge-Kutta45で積分して描画した。  \dot{r}=0のときに\frac{d\varphi}{dr}の分母が0になってしまいうまく計算できないので多少の誤差はある(U(r)=-1/rと設定し、楕円曲線からズレていくのを見ると明らかだ)が、 解析解は(多分)ないので仕方ないだろう。

from functools import partial

import matplotlib.pyplot as plt
import numpy as np
from scipy.integrate import solve_ivp


def U(r):
    return 0.5 * r ** 3


def V(r):
    return U(r) + 0.5 / r ** 2


def dphi(r, E):
    return 1 / r ** 2 * (1 / np.sqrt(2 * (E - V(r))))


r_min = 0.50842208660526406332007805917088124001667877021803
r_max = 1.5286429152095940020185327716896748970412268021517
E = 2

fun = partial(dphi, E=E)
r_eval = np.arange(r_min, r_max, 0.001)
sol = solve_ivp(lambda r_, phi: fun(r_), [r_min, r_max], [0], t_eval=r_eval)
phi = sol.y.flatten()
r = sol.t

ax = plt.subplot(111, projection="polar")
for i in range(26):
    ax.plot(phi, r, color="k")
    r = r[::-1]
    phi = 2 * phi[-1] - phi[::-1]

circle_phi = np.arange(0, 2 * np.pi, 0.01)
ax.plot(circle_phi, r_min * np.ones(circle_phi.shape), color="k")
ax.plot(circle_phi, r_max * np.ones(circle_phi.shape), color="k")
ax.axis("off")
plt.savefig("orbit.png", bbox_inches="tight", dpi=200)
plt.show()

ちなみに、最初の画像の軌道は「ドーナツ」の部分を稠密に満たす。以下は十分に時間がたった時(range(26*6))の、初期時刻からの軌道である。

f:id:estshorter:20210202233919p:plain
軌道の長期的な発展

M5PaperでNTPサーバの時刻をRTCに正しく設定する

M5Paper開発の際、NTPで取得してRTCに設定した時刻がたまにズレて困っていたが、configTzTime()を呼んだ後にsntp_get_sync_status()で同期状態を確認するようにしたら直った。sntp_get_sync_status()を紹介している日本語記事は見当たらなかったので、短いが記事にしておく。

最初は以下のようなコードを書いていたが、分・秒の値が狂うことがあり気になっていた。

configTzTime(tz, server1, server2, server3);
struct tm datetime;
if (!getLocalTime(&datetime))
    return 1; //error
//以下でRTCに時刻を設定

ググッてみたところ、esp-idfの公式リポジトリに答えがあった。sntp_get_sync_status()の戻り値が、SNTP_SYNC_STATUS_RESETでない(SNTP_SYNC_STATUS_COMPLETEDSNTP_SYNC_STATUS_IN_PROGRESSである)ことを確認すればよさげだ。

github.com

つまり、以下のコードとすればよい。

configTzTime(tz, server1, server2, server3);
struct tm datetime;
int retry = 0;
constexpr int retry_count = 50;
while (sntp_get_sync_status() == SNTP_SYNC_STATUS_RESET && ++retry < retry_count)
{
    delay(100);
}
if (retry == retry_count)
{
    return 1; //error
}
if (!getLocalTime(&datetime))
    return 1; //error
//以下でRTCに時刻を設定

ちなみに、上記のesp-idfレポジトリのコードはPublic Domainなのでコピペしても安心である。

dアニメストアの今期アニメ配信情報をGoogleカレンダーに登録する

しょぼいカレンダーにはアニメの放送日をGoogleカレンダーに表示できる機能があり、以前までは便利に使っていた。以前まで、と書いたのは、私は最近、録画派からdアニメ派に鞍替えしており、dアニメ視聴だと上述の機能が使えない*1ためである。 そこで、Golangで同様の機能を簡易に実装してみたというのが本記事の内容である。

成果物は以下のリポジトリにおいた。

github.com

これは、dアニメストア今期アニメ一覧ページの情報をスクレイピングし、指定したアニメの配信情報を表すiCalデータを作るプログラムである。iCalデータは手動でGoogleカレンダーに読み込ませることを想定している。アニメ一覧ページのURLおよび、アニメタイトルの指定はconfigs.jsonで行う。configs.jsonの場所は下記のようにコマンドライン引数で与える。指定しない場合には、カレントディレクトリのものが読み込まれる。

danime-ical.exe PATH_TO_CONFIGS_JSON

生成されるiCalファイルの例は以下の通り。クールの初めの月(冬アニメだったら1月)の最初の配信曜日から、毎週の配信予定が記述されている。アニメの話数は13話を想定。

BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//Arran Ubels//Golang ICS library 
METHOD:REQUEST
BEGIN:VEVENT
UID:のんのんびより のんすとっぷ
DTSTART;TZID=Asia/Tokyo:20210101T010000
DTEND;TZID=Asia/Tokyo:20210101T013000
SUMMARY:のんのんびより のんすとっぷ
RRULE:FREQ=WEEKLY;COUNT=13
END:VEVENT
END:VCALENDAR

クールの始まり終わりあたりは不正確な可能性もあるが、ひとまずはこれで十分だろう

*1:しょぼいカレンダーにはdアニメでの配信日時が登録されていないため