2026年 05月 08日

スマートメータから100V 2系統それぞれの電流値を得る

スマートメータから瞬間消費電力を読むRubyのコードをT相・R相の電流値(100Vの2系統)をとるように修正した。

仕様上は0.1A単位でとれるみたいだけど、実際は1A単位にまるめられてしまうみたい。なので細かいことはわからない。

うちの分電盤の場合、図面を確認すると交互に配線 (L1N, L2N, L1N, L2N) されているようで、奇数番目がR相、偶数番目がT相になっているようだった。

何が嬉しいか? というと電流値を見ることで、ブレーカーを落としたりしなくてもT相系かR相系かまでは特定できる。

あと力率がある程度わかるかな。コイル負荷か抵抗負荷かぐらいの種類をある程度推測できる。

--- smartmeter.rb.orig	2026-05-08 08:43:13
+++ smartmeter.rb	2026-05-08 20:30:44
@@ -385,7 +385,7 @@
 				ECHONET_Lite::EOJ.new(0x05, 0xFF, 0x01),
 				ECHONET_Lite::EOJ.new(0x02, 0x88, 0x01),
 				0x62,
-				3,
+				4,
 				[
 					ECHONET_Lite::Property.new(
 						0xe7,
@@ -399,6 +399,11 @@
 					),
 					ECHONET_Lite::Property.new(
 						0xe0,
+						0x00,
+						""
+					),
+					ECHONET_Lite::Property.new(
+						0xe8, # 瞬時電力
 						0x00,
 						""
 					),
@@ -494,6 +499,8 @@
 				unit = nil
 				energy = nil
 				watts = nil
+				r_amp = nil
+				t_amp = nil
 				frame.edata.properties.each do |prop|
 					p prop
 					case
@@ -512,16 +519,26 @@
 						#0x0C:1000kWh
 						#0x0D:10000kWh
 						unit = UNIT_KW[prop.edt.unpack("C")[0]]
+
+					when prop.epc == 0xe8 && prop.pdc == 4
+						# 符号付き16bitビッグエンディアン2つ分をアンパック
+						r_raw, t_raw = prop.edt.unpack("s>2")
+						r_amp = r_raw / 10.0
+						t_amp = t_raw / 10.0
 					end
 				end
 
-				puts "%.1f kWh / %d W" % [energy * unit, watts]
+				puts "%.1f kWh / %d W (R: %.1f, T: %.1f)" % [energy * unit, watts, r_amp, t_amp]
 
 				body = ""
 				body << "# TYPE consumed_power GAUGE\n"
 				body << "consumed_power #{watts}\n"
 				body << "# TYPE consumed_energy COUNTER\n"
 				body << "consumed_energy #{energy * unit}\n"
+				body << "# TYPE consumed_current_r GAUGE\n"
+				body << "consumed_current_r #{r_amp}\n"
+				body << "# TYPE consumed_current_t GAUGE\n"
+				body << "consumed_current_t #{t_amp}\n"
 				
 				require 'net/https'
2026年 02月 21日

メモリアクセスでAMラジオを鳴らす

https://youtu.be/IDtk7N53G-0

system-bus-radio という2015年からある由緒正しいプロジェクトがあり、なんか Gemini と会話してたら急に出てきたので、M1でも動かないかなあといじっていた。ごちゃごちゃやってたら任意 wav ファイル再生までできた。ちゃんとリサーチしてないので既にやってる人はいくらでもいるんだろうが……

元の実装は単音メロディーだけで任意wavを再生できる感じではなかったので適当に拡張してwav再生できるように。

これはどう考えても動画にしたほうが見栄えするので適当に作った。調子が悪すぎて声がおかしいし喋りもおかしい……

2026年 02月 18日

uutransfer-tmux-v2

https://github.com/cho45/uutransfer/blob/master/uutransfer-tmux-v2

ちょっとだけ賢い v2 を作ってみた。

uutransfer とは何か

ごく稀に scp が直接通らないが ssh ができるホストからデータをひっぱってきたいときがある。(例: 多段プロキシなどややこしいホスト)

こういう場合に、既にある tmux セッションを利用して、ファイルを手元に持ってこようというのが uutransfer。scp よりかなり効率は悪いものの手軽にリモートファイルを手元に持ってこれる。要はコピペでがんばって転送するのを自動化している。

uutransfer-tmux-v2

従来は

  • uutransfer 1 で uutransfer の待受実行
  • openssql ... など適当なコマンドをリモートで手動実行

という2ステップ必要だったのを、単に

uutransfer-tmux 1 foobar.log

でいけるようにした。

やってること

  • tmux send-keys をより活用するようにした。ワンライナーをインジェクトするような感じに
  • 圧縮→base64 するようにした
    • メインの想定はログファイルとかなので効率良くなったはず

subtechグループのcho45日記をインポート

subtech.g.hatena.ne.jp/cho45 以下のデータをインポートした。

はてブのホッテントリの復元

(iframeです)

https://lowreal.net/2026/restore-subtech-hotentry.html

サブテクURLからインポート済みへ転送

やったこと

グループのサービス終了にともなってエクスポートだけはしたけど、放置してあった。ときたま「あぁサブテクグループに書いた気がする」と思っても検索不可能だったのが地味に自分にとって不便だった。

ということで2020年にグループが終わってるから6年越しに、ずっっっっとやってなかったのを Codex と共にやってみた。

/cho45/20100107/1262839487 → /2010/01/07/1262839487 となるようにインポートしたので基本的に機械的に遷移できるはず。

そして http://subtech.g.hatena.ne.jp/cho45/20100107/1262839487 みたいに貼り付けられているリンクも後処理時に書き換えるようにしたので、この日記内ではリンク切れが復活するつもり。

あと一律で subtechタグをつけるようにした。ほとんど gerry++ で埋まっとるが……

ホッテントリの復元

グループ関係のはてブは全部タイトルが一緒になってしまっていて終わっているので、このホッテントリのトップだけ雑に再現したリンク集をつくった。供養。

2026年 02月 16日

ONNX Runtime Web の `ort.env.wasm.proxy` と `numThreads` は軽い推論では効果が薄そう

軽量なモデルの推論だと ONNX Runtime Web のマルチスレッド化の恩恵は受けれないという理解してるけど、どうするのが正解か考えている。

ort.env.wasm.proxy で Worker 化しても Session が1つだと1コアしか使えないよな、と思ってなんかいろいろ複雑なこと考えていたけど、ふと思うと単に Session 複数にすれば一番重いところはオフロードしてできるのか? → これはできなそう。プロキシ用のワーカーはグローバルに1つしか作られないっぽい。numThreads に応じてそこからさらに分岐するっぽいが

ということで、基本的に ort.env.wasm.proxy と numThreads は重い推論の分散化だから軽量なモデルの大量推論にはあまり有効ではなく、そういうことをするなら自力で複数 Worker + Session 分離の仕組みを作らなければならない。

リアルタイム推論での複数同時推論の設計の方向性

リアルタイム前提のモデルの場合、推論時間はリアルタイム性に対して十分余裕があるぐらい軽量に作ってあるはず (100ms分の推論が10msで終わるというような)。これを複数同時にCPUコアを活用して行いたい。

この場合 90ms 分が余裕時間なので、以下のようなのが理想の状態

  • ワーカー数 m: できるだけ少なく〜最大でコア数 (2〜4)
    • onnx の session の数になる = モデルのコピーが m 個分必要 = メモリ必要量の増加
  • 処理数 n: 1つのワーカーに複数のストリーム(推論リクエスト)をコンカレントに(多重に) 流し込む。
    • ジッターを考えるとどのぐらいが適切かわからないが 100ms 分が 10ms で終わるなら5つ(50ms = 50%) ぐらい詰めこんでもいいか?

ort の session の初期化は結構重いので、ワーカー数はあまり増やすと初期化のコストが全然無視できない。なので1つのワーカーでできるだけ多くのことをしたい。そして session 自体はステートレスなので別々の推論ストリームを流しても大丈夫 (モデルの入力としてステートを渡すのが普通なので)

ワーカー数でパラレルにしつつ、リアルタイム余裕分をコンカレントに実行させる。

memo

let proxyWorker: Worker はモジュールレベルで定義されて使いまわされている

numThreads SetGlobalIntraOpNumThreads(numThreads) numThreads は Intra-Op (演算内のオペレーション) の並列化

Python だと inter_op_num_threads があるようだ Web にはないっぽい。

interOpNumThreads 自体はあった。This setting is available only in ONNXRuntime (Node.js binding and react-native). なので Web では使えない。

モールスデコーダの並列推論

ONNX Runtime Web の `ort.env.wasm.proxy` と `numThreads` は軽い推論では効果が薄そう ということで独自にワーカー化して分散推論させるようなものにしてみた。動画だとメイン周波数 (シグナル履歴がでているやつ) で占有ワーカー1つ、ほかサブ周波数 (デコード結果だけでているやつ) が16本まで推論する。

↑ のエントリに書いた通りリアルタイム性としては余裕があるので、1つのSessionで複数の周波数のデコードを順次行う(コンカレント)のを、複数の Worker で同時に行う(パラレル)

  • ワーカーごとに ONNX Runtime の Session を持つ (モデルのコピーをもつ)
    • session 自体はステートレス
  • ワーカー内で「スロット」という概念で特定の周波数のデコード状態を持つ
    • attention とかもろもろ。外に出す必要がないのでワーカー内で閉じる

普通に実装するだけなら難しくないんだけど、クリックしたとき遡って再デコードという機能を入れた結果割と面倒くさい。結局再デコードを伴うという特殊性からメインの推論はこのワーカー分散の仕組みには入れず、単体で ort.env.wasm.proxy = true によるワーカー分離をしている。

2026年 02月 12日

モールスデコーダのストリームデコードテスト

チマチマ表示されると可愛い

2026年 02月 11日

モールスデコーダwpmによるSNRの悪化

まだいろいろいじってしまっていた。10-40wpm を学習するようにしたり (文字単位で学習頻度が一定になるような確率補正をいれている)、データジェネレータをリファクタリングしたり。

評価手段の1つとしてwpm(送信速度)ごとのSNRパフォーマンスを出すようにしてみた。今までは 15wpm 固定で評価していたので、学習の偏り (過学習) になっていないかちゃんと評価できていなかった。

理論的には速度が倍なら3dB分のディスアドバンテージがあるはずだけど、CER=10%の線を見ると、10wpmで-12dB、20wpmで-10dB、30wpmで-8dB、40wpmで-6dB程度となっている。

  • 10wpm → 20wpm は理論値より悪化がすくない (2dB)
  • 20wpm → 40wpm は理論値より悪化が多い (4dB)
2026年 02月 08日

NanoVNA-Web-Client の更新

https://github.com/cho45/NanoVNA-Web-Client

これまた Vue2 + Vuetify という構成だったのを Vue3 + Vanilla CSS に改修した。普通に大変だった。

Android App サポート (Capacitor) は完全に削除した。PWAでいいし…… しかしAndroidでWebSerialがうまくいかなかったりして案外つらい。WebUSBサポートは切れなかった。

テストがなかったのでこれを期にかなり書いたけど、LLMに書かせると本当にひどいしまともに mock できるまでに時間がかかる。ESMのimportの巻き上げとかを理解してない。まじカス

vite でビルドして成果物を github pages で公開するという形にかえた。もともと公開していたURLにはリダイレクトするだけのHTMLを置いた。こういうビルドシステム本当に嫌いだけどまぁもういいや…… メンテ不能になるのが嫌なわけだけど、きっと次メンテ不能になるころにはLLMがなんとかしてくれるでしょ……

まーーじでこんな1円にもならないことしててどうすんの

Hacking Reality with JavaScript: 7 Ways to Control the Physical World from the Browser

『JavaScriptから現実世界に干渉する7の方法: ブラウザでハードウェアをコントロールする技術』という本を書いた。これの英語版もKDPで出してみた。『Hacking Reality with JavaScript: 7 Ways to Control the Physical World from the Browser』とちょっとタイトルかえた。翻訳自体は Gemini CLI。まぁないよりはいいだろう…… どうせ日本語版も対象読者が狹すぎて売れないし、あってもなくても同じならあったほうがマシだろう

本文中の図は基本的に全部 matplotlib で書いてあるので、そこも全部翻訳して生成しなおしつつ、サンプルアプリは簡易的な i18n 対応をした。