<?xml version="1.0" encoding="utf-8" standalone="yes" ?>

   <rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
     <channel>
       <title>Posts on RORO</title>
       <link>https://ciscorn.net/posts/</link>
       <description>Recent content in Posts on RORO</description>
       <generator>Hugo -- gohugo.io</generator>
       <language>ja</language>
       <copyright>&amp;copy; ciscorn</copyright>
       <lastBuildDate>Tue, 21 Sep 2021 08:43:17 +0900</lastBuildDate>
       
           <atom:link href="https://ciscorn.net/posts/index.xml" rel="self" type="application/rss+xml" />
       
       
       <item>
         <title>Slack APIでユーザインタラクションに対応する</title>
         <link>https://ciscorn.net/2021/09/21/slack_interaction/</link>
         <pubDate>Tue, 21 Sep 2021 08:43:17 +0900</pubDate>
         
         <guid>https://ciscorn.net/2021/09/21/slack_interaction/</guid>
         <content:encoded><![CDATA[<p>以下のユーザアクションが行われたとき、Slackはアプリ開発者が事前に指定したエンドポイントに以下のペイロードを投げる：</p>

<ul>
<li>ユーザが <strong>Block Kit interactive component</strong> (in Posts, Modals, Home tab) を操作したとき

<ul>
<li>→ <code>block_actions</code> payload</li>
</ul></li>
<li>ユーザが <strong>Shortcut</strong> を使ったとき

<ul>
<li>→ <code>shortcut</code> or <code>message_actions</code> payload</li>
</ul></li>
<li>ユーザが <strong>Slash command</strong> を送信したとき

<ul>
<li>→ <code>view_submittion</code> payload</li>
</ul></li>
<li>ユーザが <strong>Modal</strong> を送信またはキャンセルしたとき

<ul>
<li>→ <code>view_closed</code> payload</li>
</ul></li>
</ul>

<p>（これらのペイロードにはHMACの署名も付いている。）</p>

<p>Slackによって呼ばれるエンドポイントは以下の2つがある：</p>

<ul>
<li><strong>Request URL</strong>: ユーザがアクションを起こしたときに呼ばれる。</li>
<li><strong>Options Load URL</strong>: select menu と multi-select menu のアイテムの読み込みに用いられる。使わなければ無指定でよい。</li>
</ul>

<p>そして、ユーザのアクションに対して、アプリのエンドポイントは以下の反応ができる：</p>

<ul>
<li><strong>Acknowledgement</strong> - （必須）アプリは3秒以内に 200 OK でレスポンスを返さなければならない（つまり時間のかかる処理は何らかの手法で非同期実行する必要がある）</li>
<li><strong>Message</strong> - メッセージの投稿または当該アクションが行われた<strong>投稿の書き換え</strong>を行う。これはペイロードに含まれる <code>response_url</code> に対してHTTP POSTすることによって行う。</li>
<li><strong>Modal</strong> - モーダルダイアログを開く。<code>views.open</code> APIを呼ぶ。</li>
<li><strong>Asynchronous</strong> - リクエストに対して直接的には何もせず、他のSlack APIを呼ぶ。</li>
</ul>

<p>詳しい条件は忘れたけど、アプリによる投稿がアプリによって書き変えられた場合には「（編集済み）」のマークは付かない。</p>
]]></content:encoded>
       </item>
       
       <item>
         <title>リアルタイム共同編集のアルゴリズム (Operational Transformation; OT) を理解する試み</title>
         <link>https://ciscorn.net/2021/08/30/ot/</link>
         <pubDate>Mon, 30 Aug 2021 23:12:06 +0900</pubDate>
         
         <guid>https://ciscorn.net/2021/08/30/ot/</guid>
         <content:encoded><![CDATA[<p>Google Docsのように文書を複数人でリアルタイムに共同編集できるアプリケーションがあります。あのような機能は、多かれ少なかれ、Operational Transformation (OT; 操作変換) という考え方を使って実現されているようです。興味があったので、このOTについて調べてみました。</p>

<p>(追記: これからは OT でなく CRDT だという話 → <a href="https://josephg.com/blog/crdts-are-the-future/">I was wrong. CRDTs are the future</a>)</p>

<p>なおGoogle Docsではいわゆる「リッチテキスト」を共同編集できますが、ここでは話を簡単にするために「プレーンテキスト」を共同編集することを想定します。</p>

<h2 id="リアルタイム共同編集の流れ">リアルタイム共同編集の流れ</h2>

<p>共同編集システムの登場人物は次の通りです：</p>

<ul>
<li>サーバ x 1（各クライアントから届く編集操作をもとに、最新の文書を保持します）</li>
<li>クライアント x N（文書を編集する側です）</li>
</ul>

<p>そして、リアルタイム共同編集では以下のような処理が繰り返されます：</p>

<ol>
<li>各クライアントは、手元で文書が編集されたら、その編集操作の内容をサーバに送信します。</li>
<li>サーバは、各クライアントから送られてくる編集操作を、到着した順にサーバ上の文書に適用します（<strong>※ここでOTを使います。後述</strong>）。</li>
<li>次にサーバは、他の全てのクライアントに編集操作の内容を送信します。</li>
<li>各クライアントは、サーバから送られてきた（他のクライアントによる）編集操作を、手元の文書に反映します（<strong>※ここでもOTを使います。後述</strong>）。</li>
</ol>

<p>ちなみにプレーンテキストにおいては、「編集操作」というのは文字列の単なる挿入と削除のことです。例えば「39文字目に文字列 &ldquo;hello&rdquo; を追加」とか「74文字目から8文字を削除」といったものです。</p>

<p>サーバは、各クライアントから届く編集操作を、到着順にサーバ上の文書に反映させていきます。これによってサーバ上の文書は次々と更新されていき、文書のリビジョン番号 (rev) は 0 → 1 → 2 → 3 → 4 → … と直線的に進んでいきます。サーバは、あるクライアントから届いた編集の内容を、他のクライアント全員にも送信することで、皆が同じ文書の状態を共有します。</p>

<p>…というのが基本ですが、現実はそう簡単ではありません。</p>

<h2 id="遅延による難しさ">遅延による難しさ</h2>

<p>リアルタイム共同編集が難しいのはネットワーク等による遅延があるからです。遅延があるため、すべてのクライアントが常に最新のリビジョンを見ているとは限りません。あるクライアントは、古いリビジョンの内容に基づいて編集を行い、それをサーバに送信するかもしれません。また、同じリビジョンに対して複数のクライアントが同時に編集するという競合も起きるでしょう。具体例としては以下のようなことが発生します：</p>

<ul>
<li>シナリオ (1) 「サーバ側の文書はリビジョン rev:53 なのに、クライアントBにはまだ rev:51 までしか同期されていなかった。このクライアントBが文書を編集し、rev:51 に対する編集操作をサーバに送った。しかしサーバ側は既に rev:53 である。どうやって辻褄を合わせればいいのか？」</li>
<li>シナリオ (2) 「クライアントCは rev:74 に対して文字列の追加を行なった。この操作をサーバに送ろうとする最中に、サーバから（他のクライアントによる編集で生じた）rev:74→75 の編集操作が送られてきた。このとき、クライアントCが手元で rev:74 を編集したあとの状態と、サーバから送られてきた rev:74→75 の操作は、どうやって辻褄を合わせればいいのか？」</li>
</ul>

<p>この問題を解決する手法がOTです。</p>

<h2 id="リビジョンが分岐してはならない">リビジョンが分岐してはならない</h2>

<p>同時編集の目標は「遅延がありつつも結果的には皆が同じ文書を共有する」ということです。つまりリビジョンは分岐してはならず、ただ1つの正統な本流だけが存在すべきだということです。</p>

<p>この観点で見ると、先述したシナリオでは、「本流のリビジョンからの枝分かれの発生」によって問題が生じていることが分かります。</p>

<h2 id="解決方法-競合する2つの操作を-順不同な直列操作に変換する方法を定義する">解決方法:「競合する2つの操作を、順不同な直列操作に変換する方法を定義する」</h2>

<p>逆にいえば、一時的に編集結果が枝分かれしても、それをすぐに本流の状態へと軌道修正できれば、ただ1つの本流を皆で共有することができます。</p>

<p>そのための要件として、まず、少なくとも、分岐が生じた場合にそれを直列に直せなければなりません。</p>

<p style="text-align:center">(図はまだ) </p>

<p>「同じリビジョンに対する（つまり競合する）opAとopB」を、「opAをしたあとで、opBに相当するop B&rsquo; をする」に変換できなければならないのです。</p>

<p>また、それぞれの立場によって opA, opB の発生順序が違うので、「opAをしたあとで、opBに相当するopB&rsquo; をする」と「opBをしたあとで、opAに相当するopA&rsquo; をする」とが同一の結果になるという性質も必要です。</p>

<p style="text-align:center">(図はまだ) </p>

<p>プレーンテキストでの具体例を示しましょう。同じリビジョンに対して「opA: 4文字目から10文字を削除する」「opB: 8文字目に &ldquo;hello&rdquo; を追加する」という競合する操作が生じた場合、これらは2通りの直列な操作に変換できます：</p>

<ul>
<li>opA→opB&rsquo;の順の場合 「opA: 4文字目から10文字を削除する。→ opB&rsquo;: 4文字目に &ldquo;hello&rdquo; を追加する」</li>
<li>opB→opA&rsquo;の順の場合「opB: 8文字目に &ldquo;hello&rdquo; を追加する。→ opA&rsquo;: 4文字目から4文字を削除し、13文字目から6文字を削除する」</li>
</ul>

<p>という直列な操作に変換することができます。いずれも同じ結果が得られます。</p>

<p>これが「操作変換 (OT)」です。このような操作変換が定義できれば、分岐の発生を修正して常に本流のリビジョンだけが存在するようにできます。</p>

<h2 id="プレーンテキストにおける操作と操作変換の例">プレーンテキストにおける操作と操作変換の例</h2>

<p>ここで一旦、プレーンテキストに対する操作を見ておきましょう。プレーンテキストに対する操作 (operation) は、以下の3つの基本操作の列として記述することが多いようです。</p>

<ul>
<li><code>insert(s)</code> &mdash; 文字列sを挿入する</li>
<li><code>delete(n)</code> &mdash; n文字を削除する</li>
<li><code>retain(n)</code> &mdash; 既存のn文字を維持する</li>
</ul>

<p>例えば、20文字の文書において、opA「4文字目から10文字を削除する」と、opB「8文字目に &ldquo;hello&rdquo; を追加する」いう操作は以下のように記述できます。</p>

<ul>
<li><code>opA = [retain(4), delete(10), retain(6)]</code></li>
<li><code>opB = [retain(8), insert(“hello”), retain(12)]</code></li>
</ul>

<p>そして、これを操作変換 <code>transform(opA, opB) -&gt; (opA', opB')</code> すると以下が得られます。</p>

<ul>
<li><code>opB' = [retain(4), insert(“hello”), retain(6)]</code>（opA→opB&rsquo; の順に適用する場合にopBに相当する操作）</li>
<li><code>opA' = [retain(4), delete(4), retain(5), delete(6), retain(6)]</code>（opB→opA&rsquo; の順に適用する場合にopAに相当する操作）</li>
</ul>

<p>プレーンテキストの操作についての操作変換 <code>transform(opA, opB) -&gt; (opA’, opB’)</code> の具体的な実装についてはここでは割愛しますが、プレーンテキストに関しては、それほど難しい処理ではありません。</p>

<p>また、上記の transform のほかに、2つの直列なopを合成する <code>compose(op1, op2) -&gt; op</code> も定義しておきます。これも詳細は省略。</p>

<h2 id="サーバ側での処理">サーバ側での処理</h2>

<p>サーバは以下の状態をもっています：</p>

<ul>
<li>最新の文書（途中から参加するクライアントに配布する）</li>
<li>現在のリビジョン番号</li>
<li>過去に適用してきたopsの履歴（全てではなく直近のものだけ保持すればOK。保持する量を増やすほど遅延に耐えられるようになる）。適用時のリビジョン番号と紐付けて保持します。</li>
</ul>

<p>サーバは、クライアントから編集操作が送られてくるたびに、次の処理を行います。（なおクライアントから送られてくるメッセージには、編集操作のほかに、クライアント側のリビジョン番号も含まれています。）</p>

<ol>
<li>クライアント側とサーバ側とでリビジョン番号が異なる場合は、クライアント側から送られてきた op に操作変換を適用して、サーバ上の最新のリビジョンに適用できる op&rsquo; に変換します。具体的には、サーバ上にあるopsの履歴を用いて操作変換を必要なだけ繰り返します。例えばクライアント側が rev:30 でサーバ側が rev:33 の場合は、サーバに記録されている rev:31 から rev:33 までの3つのopsを相手にして、 opを3回transformします。これを op&rsquo; とします。</li>
<li>クライアント側とサーバ側でリビジョン番号が同一の場合は上記の変換は不要です。</li>
<li>編集操作 op&rsquo; をサーバの文書に適用し、リビジョン番号をインクリメントします。また、op&rsquo; をサーバ上の履歴に追加します。</li>
<li>編集操作を送ってきたクライアントにACKを返します。このACKには、サーバ上で確定したリビジョン番号を含めます。</li>
<li>その他の全クライアントに編集操作を反映させるため、op&rsquo; と最新のリビジョン番号を送信します。</li>
</ol>

<h2 id="クライアント側での処理">クライアント側での処理</h2>

<p>クライアント側は以下の状態をもっています：</p>

<ul>
<li>現在の文書</li>
<li>リビジョン番号（現時点でどのリビジョンまで同期しているかを表します）</li>
<li>outstanding op（手元での編集によって一時的に分岐した操作のうち、サーバに送信済みだがまだACKが返ってきていないもの）</li>
<li>buffered op（手元での編集によって一時的に分岐した操作のうち、送信待機中のもの）</li>
</ul>

<p>クライアントでは、各イベントにおいて以下の処理を行います。</p>

<h3 id="文書が編集されたとき">文書が編集されたとき</h3>

<ol>
<li>outstanding op がない場合は (=ACK待ち中でない場合は)、編集操作 op を outstanding op にセットしつつ、サーバに送信します。</li>
<li>outstanding op がある場合は (=ACK待ち中の場合は)、buffered op に op をセットして送信待ちにします。buffered op が既に存在する場合は、その buffered op に op を合成します。</li>
</ol>

<h3 id="ackが返ってきたとき">ACKが返ってきたとき</h3>

<ol>
<li>ACKに含まれる revision番号で手元のリビジョン番号を更新します。</li>
<li>outstanding op を空にします。</li>
<li>buffered op がある場合は、それを outstanding op に移しつつ、サーバに送信します。</li>
</ol>

<h3 id="opを受信したとき">opを受信したとき</h3>

<ol>
<li>outstanding op がある場合は、<code>(outstanding_op, op') := transform(outstanding_op, op)</code> という更新をします。</li>
<li>さらに buffered op がある場合は、<code>(buffered_op, op') := transform(buffered_op, op')</code> という更新をします。</li>
<li>outstanding opもbuffered opもない場合は、opをそのまま使います（つまり <code>op' := op</code> とします）。</li>
<li>クライアント側の文書に操作 op&rsquo; を適用します。</li>
</ol>

<h2 id="おわりに">おわりに</h2>

<p>実用的なテキストエディタを作る場合は以下のようなトピックも検討しなければいけない気がします。</p>

<ul>
<li>カーソルの動きもOTの対象にする</li>
<li>operation をベースにしたUndo/Redoの実装</li>
<li>サーバ側でどのようにスケーラビリティを得るか</li>
<li>HTML5 の contenteditable と日本語IMEによって発生しそうな地獄との戦い</li>
</ul>

<h2 id="参考文献">参考文献</h2>

<ul>
<li><a href="http://operational-transformation.github.io">http://operational-transformation.github.io</a></li>
</ul>]]></content:encoded>
       </item>
       
       <item>
         <title>メカニカルキーボード (Niz micro82) を買って Magic Trackpad 2 と併置した</title>
         <link>https://ciscorn.net/2021/06/02/nizmicro82/</link>
         <pubDate>Wed, 02 Jun 2021 20:25:25 +0900</pubDate>
         
         <guid>https://ciscorn.net/2021/06/02/nizmicro82/</guid>
         <content:encoded><![CDATA[<p>MacBook Proを横によけて外付けディスプレイをメインで使おうと思って、Niz micro82というキーボードを買った。メカニカルキーボードを使うのは10年以上ぶり。</p>






<figure style="">
  <img data-original="/2021/06/02/nizmicro82/2.jpg" src="/2021/06/02/nizmicro82/2.jpg" />
  
</figure>


<p><a href="https://www.nizkeyboard.com">公式サイト</a>で注文してちょうど1週間で届いた。Bluetooth付きモデルで、送料込みで$189だった。</p>






<figure style="">
  <img data-original="/2021/06/02/nizmicro82/feature.jpg" src="/2021/06/02/nizmicro82/feature.jpg" />
  
</figure>


<p>マウス周りは、AppleのMagic Trackpad 2をキーボードの真横に置いて使っている。ただしそのままだとトラックパッドとキーボードの高さがまったく合わなくて辛いので、簡単なスタンドを設計して3Dプリントしてトラックパッドを底上げした。これで十分に使いやすくなったけど、トラックパッドはキーボードより更に高くしてもいいかもしれない。</p>






<figure style="">
  <img data-original="/2021/06/02/nizmicro82/3.jpg" src="/2021/06/02/nizmicro82/3.jpg" />
  
</figure>


<p>トラックボール（中指）も好きだけど、トラックパッドのほうが用途を選ばないかなと思っている。例えばPhotoshopみたいな操作をトラックボールでやるのは自分は苦手だ。</p>

<h2 id="キーレイアウトに関して慣れない点">キーレイアウトに関して慣れない点</h2>

<ul>
<li>Macの純正キーボード (US配列) と比べてCommandキーが親指から遠い位置にあって押しにくい。この点を考えるとmicro82ではなくmicro84のほうが良かったかもしれない。</li>
<li>最右列（Home, End PgUp, PgDnとか）が邪魔に感じる。</li>
</ul>]]></content:encoded>
       </item>
       
       <item>
         <title>1万円超のフライパン (Scanpan) に買い替えた</title>
         <link>https://ciscorn.net/2020/06/28/scanpan/</link>
         <pubDate>Sun, 28 Jun 2020 16:17:25 +0900</pubDate>
         
         <guid>https://ciscorn.net/2020/06/28/scanpan/</guid>
         <content:encoded><![CDATA[<p>700円で買ったフライパンを捨ててデンマーク製の &ldquo;Scanpan Pro IQ Saute Pan（ソテーパン）&rdquo; というやつを買った。ソテーパンは日本でいう炒め用フライパンと同じような形状をしている。</p>






<figure style="">
  <img data-original="/2020/06/28/scanpan/2.jpeg" src="/2020/06/28/scanpan/2.jpeg" />
  
</figure>


<p>日本でまともに買うと32,000円以上するので、保証が効かないことを覚悟したうえでオーストラリアから輸入した。本体12,000円＋送料4,000円の計16,000円で買えた。</p>

<p>なぜ買い替えたのか理由を色々と書いたのだけど、うっかり消してしまったので写真だけ貼っておく。手短に言えば「安くて軽いフライパンをIHで使うとすぐに反り返ってストレスMAXになる」ということです。</p>

<p>火の通りもテフロン加工もすごく良いです。大事に使おうと思う。</p>






<figure style="">
  <img data-original="/2020/06/28/scanpan/1.jpeg" src="/2020/06/28/scanpan/1.jpeg" />
  
</figure>







<figure style="">
  <img data-original="/2020/06/28/scanpan/3.jpeg" src="/2020/06/28/scanpan/3.jpeg" />
  
</figure>







<figure style="">
  <img data-original="/2020/06/28/scanpan/4.jpeg" src="/2020/06/28/scanpan/4.jpeg" />
  
</figure>







<figure style="">
  <img data-original="/2020/06/28/scanpan/5.jpeg" src="/2020/06/28/scanpan/5.jpeg" />
  
</figure>







<figure style="">
  <img data-original="/2020/06/28/scanpan/feature.jpeg" src="/2020/06/28/scanpan/feature.jpeg" />
  
</figure>]]></content:encoded>
       </item>
       
       <item>
         <title>Facebook PageとInstagramへの反応をAPIでリアルタイムにウォッチする</title>
         <link>https://ciscorn.net/2020/06/04/fbig_webhook/</link>
         <pubDate>Thu, 04 Jun 2020 16:45:35 +0900</pubDate>
         
         <guid>https://ciscorn.net/2020/06/04/fbig_webhook/</guid>
         <content:encoded><![CDATA[<p>（追記：その後、App作成時に &ldquo;Facebook Business Integrations&rdquo; という種別を選択できるようになったらしく、身内でしか使わないアプリに関しては以下のような審査は不要かもしれません。どんどん状況が変っていくのでついていけません&hellip;）</p>

<hr />

<p>チームで管理するFacebookページやInstagramアカウントへの反応（例えばコメント）をリアルタイムに把握したいというニーズは多いと思う。これはFacebookが提供するWebhooksを使えば実現できる。ただしWebhooksの利用自体は比較的簡単だが、その利用審査が面倒くさい。</p>

<p>Facebookの多くのAPIは、自分が管理するオブジェクトに対する操作であれば「開発モード」（つまり審査なし）でも使える。しかしここで扱う Webhooks (for Facebook Pages / Messenger / Instagram) に関しては、「ライブモード（本番モード）」でなければ動作しない。つまり、たとえ自分が管理しているページでの利用であっても、必要なパーミッションを申請して審査を受ける必要がある。</p>

<p>主要なWebhooksとしては以下がある：</p>

<ul>
<li><a href="https://developers.facebook.com/docs/pages/realtime">Webhooks for Pages</a> &mdash; Facebookページ上での各種アクティビティをリアルタイムに知れる。</li>
<li><a href="https://developers.facebook.com/docs/messenger-platform/webhook/">Messenger Platform - Webhook</a> &mdash; Facebookページの受信箱のアクティビティをリアルタイムに知れる。</li>
<li><a href="https://developers.facebook.com/docs/instagram-api/guides/webhooks/">Webhooks for Instagram</a> &mdash; Instagramアカウント (Business/Creatorのみ) へのコメントやストーリーズ終了時のInsightsをリアリタイムに得られる。</li>
</ul>

<p>それぞれに必要なパーミッションとその審査について簡単にまとめておく。</p>

<h2 id="webhooks-for-pages">Webhooks for Pages</h2>

<p>Page関係のWebhookを使うには、該当するオブジェクトを扱うパーミッションを取得して、なおかつライブモードで動作させる必要がある。それまでは開発者用のAppダッシュボードから送れるテストメッセージしか受信できない。</p>

<p>例えばページの投稿に付いたコメントをWebhookで知りたい場合は <code>pages_read_user_content</code> パーミッションが必要になる。（ちなみに以前は <code>manage_pages</code> と <code>publish_pages</code> という2種類のパーミッションしか存在しなかったが、API v7.0から細分化されたらしい）。</p>

<p>Facebookログインを一切使わないアプリを申請するときは、アプリが &ldquo;Server-to-server App&rdquo; であることをレビュアーに伝える必要がある。とくにWebhookだけを使うアプリはレビュアーに趣旨をうまく理解してもらえないことがある。申請がどうしても却下される場合は、Webhookの利用ではなく、サーバからPages API呼び出しを行う様子をスクリーンキャストとして撮影して申請するといいかもしれない。</p>

<h2 id="messenger-platformのwebhook">Messenger PlatformのWebhook</h2>

<p>Messenger Platformを使うには <code>page_messaging</code> パーミッションが必要になる。このパーミッションがない場合は、開発モード／ライブモードを問わず、管理者によるアクションに対してしかWebhookが送信されない。</p>

<p><code>page_messaging</code> パーミッションの申請は簡単で、簡単なスクリーンキャストを撮影して送るだけでいい。Messenger PlatformはWebhookの利用が前提なので、レビュアーにWebhookの話が通じないという難しさもない。</p>

<p>審査の過程でテストメッセージが送られてくるので、それをちゃんと受信しておき、2日以内にSend APIを使って適当な返事をする必要がある。</p>

<h2 id="webhooks-for-instagram">Webhooks for Instagram</h2>

<p>InstagramのWebhooksは、自分のInstagram Business/Creatorアカウントでの利用であれば審査なしでも使えるようだ（そういう仕様なのか、それともたまたま使えているだけなのかは分からない）。</p>

<p>これは次のようにする：</p>

<ul>
<li>Facebookページと対象のInstagramアカウントをリンクする</li>
<li>Facebook Appの開発者をページの管理者にする</li>
<li>Facebook App内にInstagramのAppを作る</li>
<li>InstagramのWebhooks用エンドポイントを設定する</li>
<li>対象のInstagram Business/Creator アカウントを、アプリのテスターとして追加する</li>
</ul>

<p>これでWebhookが飛んでくるようになる。</p>]]></content:encoded>
       </item>
       
       <item>
         <title>届いたFAXを自動でGoogle Driveに保存してOCRで本文を抽出してSlackで通知する</title>
         <link>https://ciscorn.net/2020/05/21/fax_receiver/</link>
         <pubDate>Thu, 21 May 2020 20:05:14 +0900</pubDate>
         
         <guid>https://ciscorn.net/2020/05/21/fax_receiver/</guid>
         <content:encoded><![CDATA[<p>(@前職)</p>

<p>リモートワークの絡みで、タイトルの通りのことをやってみようと思いついて、やってみたらけっこう簡単にできたので、その概要を書いておく。</p>

<p>処理の要点は、メールの自動受信処理、Google Cloud VisionによるOCR、Google Drive (G Suite) へのアップロードなど。</p>

<p>最終的な結果は以下のような感じでSlackに通知される。</p>






<figure style="">
  <img data-original="/2020/05/21/fax_receiver/result.png" src="/2020/05/21/fax_receiver/result.png" />
  
</figure>


<h2 id="処理の流れ">処理の流れ</h2>

<pre><code>FAX複合機
 ↓
SendGrid Inbound Parse Webhook
 ↓
Google Cloud Functions
    → Google Cloud Vision
    → Google Drive (G Suite)
 ↓
Slack
</code></pre>






<figure style="">
  <img data-original="/2020/05/21/fax_receiver/feature.jpg" src="/2020/05/21/fax_receiver/feature.jpg" />
  
</figure>


<p>Cloud Functionsでの処理はPythonで書いた。</p>

<h2 id="1-複合機からメールを送信する">1. 複合機からメールを送信する</h2>

<p>大抵のFAX複合機には、受信したFAXをPDFにしてメールで送信する機能がついている。これを使って受信データを外部に送信する。</p>

<p>次に、複合機から送信されたメールを何らかの方法で自動で（機械的に）受信する必要がある。今回はそれを SendGrid Inbound Parse Webhook を使って行うことにした。</p>

<h2 id="2-メールを受信する">2. メールを受信する</h2>

<p>複合機から送信したメールをSendGrid Inbound Parseに受信させる。受信したメールはparseされて、HTTPリクエストとしてGoogle Cloud Functions (のHTTPトリガ) に送られる。</p>

<h2 id="3-google-cloud-functionsでfax-pdf-を処理する">3. Google Cloud FunctionsでFAX (PDF) を処理する</h2>

<p>Google Cloud Functionsで、メールヘッダや添付PDFファイルを受け取って、以下の処理を行う。</p>

<h3 id="3-1-google-cloud-visionでocrする">3.1. Google Cloud VisionでOCRする</h3>

<p>Cloud Visionの <code>DOCUMENT_TEXT_DETECTION</code> を使ってOCRを実行する。手書き文字も認識される。</p>

<ul>
<li>ページはPDFの1〜5ページ目のみを読み取るようにした。</li>
<li>languageHintsには[&lsquo;ja&rsquo;]を指定した。</li>
</ul>

<h3 id="3-2-pdfの向きを直す">3.2. PDFの向きを直す</h3>

<p>FAXはひっくり返った向きで送られてくることが多いし、横長で印刷された文書というのもたまにある。</p>

<p>向きを直しておかないと見づらいので、OCRの結果をもとにページの向きを判定して、PyPDF2を使ってPDFの各ページを回転させる。</p>

<h3 id="3-3-google共有ドライブ-g-suite-にアップロードする">3.3. Google共有ドライブ (G Suite) にアップロードする</h3>

<p>GoogleのDrive APIを使ってGoogle DriveにPDFをアップロードする。</p>

<p>PythonでGoogleのサービスのAPIを呼ぶには、googleapiclient (google-api-python-client) を使う。</p>

<p>G Suiteの共有ドライブにアップロードする場合は、driveIdや、supportsAllDrives=Trueなどを指定しないとうまく動かない（後者はそのうちに撤廃されるらしいが）。このへんの「共有ドライブ」周りについてはDrive APIの英語ドキュメントもまだちゃんと整備されておらず、ググらないと分からなかった。</p>

<h3 id="3-4-文書のサマリーを作る">3.4. 文書のサマリーを作る</h3>

<p>文書の形式によっては、OCR結果にノイズ的な文字列が多く含まれることがある。そのままだと読みにくいので、情報として価値のない文字列や、罫線を文字として誤認識したものや、日本語/英語/中国語(漢字)以外の謎の言語として誤認識されたものは、除去したほうがいい。OCR結果の各要素に含まれる confidence の値なども参考にする。</p>

<p>本気でやるなら、罫線は画像処理系のアルゴリズムを使ってあらかじめ除去したほうがいいかもしれない。</p>

<p>Cloud Visionが返す結果は pages → paragraphs → words → symbols という構造になっている。この構造をトラバースして、書式を整えたり、不要部分を除去したりしながら、結果の文字列を組み立てる。</p>

<h3 id="3-5-文書のタイトル抽出する">3.5. 文書のタイトル抽出する</h3>

<p>OCRの結果をいろいろとルールベースで処理して、タイトルっぽいものを抽出する。</p>

<p>文字列の長さや、文字列が占める面積、タイトルによくある表現（について、のお知らせ、件名〜、etc.）等々を基準にスコアリングして判定している。</p>

<p>抽出したタイトルを用いて、Google Driveに先ほどアップロードしたファイルをリネームする。</p>

<h3 id="3-6-slackへ投稿する">3.6. Slackへ投稿する</h3>

<p>Slack APIのchat.postMessageを使って、以下のものをSlackに投稿する。</p>

<ul>
<li>抽出タイトル</li>
<li>Google DriveのプレビューURL (webViewLink)</li>
<li>OCR結果のサマリ文字列</li>
</ul>






<figure style="">
  <img data-original="/2020/05/21/fax_receiver/result.png" src="/2020/05/21/fax_receiver/result.png" />
  
</figure>


<p>送信元FAX番号やOCRで得た内容に基づいたルールセットで投稿先のチャンネルを変えるようにしている。</p>

<p>SlackにはGoogle Driveの公式Appがインストールされているので、Drive上のファイルのURLを投稿するだけで自動的にunfurl（プレビュー展開）される。ただしこれを機能させるには、Botユーザではなく、Google Driveへのアクセス権をもつ一般ユーザとしてURLを投稿する必要がある。</p>

<h2 id="評価">評価</h2>

<ul>
<li>良いときは複合機がFAXを受信してから5〜10秒くらいでSlackに投稿される。しかしCloud FunctionsがColdスタートした場合は受信から投稿まで10〜15秒くらいかかってしまうこともまれにある。</li>
<li>しばらく運用しているが、エラーでFAXをとりこぼすということは起きていない。</li>
</ul>]]></content:encoded>
       </item>
       
       <item>
         <title>メールの「受信」ができるAPIの比較 (2020年5月)</title>
         <link>https://ciscorn.net/2020/05/16/mail_inbound_api/</link>
         <pubDate>Sat, 16 May 2020 21:40:16 +0900</pubDate>
         
         <guid>https://ciscorn.net/2020/05/16/mail_inbound_api/</guid>
         <content:encoded><![CDATA[<p>Amazon SES vs. SendGrid vs. Mailgun vs. Mailjet vs. Mailchimp</p>

<h2 id="amazon-ses-の受信機能">Amazon SES (の受信機能)</h2>

<p><a href="https://aws.amazon.com/jp/ses/pricing/">https://aws.amazon.com/jp/ses/pricing/</a></p>

<ul>
<li>受信は1000通/月まで無料。以降は0.10USD/1000通。これに加えて256Kバイトのチャンク（端数切り捨て）について0.09USD/1000チャンク。</li>
<li>（送信は0.10USD/1000通。添付ファイルは0.12USD/1GB）</li>
<li>圧倒的に安い一方で、機能は最もプリミティブ</li>
</ul>

<p>受信のレイテンシの低さは申し分ない。メールがAWSのSMTPサーバに到達した1〜3秒後にはS3に格納される。</p>

<p>AWSの中で使うのは楽だが、AWSの外に受信データを渡したい場合はAWS LambdaやAmazon SNSを使ってその仕組みを作る必要がある。</p>

<h2 id="twilio-sendgrid-のinbound-parse-webhook">Twilio SendGrid (のInbound Parse Webhook)</h2>

<p><a href="https://sendgrid.com/pricing/">https://sendgrid.com/pricing/</a></p>

<ul>
<li>受信 (Inbound Parse Webhook) は無料プランでも使える</li>
<li>有料プランは、14.95USD/mo (40000通/mo = 0.373USD/1000通) から</li>
<li>（送信は無料プランだと100通/day）</li>
<li>（マーケティングメール向けの機能/プランもある）</li>
</ul>

<h3 id="遅延問題-今は解決">遅延問題 (今は解決）</h3>

<p>トライアル契約でParse Webhookを使ってみたが、レイテンシが使い物にならないくらい大きくなることがあった。良いときはメールを送ってから数秒でWebhookが呼ばれるが、悪いときには30〜60分以上の遅延が（しかも少なくない頻度で）発生した。</p>

<p>念のために有料プラン (Essentials 40K) を契約して数日置いてから試してみたが状況は変わらなかった。</p>

<p>サポートに報告したら、数日後にチケットに &ldquo;degraded&rdquo; という印が付いたが、返事はまだこない。</p>

<p><strong>2020-06-02追記</strong>： SendGridのサポートからようやく返事が来て、この問題は解決したとのこと。コロナ禍で対応が遅れたのだろうか。</p>

<h2 id="mailgun-のinbound-routing">Mailgun (のInbound Routing)</h2>

<p><a href="https://sendgrid.com/pricing/">https://sendgrid.com/pricing/</a></p>

<ul>
<li>受信 (Inbound Routing) は有料プランユーザのみ使える。

<ul>
<li>最安のプランが高い: 35USD/moで送信50000通/mo = 0.7USD/1000通</li>
</ul></li>
<li>受信自体は無料で、そのあとの転送 (HTTP POSTも) にチャージされる。</li>
<li>（メール送受信自動化用プラットフォームとしては高機能）</li>
<li>（マーケティング担当者向けの機能はない）</li>
</ul>

<p>試用はしていない。</p>

<h2 id="mailjet-のparse-api">Mailjet (のParse API)</h2>

<p><a href="https://sendgrid.com/pricing/">https://sendgrid.com/pricing/</a></p>

<ul>
<li>受信 (Parse API) は有料プランユーザのみ使える。</li>
<li>最安の有料プランは8.69USD/moで30000通 = 0.289USD/1000通</li>
<li>（マーケティングメール向けの機能もある）</li>
</ul>

<p>試用はしていない。</p>

<h2 id="mailchimp-transactional-email-旧mandrill">Mailchimp Transactional Email (旧Mandrill)</h2>

<p><a href="https://mailchimp.com/pricing/transactional-email/">https://mailchimp.com/pricing/transactional-email/</a></p>

<ul>
<li>Mailchimpの有料プラン (10.62USD/mo〜) に入らないと使えない</li>
<li>送信コストが高い (20USDで20000通 = $0.8/1000通)</li>
</ul>

<p>試用はしていない。</p>

<h1 id="その他">その他</h1>

<p>ドキュメントを見ても受信機能の詳しい仕様が書かれていないサービスが多かった。送信がメインで、受信機能はあくまでおまけという位置づけであることが多いのだろうか。</p>

<p>この種のメールサービスは悪用防止のために、試用ユーザや新規契約ユーザに一定の制限をかけることがあるそうなので（しかもその内容は明示されていないので）、試用中に評価を行うのが難しいと感じた。</p>]]></content:encoded>
       </item>
       
       <item>
         <title>Google Cloud Functionsを試す with Python 3.7</title>
         <link>https://ciscorn.net/2020/05/16/cloudfunctions/</link>
         <pubDate>Sat, 16 May 2020 15:22:12 +0900</pubDate>
         
         <guid>https://ciscorn.net/2020/05/16/cloudfunctions/</guid>
         <content:encoded><![CDATA[<p>Cloud Functionsは、端的にいえばAWS LambdaのGoogle Cloud版だ。要するに、比較的小規模なコードをデプロイしておいて、何らかのトリガに応じてそれを実行させて、実行された時間分だけ課金される、という仕組みで、俗に「サーバレス」と呼ばれているものの礎の1つでもある。</p>

<h2 id="aws-lambdaと比べて">AWS Lambdaと比べて</h2>

<p>Cloud Functionsの特徴としては：</p>

<ul>
<li>HTTPトリガー（要するにWebhook）が最も基本的なトリガーである。

<ul>
<li>汎用的な仕組みなのでロックインされにくい。</li>
<li>外部サービスなどからのWebhook連携がシンプルに実現できる。HTTPエンドポイントを別途に設定しなくていいので、構成がシンプルだし、ロックインも軽減できる。</li>
</ul></li>
<li>使える言語: Python 3.x, Node.js, Go, (近日サポート？: Ruby, Java, PHP)</li>
<li>Pythonの場合は：

<ul>
<li>依存パッケージは、pipのrequirements.txtに書いておくだけでインストールされる。</li>
<li>HTTPトリガの処理にはFlaskが使われる。</li>
</ul></li>
</ul>

<p>AWS Lambdaと比べて、簡便であることを重視している印象を受ける。</p>

<h2 id="functionを作る">Functionを作る</h2>

<p>基本的には公式ドキュメントの<a href="https://cloud.google.com/functions/docs/first-python">Your First Function: Python</a>の通りにすればいいので、そこに載ってないことだけ書いておく。</p>

<h3 id="functions-frameworkを使う">Functions Frameworkを使う</h3>

<p>最近になってFunctions Frameworkというライブラリが用意されたようだ。これは、実行環境（ローカル環境 / Cloud Functions / Cloud Run）を選ばずに、Functionを実行するためのもので&hellip;といっても大げさなものではなくて、例えばPython版の場合は単にFlaskやGunicornによる実行を遮蔽しているに過ぎない。</p>

<p>Functions Frameworkを利用して、Functionを作ってみる。</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-bash" data-lang="bash">$ pip3 install functions-framework -U</code></pre></div>
<p>main.py</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-python" data-lang="python"><span style="color:#66d9ef">def</span> <span style="color:#a6e22e">hello</span>(request):
    <span style="color:#66d9ef">return</span> <span style="color:#e6db74">&#34;Hi&#34;</span></code></pre></div>
<p>requirements.txt</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-bash" data-lang="bash"><span style="color:#75715e"># 今回は空っぽ</span></code></pre></div>
<p>そして以下のように実行すると、Flaskアプリケーションとして実行される：</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-bash" data-lang="bash">$ functions-framework --target<span style="color:#f92672">=</span>hello --debug</code></pre></div>
<h3 id="functionをデプロイする">Functionをデプロイする</h3>

<p>以下のコマンドでデプロイできる。デプロイを上書きする時も同じ。</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-bash" data-lang="bash">$ gcloud functions deploy &lt;name&gt; <span style="color:#ae81ff">\
</span><span style="color:#ae81ff"></span>    --project &lt;project-name&gt; <span style="color:#ae81ff">\
</span><span style="color:#ae81ff"></span>    --region<span style="color:#f92672">=</span>asia-northeast1 <span style="color:#ae81ff">\
</span><span style="color:#ae81ff"></span>    --runtime<span style="color:#f92672">=</span>python37 <span style="color:#ae81ff">\
</span><span style="color:#ae81ff"></span>    --entry-point<span style="color:#f92672">=</span>hello <span style="color:#ae81ff">\
</span><span style="color:#ae81ff"></span>    --trigger-http <span style="color:#ae81ff">\
</span><span style="color:#ae81ff"></span>    --allow-unauthenticated</code></pre></div>
<p>ここでは以下を指定している。</p>

<ul>
<li>リージョンはasia-northeast1（東京）</li>
<li>ランタイムはPython3.7</li>
<li>トリガはHTTP</li>
<li>認証なし</li>
</ul>

<p>デプロイには1分くらいかかる。</p>

<h2 id="他のgoogle-cloudサービスを呼ぶ">他のGoogle Cloudサービスを呼ぶ</h2>

<p>作ったFunctionから他のGoogle Cloudサービスを呼ぶには当然ながらCredentials（認証情報）が必要になる。Cloud Functionsにおいては、環境変数<code>GOOGLE_APPLICATION_CREDENTIALS</code>を明示的に設定していない限り、デフォルトのCredentialsが自動的に取得される（内部的には、公式ライブラリがMetadata Serviceから取得してきてくれるっぽい）。</p>

<p><a href="https://google-auth.readthedocs.io/en/latest/reference/google.auth.html#google.auth.default">https://google-auth.readthedocs.io/en/latest/reference/google.auth.html#google.auth.default</a></p>

<p>この仕組みによって、コードベースなどにCredentialsを入れることなしに、他のGoogle Cloudのサービスを呼ぶことができる。例えばCloud Visionを使いたいなら、以下のようにするだけでいい。</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-python" data-lang="python">client <span style="color:#f92672">=</span> vision<span style="color:#f92672">.</span>ImageAnnotatorClient()</code></pre></div>
<p>Credentials自体が必要なときは、google-authモジュールを使えばいい。</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-python" data-lang="python"><span style="color:#f92672">import</span> google.auth
(creds, _) <span style="color:#f92672">=</span> google<span style="color:#f92672">.</span>auth<span style="color:#f92672">.</span>default(scopes<span style="color:#f92672">=</span>[<span style="color:#e6db74">&#34;https://www.googleapis.com/auth/drive&#34;</span>])
drive_service <span style="color:#f92672">=</span> build(<span style="color:#e6db74">&#34;drive&#34;</span>, <span style="color:#e6db74">&#34;v3&#34;</span>, credentials<span style="color:#f92672">=</span>creds)</code></pre></div>
<h2 id="データストア-永続化">データストア / 永続化</h2>

<p>これはどうすればいいのかまだ考えていない。「ちょっとした自動化」程度のアプリケーションを除けば、多くの場合に、状態を保存するための場所が必要になる。Cloud Firestoreが最も手軽だろうか。</p>

<h2 id="デプロイ後の監視など">デプロイ後の監視など</h2>

<h3 id="エラー追跡">エラー追跡</h3>

<ul>
<li>標準の仕組み： 発生した例外は Cloud Error Reporting に集積される。

<ul>
<li>ただし通知はメール通知のみ</li>
</ul></li>
<li><a href="https://sentry.io/">Sentry</a>などのサードパーティのエラーモニタリングサービスのほうが高機能

<ul>
<li>サーバレス環境用のPythonデコレータが<a href="https://docs.sentry.io/platforms/python/serverless/">提供されている</a>。</li>
<li>Slackへの通知などもできる</li>
</ul></li>
</ul>

<h3 id="ロギング">ロギング</h3>

<ul>
<li>標準の仕組み： 以下は Cloud Logging に記録される

<ul>
<li>stdout, stderrへの出力</li>
<li>Cloud Logging ライブラリによるログ出力（Pythonのloggingモジュール用のハンドラあり）</li>
</ul></li>
<li>用途によっては<a href="https://www.papertrail.com/">Papertrail</a>や<a href="https://logdna.com/">logDNA</a>といった、ログをLive tailできるサービスも便利かもしれない（これらにはPythonのloggingモジュール用のハンドラもある）</li>
</ul>]]></content:encoded>
       </item>
       
       <item>
         <title>CloudflareによるロードバランシングとZero-Downtime Failover</title>
         <link>https://ciscorn.net/2020/05/01/cloudflare_lbzd/</link>
         <pubDate>Fri, 01 May 2020 00:35:00 +0900</pubDate>
         
         <guid>https://ciscorn.net/2020/05/01/cloudflare_lbzd/</guid>
         <content:encoded><![CDATA[<p>格安VPSなどでWebサーバのクラスタを組む場合に、Cloudflareのロードバランシング機能が便利に使えることがある。Cloudflare自体がそもそもリバースプロクシなので、そのリバースプロクシから複数のOriginへとリクエストを振り分ければ負荷分散やフェイルオーバーが行えるという仕組みだ。</p>

<p>格安VPSとこれを組み合わせるメリットとしては例えば：</p>

<ul>
<li>格安VPS事業者がロードバランサを提供していなくても使える</li>
<li>格安VPS事業者のあやしいロードバランサより優れている（かも）

<ul>
<li>ヘルスチェックの結果をちゃんとメールしてくれる</li>
<li>信頼性が高そう、おかしな挙動が少なそう</li>
<li>高速フェイルオーバー（後述）ができる。</li>
</ul></li>
<li>そこそこ安い:

<ul>
<li>Origin2つまでは $5/mo</li>
<li>Originを1つ増やすごとに +$5/mo</li>
</ul></li>
<li>2019年末に &ldquo;Zero-Downtime Failover&rdquo; という機能が実装された（有料プランのみで使える）。リクエストを送ろうとしたOriginがダウンしていた場合に、別のOriginに1回だけリトライしてくれる。つまりヘルスチェックによる死活監視を待たずに即座にフェイルオーバーが行われる。</li>
</ul>

<p>短所としては：</p>

<ul>
<li>Cloudflareを経由するのでリクエストのRTTが10~25ms程度増える。これが問題になるかどうかは用途による。</li>
<li>HTTP/S以外には使えない</li>
<li>WebSocketを使う場合、同時接続数の許容値がよくわからない。</li>
</ul>

<p>あと、もちろん、グローバルな負荷分散が行えるとかのメリットもあるけど、ここでの文脈では関係ない。</p>

<h2 id="zero-downtime-failover">Zero-Downtime Failover</h2>

<p>これはウェブのリバースプロクシによくある機能で、例えばnginxの<code>proxy_next_upstream</code>のようなものだ。Cloudflareでは次のように機能する:</p>

<p>A/AAAAレコードが複数用意されている場合またはCloudflare Load Balancingを使っている場合に、1つ目のOriginに対するリクエストでそのOriginが落ちていたら、2つ目のOriginに対してリトライする。このリトライは1リクエストについて1度だけ行われる。</p>

<p>これによってヘルスチェックを待たずに即座にフェイルオーバーできる。Cloudflareのヘルスチェック間隔は60秒（課金すると15秒まで縮められる）と結構長いので、これ重要。</p>

<p>ちなみに、この機能は2019年末ごろに追加されたようで、CloudflareのFreeプランでは使えず、有料プランのみで使える。</p>

<p>このZero-Downtime Failoverは、特に何も設定しなくても自動で行われるはずなのだけど、自分は以下のワナにはまって上手く動かなくなっていた。</p>

<h3 id="zero-downtime-failoverが機能しなくなるワナ">Zero-Downtime Failoverが機能しなくなるワナ</h3>

<p>Load BalancerのPoolにOriginサーバを列挙する際に、IPアドレスではなくホスト名でOriginsを指定するとZero-downtime Failoverが機能しなくなる (2020年4月末時点)。</p>]]></content:encoded>
       </item>
       
       <item>
         <title>Nature Remo E liteでスマートメーターから使用電力を読む</title>
         <link>https://ciscorn.net/2020/04/30/remo_e_lite/</link>
         <pubDate>Thu, 30 Apr 2020 20:35:00 +0900</pubDate>
         
         <guid>https://ciscorn.net/2020/04/30/remo_e_lite/</guid>
         <content:encoded><![CDATA[<p>自分が住んでいるアパートには既にスマートメータが設置されているので、スマートメータと通信できる機器さえ用意すればば現在の使用電力を読み取ることができる。今回、Nature Remo E liteという比較的安めの機器が発売されたので買ってみた。</p>

<h2 id="nature-remo-e-lite">Nature Remo E lite</h2>






<figure style="">
  <img data-original="/2020/04/30/remo_e_lite/feature.jpg" src="/2020/04/30/remo_e_lite/feature.jpg" />
  
</figure>


<p>Nature Remo Eは、「Nature Remo」というIoT赤外線リモコンを主力商品として出している会社が作ったHEMSコントローラだ。</p>

<p>上位版の Nature Remo E（約3万円）は2019年末に出ていたが、今回、そこから太陽光発電や蓄電池システムとの通信機能を省いたNature Remo E liteという廉価版（1.4万円）が発売された。liteのほうはスマートメーターとの通信のみに限定されている。</p>

<p>Nature Remo E lite は、元々は2020年3月に発売される予定で、自分は2月の初頭に予約してあったのだけど、けっきょく4月末になって発売されて、今日ようやく届いた。</p>

<h2 id="東北電力でのbルート開通手続き">東北電力でのBルート開通手続き</h2>

<p>スマートメータと電力使用者のHEMS機器とのあいだの通信のことを「Bルート」と呼ぶらしい。Bルートサービスを利用するには、電力会社に申請してIDとパスワードを得る必要がある。</p>

<p>東京電力などではウェブから申請できるらしいが、（2020年4月時点で）東北電力にはそういう仕組みが無いので、書面で申請することになる。</p>

<p>自分の場合は、Webで検索すると出てくる「Bルートサービス利用申込書」という書類を印刷して、担当地域の営業所に郵送した。1週間後くらいにIDとパスワードが書かれた紙が郵送されてきた。</p>

<h2 id="セットアップはこんな感じ">セットアップはこんな感じ</h2>

<ol>
<li>Remo E liteをコンセントに指す。</li>
<li>Nature Remoアプリを開いて、デバイス登録 → Remo E liteを登録</li>
<li>Wi-Fiのパスワードを入力する。</li>
<li>BルートのIDとパスワードを入力する（BルートのIDがけっこう長くて入力が面倒）。</li>
<li>何も表示されないなーと思いつつ3分くらい待っていると情報が表示された。</li>
</ol>

<p>アプリの画面はこんな感じ：</p>






<figure style="">
  <img data-original="/2020/04/30/remo_e_lite/IMG_0402.jpg" src="/2020/04/30/remo_e_lite/IMG_0402.jpg" />
  
</figure>


<h2 id="使用感など">使用感など</h2>

<ul>
<li>現在の電力値が表示されなくなることがたびたびある。スマートメータとの通信がロストしてるっぽい？</li>
<li>スマートメータとの通信状況（通信がうまくいってるかそうでないか）がよく分からない。</li>
<li>アプリがまだまだ未成熟という感じで必要最小限の機能しかない。グラフ表示の日付切り替えの動作がおかしい。まだ発売されたばかりなので今後のアップデートに期待。</li>
<li>家全体の合計消費電力がわかるだけ（＋グラフとして残るだけ）なので、これがどれだけ役に立つかはまだ分からない。</li>
</ul>

<h2 id="apiも使えるらしいが">APIも使えるらしいが</h2>

<p>情報取得用のAPIもあるみたいだけどまだ試してない。</p>]]></content:encoded>
       </item>
       
       <item>
         <title>スマホ用ジンバル Osmo Mobile 3 を買った</title>
         <link>https://ciscorn.net/2020/04/14/osmo_mobile_3/</link>
         <pubDate>Tue, 14 Apr 2020 17:07:03 +0900</pubDate>
         
         <guid>https://ciscorn.net/2020/04/14/osmo_mobile_3/</guid>
         <content:encoded><![CDATA[<p>動画を撮る用事があって、試しにスマホ用3軸ジンバル「Osmo Mobile 3」を買った。スタンドとケースも付いているエディションを買って、約13,000円だった。</p>

<h2 id="スマホでシネマティック撮影">スマホでシネマティック撮影</h2>

<p>今回Osmo Mobile 3を買おうと思ったのは、こういう動画を見てちょっと衝撃を受けたため。</p>


<div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;">
  <iframe src="//www.youtube.com/embed/-wYJGHW0-WA" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" allowfullscreen title="YouTube Video"></iframe>
</div>


<div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;">
  <iframe src="//www.youtube.com/embed/BzFBwyrzDz0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" allowfullscreen title="YouTube Video"></iframe>
</div>


<p>スマホのレンズなので、ボケなどの表現は難しいし、暗所には弱いけど、それでもこれくらいの動画が撮れるならかなり使い道があるなと思った。</p>

<p>実はこの種の3軸ジンバルは、平行移動方向の揺れ（たとえば歩行による揺れ）を吸収するのが苦手らしいのだけど、最近のスマホに付いている光学式手ブレ補正と組み合わせることで、その点もある程度補えるようだ。</p>

<h2 id="開封-初使用">開封＆初使用</h2>

<p>開封の様子も一応撮っておいた。</p>


<div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;">
  <iframe src="//www.youtube.com/embed/LgkF5Kksg48" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" allowfullscreen title="YouTube Video"></iframe>
</div>


<p>そして、近くの神社に行って初めて使ってみて撮れた動画がこれ。</p>


<div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;">
  <iframe src="//www.youtube.com/embed/onoPblAeqiI" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" allowfullscreen title="YouTube Video"></iframe>
</div>


<p>この程度の安定性が限界ということはまったくなくて、あくまでも初めて使ってサンダル履きで適当に歩いてもこれくらいになるというもの。滑らかな歩き方とかスタビライザーの構え方を練習すれば、もっと上手に撮れると思う。</p>

<h2 id="良い点">良い点</h2>

<p>数年前にもこういう電子ジンバルに触れてみたことがあるけど、それとは比べ物にならないくらい良くできている。</p>

<ul>
<li>バッテリーがかなり持つ（公称10時間以上？）</li>
<li>バッテリー内蔵でUSB Type-Cで充電できる</li>
<li>値段がとにかく安い</li>
<li>スマホアプリとジンバルが通信して連携する</li>
<li>そこそこコンパクトに折り畳める</li>
</ul>

<p>いまいちな点</p>

<ul>
<li>チルト方向（いわゆる上下）の可動域がちょっと狭いので、通常の持ち方からぶら下げ撮影にシームレスに移行できない。</li>
</ul>]]></content:encoded>
       </item>
       
       <item>
         <title>第一級アマチュア無線技士（１アマ）試験の急きょ中止が前日深夜に通知された件</title>
         <link>https://ciscorn.net/2020/04/04/1ama_covid19/</link>
         <pubDate>Sat, 04 Apr 2020 12:10:00 +0900</pubDate>
         
         <guid>https://ciscorn.net/2020/04/04/1ama_covid19/</guid>
         <content:encoded><![CDATA[

<p>ここ１か月半くらい第一級アマチュア無線技士の免許の勉強をしていた（ちなみに自分はアマチュア無線の経験が一切ない完全なる入門者）。この試験が&hellip;新型コロナウイルスの影響で、試験前日の深夜23時頃に、試験の急遽中止が受験者に通知されるという珍事&hellip;が起きたので、その経緯を時系列的に記録しておく。</p>

<h2 id="3月30日-予定通り実施の告知">(3月30日) 予定通り実施の告知</h2>

<p>3月31日に日本無線協会から「無線従事者試験の実施について」という書面が届いた。内容としては、4月4日と5日の１アマと２アマの試験は予定通り実施するということと、今回の受験を取り止めたい人には、次回の試験への振替または受験料の返還に応じるというものだった。</p>

<h2 id="3日19時-産経新聞が記事を出す">(3日19時) 産経新聞が記事を出す</h2>

<p>中止の決定が行われる前の3日19時過ぎに産経新聞がこんな記事を出した。偶然にもブラウザのレコメンド検索機能にこの記事がひっかったので、自分も20時くらいに読んでいた。</p>

<p><a href="https://www.sankei.com/economy/news/200403/ecn2004030037-n1.html">週末のアマチュア無線の国家試験実施へ 国の方針に批判も</a> [産経ニュース 2020-04-03 19:12]</p>

<p>一部を引用しておく：</p>

<blockquote>
<p>新型コロナウイルスの流行を受け、東京都などが不要不急の外出自粛を呼び掛け、民間の小売店なども営業を中止するなど、感染拡大防止への取り組みが広がる中、主に趣味のための同試験を実施する国の方針には批判も上がりそうだ。</p>

<p>（中略）</p>

<p>ただ、受験予定者の中からは「アマチュア無線は不要不急の資格で、中止すべきだ」との声も上がる。試験時間は１日計５時間と長く、閉鎖空間に多くの人が集まるため、感染拡大のリスクが高まるためだ。</p>
</blockquote>

<h2 id="3日23時-試験中止の連絡が来る">(3日23時) 試験中止の連絡が来る</h2>

<p>試験前日の夜23時過ぎになって日本無線協会の担当支部から中止を知らせるSMSが届いた。</p>

<p>なぜSMSで？と思いつつ、念のために日本無線協会のサイトを見に行ったら、（かなり目につきにくい場所に）中止のお知らせが書いてあった。</p>






<figure style="">
  <img data-original="/2020/04/04/1ama_covid19/feature.png" src="/2020/04/04/1ama_covid19/feature.png" />
  
</figure>


<p>自分が申し込んだ試験地はいつも受験者が少ないようなので、感染リスクを気にする感じでもなかった。ただ、東京会場はどうやら百人以上受験するそうなので、中止になること自体は仕方ないと思う。もちろん前日の深夜にドタキャンというのはひどいと思うのだけど。</p>

<h2 id="4日0時-試験中止を伝える記事が出る">(4日0時) 試験中止を伝える記事が出る</h2>

<p>中止決定を受けて、深夜にこの記事が出た。</p>

<p><a href="https://www.sankei.com/economy/news/200404/ecn2004040001-n1.html">アマチュア無線の試験、急遽中止に　受験者への通知間に合わず</a> [産経ニュース 2020-04-04 00:49]</p>

<h2 id="追記-その後の手続きについての書類-4月6日付-が届く">追記: その後の手続きについての書類 (4月6日付) が届く</h2>

<p>日本無線協会から封書が届いた。内容は、9月期への振替受験または受験料返金のための書類をまだ提出していない人は提出してくれということと、当日に試験地に来てしまった人には交通費や宿泊費を補償するというものだった。</p>

<p>84円切手付きの返信用封筒も同封されていた。</p>

<h2 id="おわりに">おわりに</h2>

<p>受験すればまず落ちることはないだろうと思っていたので、けっこうガックリきた。</p>

<p>次の試験は9月末&hellip; とりあえず3級とかを取るという選択肢もあるかもしれないけど、3級の試験もいつ再開されるのか分からない状況だ。</p>
]]></content:encoded>
       </item>
       
       <item>
         <title>アマチュア無線の試験勉強を始めた</title>
         <link>https://ciscorn.net/2020/03/11/1ama_resume/</link>
         <pubDate>Wed, 11 Mar 2020 23:28:10 +0900</pubDate>
         
         <guid>https://ciscorn.net/2020/03/11/1ama_resume/</guid>
         <content:encoded><![CDATA[<p>アマチュア無線の試験（いきなり１級）を受けてみようと思い立って、昨年の終わり頃に参考書を眺めていたのだけど、その後、正月から身内のバタバタが重なって勉強が完全にストップしていた。</p>

<p>気づいたら試験日（4月4日）まで残り40日余りになってしまっていて、今回はパスすべきかとも思ったが、思い切って受験申請日ギリギリで申し込んだ。</p>

<p>いまのところ<a href="https://www.amazon.co.jp/dp/4789811751/">工学問題の入門書</a>を１冊買ってざっと目を通すことしかしていない。しかしこの本は網羅性がいまいち良くなさそうなのと、最新の版が2012年なので新しい問題に対応できていない。特にここ数年で問題の傾向が変わっているらしいので、この本だけに頼るのはまずそう。</p>

<p>法規問題に関しては手を付けてすらいなかったので、法規問題が載ってる参考書を追加で１つ買った。見た感じ覚えることはそれほど多くなさそう。ただし、いかんせん入門者なのでモールス符号とかQ符号とかはゼロから覚えないといけない。</p>

<p>暗記系の事柄については<a href="https://apps.ankiweb.net">Anki</a>を使うことにした。AnkiはいわゆるSRSというもので、出題頻度を自動調整してくれる暗記カードといったもの。iOS/Android版（有料）もある。</p>

<p>というわけで以下のような感じで勉強をやり始めた：</p>

<ul>
<li>参考書２つ

<ul>
<li><a href="https://www.amazon.co.jp/dp/4789811751">基礎からよく分かる無線工学</a></li>
<li><a href="https://www.amazon.co.jp/dp/450133200X/">第1級ハム集中ゼミ</a></li>
</ul></li>
<li>過去問ベースの工学問題解説サイト: <a href="http://www.gxk.jp/elec/musen/1ama/index.html">無線工学を基礎から学ぶ - 第１級アマチュア無線技士</a>

<ul>
<li>このサイトは素晴らしい。過去問ベースなので工学はここだけ見てれば大丈夫そう。</li>
</ul></li>
<li>暗記系はAnkiにデッキを作って、やる</li>
<li><a href="http://www.dental.gr.jp/jh3kcw/kokushi/kokushi.htm">過去問PDF</a>をやる（特に法規）

<ul>
<li>iPad/macOSのNotabilityというアプリにPDFをつっこんだら何だかいい感じになった。</li>
</ul></li>
</ul>

<p>単に試験に合格するだけなら実際の問題を解きまくればいいのは分かりきっているので、そういう勉強は好きではないけど、今回は効率重視でやることにした。</p>
]]></content:encoded>
       </item>
       
       <item>
         <title>小鳥用の木工</title>
         <link>https://ciscorn.net/2020/02/12/woodern/</link>
         <pubDate>Wed, 12 Feb 2020 22:03:15 +0900</pubDate>
         
         <guid>https://ciscorn.net/2020/02/12/woodern/</guid>
         <content:encoded><![CDATA[<p>木工した。</p>

<p>インコのケージの前に置くお立ち台。これでケージの出口からそのまま歩いて出てこれるようになる。</p>






<figure style="">
  <img data-original="/2020/02/12/woodern/feature.jpg" src="/2020/02/12/woodern/feature.jpg" />
  
</figure>]]></content:encoded>
       </item>
       
       <item>
         <title>[Python] Starlette (ASGI) で Graphene v3 (GraphQL) を使う</title>
         <link>https://ciscorn.net/2020/01/02/starlette_graphene3/</link>
         <pubDate>Thu, 02 Jan 2020 03:02:00 +0900</pubDate>
         
         <guid>https://ciscorn.net/2020/01/02/starlette_graphene3/</guid>
         <content:encoded><![CDATA[<p>Python用のGraphQLフレームワークの Graphene のバージョン v3-beta を、ASGIフレームワークのStarlette上で使いたかったので簡単なライブラリを書いた。</p>

<p><a href="https://github.com/ciscorn/starlette-graphene3">https://github.com/ciscorn/starlette-graphene3</a></p>

<p>Subscriptions (via WebSocket) にも対応させてある。</p>

<p>Starletteは標準でもGraphene v2のサポートを備えている (<code>starltte.graphql</code>) のだけど、このGraphene v2は内部で <code>graphql-core-legacy</code> という古いライブラリに依存しているため、文字通りレガシーなものになってしまっている。</p>

<p>次期Graphene v3の土台になっている <code>graphql-core</code>は、Python 2を切り捨ててasyncioネイティブに書き直されているので、非同期処理が簡潔に実装されていて、非同期のWebフレームワークやsubscriptionsに容易に対応できる。</p>]]></content:encoded>
       </item>
       
       <item>
         <title>アンテナや伝送線路について知りたければ電磁気学が避けられないみたい...</title>
         <link>https://ciscorn.net/2019/12/27/electromag1/</link>
         <pubDate>Fri, 27 Dec 2019 20:08:11 +0900</pubDate>
         
         <guid>https://ciscorn.net/2019/12/27/electromag1/</guid>
         <content:encoded><![CDATA[<p>素人が独学する場合は、どうやって学べばいいのか&hellip;</p>

<hr />

<p>無線の入門書を読んでいたら「伝送線路のインピーダンス」とか「アンテナのインピーダンス」とか「自由空間のインピーダンス」とかいう概念がしれっと出てきた。インピーダンスというと電気回路のインピーダンスしか聞いたことがなかったので、ろくな説明もなく出てきて頭が混乱したのだけど、調べてみると電磁波の話のようだ。</p>

<p>つまり、（特に高周波のときには）電流を実際に伝えているのは導体や誘電体に沿って伝搬する電磁波であり、伝送線路やアンテナを扱うときはこの電磁波の振る舞いを考えないといけない…ということらしい（？）。波なので反射するし、電磁波なので空間中に放射されたりする。</p>

<p>ただし、例えばアマチュア無線レベルの試験問題なんかだと、この辺については『理解なんてしなくていい。初歩的な事実と数字だけ覚えておけばいい』みたいな扱いになっているので、試験とかのために体系的に理解する必要はまったくないようだ。</p>

<p>&hellip;とはいえ理工学的な興味として、いずれはちゃんと理解できるようになりたい。</p>

<h2 id="資料">資料</h2>

<p>このへんの話題は電波や高周波回路を扱う場合 (この領域をRFっていうらしい) にのみ要求されるので、電気/電子回路のふつうの入門書にはまず載っていない。</p>

<p>そのなかで、CQ出版社のサイトで検索して見つけたこの本は良いかもしれない。</p>

<ul>
<li><a href="https://www.amazon.co.jp/dp/4789830241">電子回路設計のための電気／無線数学</a></li>
</ul>

<p>この本の目次を眺めてみるに、やはり電気回路(電子回路)をやるなら周波数ドメイン（フーリエ変換、ラプラス変換）の感覚が必要で、さらにRFをやるなら電磁気学とかベクトル解析もやりましょうという感じなのかなと思う。</p>

<h3 id="ベクトル解析">ベクトル解析</h3>

<p>電磁気学をやるには、まず初歩的なベクトル解析の感触をつかんでおかないといけない。</p>

<ul>
<li>grad, div, rot, ▽の基本的な定理: <a href="https://eman-physics.net/math/vector02.html">https://eman-physics.net/math/vector02.html</a></li>
<li>ガウスの定理: <a href="https://eman-physics.net/electromag/gauss.html">https://eman-physics.net/electromag/gauss.html</a></li>
<li>ストークスの定理: <a href="https://eman-physics.net/electromag/stokes.html">https://eman-physics.net/electromag/stokes.html</a></li>
<li>平面のグリーンの定理</li>
<li>スカラーポテンシャル、ベクトルポテンシャル</li>
<li><a href="https://ja.wikipedia.org/wiki/四元数">https://ja.wikipedia.org/wiki/四元数</a>（ここまでやらなくてよさそう）</li>
</ul>

<p>今まで全く触れたことがないけど、初歩的な定理を使うだけならそれほど難しくないのかもしれない。</p>

<h3 id="電磁気学">電磁気学</h3>

<p>日本のWikipediaはあまり良くないのだけど、キーワードだけ並べておくと：</p>

<ul>
<li><a href="https://ja.wikipedia.org/wiki/マクスウェルの方程式">https://ja.wikipedia.org/wiki/マクスウェルの方程式</a></li>
<li><a href="https://ja.wikibooks.org/wiki/電磁気学/電磁波の式の導出">https://ja.wikibooks.org/wiki/電磁気学/電磁波の式の導出</a></li>
<li><a href="https://ja.wikipedia.org/wiki/伝送線路">https://ja.wikipedia.org/wiki/伝送線路</a></li>
<li><a href="https://ja.wikipedia.org/wiki/電信方程式">https://ja.wikipedia.org/wiki/電信方程式</a></li>
<li><a href="https://ja.wikipedia.org/wiki/波動方程式">https://ja.wikipedia.org/wiki/波動方程式</a></li>
<li><a href="https://ja.wikipedia.org/wiki/特性インピーダンス">https://ja.wikipedia.org/wiki/特性インピーダンス</a></li>
<li><a href="https://ja.wikipedia.org/wiki/電磁場解析">https://ja.wikipedia.org/wiki/電磁場解析</a></li>
<li><a href="https://ja.wikipedia.org/wiki/FDTD法">https://ja.wikipedia.org/wiki/FDTD法</a>

<ul>
<li><a href="https://www.eecs.wsu.edu/~schneidj/ufdtd/">Understanding the FDTD Method</a> ←これ面白そう</li>
</ul></li>
<li><a href="https://ja.wikipedia.org/wiki/境界要素法">https://ja.wikipedia.org/wiki/境界要素法</a></li>
<li><a href="https://ja.wikipedia.org/wiki/表皮効果">https://ja.wikipedia.org/wiki/表皮効果</a></li>
</ul>]]></content:encoded>
       </item>
       
       <item>
         <title>BJTとMOSFETとJFETの回路記号のメモ</title>
         <link>https://ciscorn.net/2019/12/24/bjtfetasy/</link>
         <pubDate>Tue, 24 Dec 2019 21:45:15 +0900</pubDate>
         
         <guid>https://ciscorn.net/2019/12/24/bjtfetasy/</guid>
         <content:encoded><![CDATA[<p>なんだかよくわからないのでメモしておく。</p>

<h2 id="bjt">BJT</h2>






<figure style="">
  <img data-original="/2019/12/24/bjtfetasy/bjt.png" src="/2019/12/24/bjtfetasy/bjt.png" />
  
</figure>


<ul>
<li>矢印はEmitterにつける</li>
<li>矢印は電流の方向を示す

<ul>
<li>NPN: B→E（C→E）</li>
<li>PNP: E→B（E→C）</li>
</ul></li>
</ul>

<h2 id="fet">FET</h2>

<p>MOSFETとJFETについて。</p>

<p>一部の製品を除いてFETに極性はなく、電流の方向に基づいて便宜的にソースやドレインと呼んでいるに過ぎない。</p>

<p>ただしディスクリート型3端子のMOSFETでは、ボディ（a.k.a. バックゲート）とソースが内部的に接続されているので、ソースとドレインを区別する必要がある。つまり極性がある。</p>

<ul>
<li>N-channelのFETは、ゲートの電位を上げるとドレイン→ソース電流が流れる。</li>
<li>P-channelのFETは、ゲートの電位を下げるとソース→ドレイン電流が流れる。</li>
</ul>

<p>MOSFETは、プロセスに応じて、エンハンスメント型、デプレッション型の両方が作れる。</p>

<h3 id="jfet">JFET</h3>

<p>ゲートの矢印は、チャネルを開けるための電圧の印加方向を示している。</p>

<p>JFETはデプレッション型。</p>

<h3 id="mosfet">MOSFET</h3>

<ul>
<li>バックゲートの矢印は、寄生ダイオードの方向を示している。</li>
<li>ディスクリート型3端子のMOSFETはソース-バックゲート間が結ばれている。回路図記号でもそれが表現されている。</li>
</ul>

<p>




<figure style="">
  <img data-original="/2019/12/24/bjtfetasy/feature.png" src="/2019/12/24/bjtfetasy/feature.png" />
  
</figure>

￼</p>]]></content:encoded>
       </item>
       
       <item>
         <title>テブナンの定理...の雑な考察</title>
         <link>https://ciscorn.net/2019/12/17/thevnin/</link>
         <pubDate>Tue, 17 Dec 2019 19:03:00 +0900</pubDate>
         
         <guid>https://ciscorn.net/2019/12/17/thevnin/</guid>
         <content:encoded><![CDATA[<p>テブナンの定理はなぜ成り立つのか。手元の本に書いてないので自分で考えてみた。</p>

<p>なんだか、すごく間違ってる気がする。</p>






<figure style="">
  <img data-original="/2019/12/17/thevnin/feature.png" src="/2019/12/17/thevnin/feature.png" />
  
</figure>


<p>（以下正しいのかどうか分からない）</p>

<p>線形回路は、次の連立一次方程式で表現できる。（電流源を含む場合はダメ？）</p>

<p>$$Z \boldsymbol{i} = \boldsymbol{v}$$</p>

<p>これは次のように変形できる：</p>

<p>$$Z \left(\begin{matrix} \frac{1}{a} &amp; 0 &amp; 0 &amp; 0 \\ 0 &amp; 1 &amp; 0 &amp; 0 \\ 0 &amp; 0 &amp; \ddots &amp; 0 \\ 0 &amp; 0 &amp; 0 &amp; 1  \end{matrix}\right) \left(\begin{matrix} a &amp; 0 &amp; 0 &amp; 0 \\ 0 &amp; 1 &amp; 0 &amp; 0 \\ 0 &amp; 0 &amp; \ddots &amp; 0 \\ 0 &amp; 0 &amp; 0 &amp; 1  \end{matrix}\right) \boldsymbol{i} = \boldsymbol{v}$$</p>

<p>左辺の右側（下式）は、電流 $i_1$ の大きさをa倍することを意味する。</p>

<p>$$\left(\begin{matrix} a &amp; 0 &amp; 0 &amp; 0 \\ 0 &amp; 1 &amp; 0 &amp; 0 \\ 0 &amp; 0 &amp; \ddots &amp; 0 \\ 0 &amp; 0 &amp; 0 &amp; 1  \end{matrix}\right) \boldsymbol{i}$$</p>

<p>左辺の左側（下式）は、電流 $i_1$ が流れるエッジにあるインピーダンスの大きさを$\frac{1}{a}$倍することを意味する。</p>

<p>$$Z \left(\begin{matrix} \frac{1}{a} &amp; 0 &amp; 0 &amp; 0 \\ 0 &amp; 1 &amp; 0 &amp; 0 \\ 0 &amp; 0 &amp; \ddots &amp; 0 \\ 0 &amp; 0 &amp; 0 &amp; 1  \end{matrix}\right)$$</p>

<p>これらが意味するのは、電流 $i_1$ が流れるエッジだけを見たときに、電流 $i_1$ の大きさと、そのエッジ上にあるインピーダンス $z_1$ の大きさは反比例の関係にあるということだ。</p>

<p>$$z_1 i_1 = \text{const.}$$</p>

<p>const.の項は起電力や電圧降下の和を表しているとすると、この式は上図の等価回路を表現していることがわかる。</p>

<p>$$ Z_{eq} I = V_{eq} - V $$</p>

<p>つまり：</p>

<p>$$ V = V_{eq} - Z_{eq} I $$</p>]]></content:encoded>
       </item>
       
       <item>
         <title>matplotlibのプロットを動画にする</title>
         <link>https://ciscorn.net/2019/12/15/matplotlib_anim/</link>
         <pubDate>Sun, 15 Dec 2019 23:30:00 +0900</pubDate>
         
         <guid>https://ciscorn.net/2019/12/15/matplotlib_anim/</guid>
         <content:encoded><![CDATA[<p>H.264の動画を作るところまでmatplotlibが面倒をみてくれるようなので試してみた。要ffmpeg。</p>

<p>プロットの内容は、1次元と2次元の波動方程式を最も単純なやりかたで数値シミュレーションしてるだけ。</p>






<figure style="">
  <img data-original="/2019/12/15/matplotlib_anim/feature.jpg" src="/2019/12/15/matplotlib_anim/feature.jpg" />
  
</figure>


<p>matplotlibのAPIは元々いまいちわかりにくいのだけど、3Dプロット周りとかアニメーション周りのAPIは更にわかりにくい。</p>

<p>２次元：</p>


<div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;">
  <iframe src="//www.youtube.com/embed/_OPM7xKGMjU" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" allowfullscreen title="YouTube Video"></iframe>
</div>


<p>こっちは１次元。つまらない。</p>


<div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;">
  <iframe src="//www.youtube.com/embed/tYokkF5iujE" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" allowfullscreen title="YouTube Video"></iframe>
</div>


<p>Pythonのコードは以下の通り。もっと効率的に計算できるだろうけど、安直にやっている。</p>

<script type="application/javascript" src="//gist.github.com/ciscorn/109eafc330cb3384b7614afd5b917814.js"></script>]]></content:encoded>
       </item>
       
       <item>
         <title>昔Wonderflで書いたコードを発掘した</title>
         <link>https://ciscorn.net/2019/12/15/wonderfl/</link>
         <pubDate>Sun, 15 Dec 2019 14:20:00 +0900</pubDate>
         
         <guid>https://ciscorn.net/2019/12/15/wonderfl/</guid>
         <content:encoded><![CDATA[<p>学生のときに４〜５日間くらいだけFlashのコード (ActionScript3) を書いたことがあった。wonderflという投稿サイト上で６個くらい書いたんだけど、その後Flashの滅亡と共にサイトごと消滅していた。</p>

<p>どっかにキャッシュでも残ってないかなと思って検索してみたら、wonderflのコードを律儀にアーカイブしてくれているサイトを見つけた。</p>

<ul>
<li><a href="http://wa.zozuar.org/user.php?name=wanson">http://wa.zozuar.org/user.php?name=wanson</a></li>
<li><a href="http://oreorekaihatsu.blogspot.com/2017/06/wonderfl.html">http://oreorekaihatsu.blogspot.com/2017/06/wonderfl.html</a></li>
</ul>






<figure style="">
  <img data-original="/2019/12/15/wonderfl/feature.jpg" src="/2019/12/15/wonderfl/feature.jpg" />
  
</figure>


<p>こんな感じ：母音音声（あいうえお）の合成、2次元の波紋、鳥や魚の群れの動き(Boid)、ロボットアーム、反射と屈折、揺れる謎の物体。</p>

<hr />

<p>ところで今だったら、こういう可視化＋インタラクティブ性みたいなものって何で作ると筋がいいんだろうか。</p>

<p>カジュアルな用途なら、どう考えてもWeb技術だろう。つまりCanvas, SVG, WebGL, WebAudio, (WebGPU?), etc. を直接使うかライブラリ経由で使うかだ。ブラウザでページを開くだけで動くのは最高だし、Electron製とかのアプリでも使える。</p>

<p>しかしそこで、他の言語と相互運用したい欲求が生じる。具体的にいえば、内部的な計算の部分はPythonで書きたいとかだ。</p>

<p>Pythonに <a href="https://github.com/bokeh/bokeh">Bokeh</a> というライブラリがある。Pythonからデータを供給して可視化だけWebブラウザで行うというものだ。BokehはWebSocketを使ってPython-JS間でデータをやりとりする仕組みらしい（WS周りはtornado）。</p>

<p>手元の可視化で使うだけならこのアプローチでいいだろうけど、Web上で公開するとなるとPythonのサーバを立てなければならなくなる。</p>

<p>WebAssemblyを使うという手もあるだろうけど、カジュアルさはかなり低下しそう。</p>

<p>関係ないけど、こんなライブラリも見つけた：</p>

<ul>
<li><a href="https://github.com/maartenbreddels/ipyvolume/">https://github.com/maartenbreddels/ipyvolume/</a></li>
</ul>]]></content:encoded>
       </item>
       
       <item>
         <title>文字セットの違いで生じる大量のフォントを自動で整理する</title>
         <link>https://ciscorn.net/2019/12/13/organize_fonts/</link>
         <pubDate>Fri, 13 Dec 2019 21:19:59 +0900</pubDate>
         
         <guid>https://ciscorn.net/2019/12/13/organize_fonts/</guid>
         <content:encoded><![CDATA[




<figure style="">
  <img data-original="/2019/12/13/organize_fonts/feature.jpg" src="/2019/12/13/organize_fonts/feature.jpg" />
  
</figure>


<p>モリサワパスポートには大量のフォントが収録されているのだけど、その中には単なる収録文字セットの違い (Std, Pro, Pr5, Pr6など) によるvariantsも数多くある。例えば以下のような感じだ：</p>

<ul>
<li>A-OTF リュウミン Pro (Adobe Japan 1-4)</li>
<li>A-OTF リュウミン Pr5 (Adobe Japan 1-5)</li>
<li>A-OTF リュウミン Pr6 (Adobe Japan 1-6)</li>
<li>A-OTF リュウミン Pr6N (Adobe Japan 1-6, JIS新字形)</li>
</ul>

<p>これらのvariantsが統制なく使われると、結局、データをやりとりする全員がすべてのvariantsをインストールしなければならなくなる。これを避けるために、なるべく最小限のvariantsのみが使われるようにしておきたい。</p>

<p>具体的な方針としては…</p>

<ul>
<li>スーパーセット（例: Pr6N）のファミリが存在するなら、サブセット（例: ProN）のファミリは捨てる。つまり常にスーパーセットを使うようにする。（大きめの文字セットを使うと計算資源も食うようだけど、問題ないだろう）。</li>
<li>字形の違い（例: ProとProN）があるものについては、新字形のみを保持する。</li>
<li>ついでにフォルダ分けもしておく： フォントファミリ名でフォルダ分けする。つまり、同じファミリ名のサブファミリ違いを、同一のフォルダにまとめる。</li>
</ul>

<p>…という感じにしようと思う。さすがにこれを手動でやるわけにはいかないので、作業を自動化した。</p>

<p>Pythonスクリプトはこんな感じ：</p>

<script src="https://gist.github.com/ciscorn/df5506080a048050b37d6575451bdfde.js"></script>
]]></content:encoded>
       </item>
       
       <item>
         <title>１次元波動方程式の一般解をラプラス変換とフーリエ変換で求める</title>
         <link>https://ciscorn.net/2019/12/12/waveeq_fourier/</link>
         <pubDate>Thu, 12 Dec 2019 16:59:13 +0900</pubDate>
         
         <guid>https://ciscorn.net/2019/12/12/waveeq_fourier/</guid>
         <content:encoded><![CDATA[<p>免責事項: 私は電磁気学も流体力学も微分方程式も学んだことはありません。デタラメです。</p>

<p>ラプラス変換とフーリエ変換だけでやってみる試みです。デタラメです。</p>

<hr />

<p>１次元の波動方程式は次の通り：</p>

<p>$$\frac{\partial^2}{\partial^2 x} u(x, t) = \frac{1}{c^2} \frac{\partial^2}{\partial^2 t} u(x, t)$$</p>

<p>初期条件は $u(x, 0) = g(x)$, $u&rsquo;(x, 0) = h(x)$ とする。</p>

<p>$t$については因果的なので、$t$ についてラプラス変換すると:</p>

<p>$$\begin{aligned}\frac{\partial^2}{\partial^2 x} U(x, s) &amp;= \frac{1}{c^2} \left( s^2 U(x, s) - s u(x, 0) - u&rsquo;(x, 0)\right) \\ &amp;= \frac{1}{c^2} \left( s^2 U(x, s) - s g(x) - h(x)\right) \\ \end{aligned}$$</p>

<p>さらに $x$ についてフーリエ変換すると:</p>

<p>$$\begin{aligned}(-j\omega)^2 U(\omega, s) &amp;= \frac{1}{c^2} \left( s^2 U(\omega, s) - s G(\omega) - H(\omega)\right)\end{aligned}$$</p>

<p>これを整理すると：</p>

<p>$$\begin{aligned}U(\omega, s) &amp;= \frac{1}{2c} \cdot \frac{2 (s / c)}{\omega^2 + (s / c)^2} G(\omega) +  \frac{1}{2cs} \cdot \frac{2 (s / c)}{\omega^2 + (s / c)^2} H(\omega)\end{aligned}$$</p>

<p>これを逆フーリエ変換すると（$\circledast$は畳み込みを表すこととする）：</p>

<p>$$U(x, s) = \left[ \frac{1}{2c} e^{-(s/c)|x|} \circledast_x g(x) \right] + \left[ \frac{1}{2cs} e^{-(s/c)|x|} \circledast_x h(x) \right] $$</p>

<p>さらに逆ラプラス変換すると：</p>

<p>$$\begin{aligned}
u(x, t) &amp;= \frac{1}{2c} \delta(t - \frac{|x|}{c}) \circledast_x g(x) + \frac{1}{2c}\int^{t}_{0} \left( \delta(\tau - \frac{|x|}{c}) \circledast_x h(x) \right) d\tau \\
        &amp;= \frac{1}{2} \delta(ct - |x|)  \circledast_x g(x)  + \frac{1}{2} \int^{t}_{0} h(x - c\tau) + h(x + c\tau) d\tau \\
        &amp;= \frac{1}{2} \left( g(x - ct) + g(x + ct) \right) + \frac{1}{2c}\int^{x+ct}_{x-ct}h(\xi) d\xi \\
\end{aligned}$$</p>

<p>Wikipediaに載ってる<a href="https://ja.wikipedia.org/wiki/ダランベールの式">ダランベールの式</a>と同じ解が得られた。</p>

<hr />

<p>合ってるのかな？</p>]]></content:encoded>
       </item>
       
       <item>
         <title>ハートレー発振回路</title>
         <link>https://ciscorn.net/2019/12/07/hartley/</link>
         <pubDate>Sat, 07 Dec 2019 21:30:00 +0900</pubDate>
         
         <guid>https://ciscorn.net/2019/12/07/hartley/</guid>
         <content:encoded><![CDATA[<p>ハートレー発振回路。LC発振回路の一種。　</p>






<figure style="">
  <img data-original="/2019/12/07/hartley/feature.jpg" src="/2019/12/07/hartley/feature.jpg" />
  
</figure>


<p>とある入門書に原理を示す図（簡略化されすぎていて実際には動かない回路図）が載っていて、それを見たら実際に動作する回路の姿が気になったので、<a href="https://wiki.analog.com/university/courses/electronics/comms-lab-hartley-osc">Analog Devicesの教育用ページ</a>を参考に実装した。今回はバイポーラトランジスタ (BJT) を使ったけど、FETのほうが簡単なのかもしれない。</p>

<p>（ちゃんと動作したが、この回路が発振を継続する仕組みについては、いまいち理解できない。）</p>

<p>回路図とシミュレーション結果はこんな感じ。自分が理解しやすいように、先のページに載っていた回路図を変形してある。パラメータを色々いじって低周波（10kHzくらい）で発振させている。</p>






<figure style="">
  <img data-original="/2019/12/07/hartley/ltspice.png" src="/2019/12/07/hartley/ltspice.png" />
  
</figure>


<p>ブレッドボード上に実装：</p>






<figure style="">
  <img data-original="/2019/12/07/hartley/feature.jpg" src="/2019/12/07/hartley/feature.jpg" />
  
</figure>


<p>測定：</p>






<figure style="">
  <img data-original="/2019/12/07/hartley/ad2.jpg" src="/2019/12/07/hartley/ad2.jpg" />
  
</figure>


<p>理論上の発振周波数は11,254Hz。実測値は11,000Hz。</p>

<h3 id="メモ">メモ</h3>

<ul>
<li>トランジスタのB, C, Eの周りをコイルとコンデンサ（LC回路）が取り囲んでいる。</li>
<li>インダクタンスの高いコイルを使うとコイルの直列抵抗が無視できなくなる。</li>
</ul>

<h3 id="仕組み">仕組み？</h3>

<p>出力電圧と、C1からLC回路に流れ込む電流の関係は下図のようになっている。</p>






<figure style="">
  <img data-original="/2019/12/07/hartley/feedback.jpg" src="/2019/12/07/hartley/feedback.jpg" />
  
</figure>


<p>この電流の変化がコイルの逆起電力を高める。</p>]]></content:encoded>
       </item>
       
       <item>
         <title>Python用のGoogle API / GCPライブラリの状況 (2019年末)</title>
         <link>https://ciscorn.net/2019/12/02/googleapi/</link>
         <pubDate>Mon, 02 Dec 2019 22:58:52 +0900</pubDate>
         
         <guid>https://ciscorn.net/2019/12/02/googleapi/</guid>
         <content:encoded><![CDATA[<p>(2019年末)</p>

<p>ここ数年でPython用ライブラリが少し整理されたようで、ちまたの記事は現在の状況に合っていないものが多い。最新っぽいやり方を整理しておく。</p>

<p>最後にGoogle PhotosのAPI (Photos Library API) を呼んでみたので、それについても末尾に書いておく。</p>

<h2 id="oauth2client-はdeprecated"><code>oauth2client</code>はDeprecated</h2>

<p>Web上では<code>oauth2client</code>を使っている記事がたくさん見つかるが、これは既にdeprecatedになっている。現在は以下のようになっている。</p>

<ul>
<li>Service Accountの認証や、Googleのトークンの扱いは、<code>google-auth</code>で実装されている。</li>
<li>OAuth 2.0のフローを行う必要があるときはサードパーティ製の<code>oauthlib</code>などを使う（<code>google-auth-oauthlib</code>というヘルパも用意されている）。</li>
</ul>

<h2 id="ライブラリの構成">ライブラリの構成</h2>

<p>GCP用も含めて、ほとんどのライブラリはここにある：</p>

<ul>
<li><a href="https://github.com/googleapis">https://github.com/googleapis</a></li>
</ul>

<p>以下は（ほぼ）関係ない：</p>

<ul>
<li><a href="https://github.com/GoogleCloudPlatform">https://github.com/GoogleCloudPlatform</a></li>
<li><a href="https://github.com/google">https://github.com/google</a></li>
</ul>

<h3 id="google-cloud-platform-gcp-関係">Google Cloud Platform (GCP) 関係</h3>

<p>GCPの各サービスごとにパッケージ<code>google-cloud-*</code>が用意されている (例えば <code>google-cloud-storage</code>など) 。ちなみに、かつて存在した<code>google-cloud</code>というメタパッケージは、既にdeprecatedで空っぽになっている。</p>

<p>これらは以下のような綺麗な名前空間に収まっている。</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-python" data-lang="python"><span style="color:#f92672">import</span> google.cloud.speech</code></pre></div>
<ul>
<li>repo: <a href="https://github.com/googleapis/google-cloud-python">https://github.com/googleapis/google-cloud-python</a></li>
<li>doc: 各<code>google-cloud-*</code>ごとに個別に存在する。上のURLからたどれる。</li>
</ul>

<p>各サービスで共通の部分は<code>google-api-core</code>, <code>google-cloud-core</code>, <code>google-resumable-media</code>などのパッケージとして抽出されている。これらは必要に応じて依存パッケージとしてインストールされるのであまり気にしなくていい。</p>

<h3 id="google-s-discovery-based-api">Google&rsquo;s Discovery-based API</h3>

<p><code>google-api-python-client</code> - これはGoogleのサービス（Google Driveなど）のAPIを叩くための汎用ライブラリ。API定義をネットから取ってきて、APIを表現するオブジェクトを手元で動的に組みたてる仕組みになっている。</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-python" data-lang="python"><span style="color:#f92672">import</span> googleapiclient</code></pre></div>
<ul>
<li>repo: <a href="https://github.com/googleapis/google-api-python-client">https://github.com/googleapis/google-api-python-client</a></li>
<li>docs: <a href="https://github.com/googleapis/google-api-python-client/blob/master/docs/README.md">https://github.com/googleapis/google-api-python-client/blob/master/docs/README.md</a></li>
<li>docs: <a href="http://googleapis.github.io/google-api-python-client/docs/epy/index.html">http://googleapis.github.io/google-api-python-client/docs/epy/index.html</a></li>
</ul>

<p>ただしGCPを扱うときはこれを使うべきではない。「GCPについてはアクティブに開発されているGCP用のライブラリを使え」とのこと。</p>

<h3 id="google-auth">google-auth</h3>

<p>認証周りを抜き出したもの。Service Accountの認証のほか、トークンのverifyなどもこれでできる。<code>google-cloud-*</code>と<code>google-api-python-client</code>はこれに依存している。</p>

<p>google-auth:</p>

<ul>
<li>repo: <a href="https://github.com/googleapis/google-auth-library-python">https://github.com/googleapis/google-auth-library-python</a></li>
<li>docs: <a href="https://google-auth.readthedocs.io/en/latest/">https://google-auth.readthedocs.io/en/latest/</a></li>
</ul>

<p><code>google-auth</code>には、ユーザクレデンシャルを取得する（=OAuth 2.0のフローを助ける）機能がない（<code>oauth2client</code>にはあった）。アクセストークンを取得したければ、<code>oauthlib</code>などをそのまま使うか、Googleが用意しているヘルパー<code>google-auth-oauthlib</code>を使う。</p>

<p>（もちろん、Webフロントエンドなどがある場合はブラウザ上でGoogle Sign-Inなどを使ってアクセストークンを取得すればいいので、サーバ側はトークンの検証だけすればいい。）</p>

<p>google-auth-oauthlib:</p>

<ul>
<li>repo: <a href="https://github.com/googleapis/google-auth-library-python-oauthlib">https://github.com/googleapis/google-auth-library-python-oauthlib</a></li>
<li>docs: <a href="https://google-auth.readthedocs.io/en/latest/">https://google-auth.readthedocs.io/en/latest/</a></li>
</ul>

<h2 id="認証方法">認証方法</h2>

<p>これはPython特有ではないけど&hellip;</p>

<p>Google Cloud Platformの認証の種類は以下の通り：</p>

<ul>
<li>Service Account - server-to-serverの通信には基本的にこれを使う。OAuth 2.0に基づいている（がOAuth 2.0そのものではない？）。</li>
<li>OAuth 2 - ふつうのOAuth 2.0のフロー。ユーザの同意を得て、ユーザのデータにアクセスするためのトークンを取得するときに使う。</li>
<li>API Key - APIキーによる認証。一部のAPIを使うときに必要。</li>
</ul>

<h3 id="service-account">Service Account</h3>

<p>推奨される方法： クレデンシャルファイルを利用する。環境変数 <code>GOOGLE_APPLICATION_CREDENTIALS</code> でクレデンシャルのjsonファイルのパスを指定しておけば、あとはライブラリが裏で勝手に認証を行ってくれる。</p>

<p>上記の通り、通常はライブラリに任せればいいのだけど、内部的には次のようなことをしている → クライアントの秘密鍵で要求用のJWTを署名して、それをエンドポイントに送信してアクセストークンを取得し、そのトークンでAPIを呼ぶ。（Pythonだと、<code>google/auth/service_accounts.py</code>の<code>refresh()</code>あたりを追うとわかる）。</p>

<p>JWTの署名アルゴリズムにはRS256（256bit RSA暗号）が使われる。一部のAPIでは、上記のJWTそのものをそのままアクセストークンとして使うこともできる。</p>

<h3 id="oauth-2-0">OAuth 2.0</h3>

<p>Googleが提供しているGoogle Sign-In (Web (JS), Android, iOS, TV, etc.) などの認証ライブラリが使える場合はそれを使えばアクセストークンを取得できる。認証時に必要なスコープを要求すればいい。</p>

<p>上記で対応できない場合は、PlainなOAuth 2.0を使う（デスクトップアプリ、CLIツールなど）。</p>

<p>Pythonの場合は、PlainなOAuth 2.0フローを簡便に行うための<code>google-auth-oauthlib</code>パッケージも用意されている。</p>

<h2 id="実際にやってみる">実際にやってみる。</h2>

<p><code>google-cloud-*</code>のほうは分かりにくいことは特にないので触れない。ここでは<code>google-api-python-client</code>を使ってみる。</p>

<p>コマンドラインから自分のアカウントのGoogle Photosの写真を列挙してみることにした。</p>

<p>事前準備として、Google Cloud Consoleで適当なOAuth 2のクライアントIDを作っておく必要がある。</p>

<p>そして、コード自体は以下だけでOK。短い。</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-python" data-lang="python"><span style="color:#f92672">from</span> google_auth_oauthlib.flow <span style="color:#f92672">import</span> InstalledAppFlow
<span style="color:#f92672">from</span> googleapiclient.discovery <span style="color:#f92672">import</span> build

<span style="color:#75715e"># Obtain an access token</span>
flow <span style="color:#f92672">=</span> InstalledAppFlow<span style="color:#f92672">.</span>from_client_secrets_file(
    <span style="color:#e6db74">&#39;client_id.json&#39;</span>,
    scopes<span style="color:#f92672">=</span>[<span style="color:#e6db74">&#39;https://www.googleapis.com/auth/photoslibrary.readonly&#39;</span>])
credentials <span style="color:#f92672">=</span> flow<span style="color:#f92672">.</span>run_local_server()

<span style="color:#75715e"># Call the Photos Library API</span>
photos_service <span style="color:#f92672">=</span> build(<span style="color:#e6db74">&#39;photoslibrary&#39;</span>, <span style="color:#e6db74">&#39;v1&#39;</span>, credentials<span style="color:#f92672">=</span>credentials)
result <span style="color:#f92672">=</span> photos_service<span style="color:#f92672">.</span>mediaItems()<span style="color:#f92672">.</span>list()<span style="color:#f92672">.</span>execute()
<span style="color:#66d9ef">print</span>(result[<span style="color:#e6db74">&#39;mediaItems&#39;</span>])</code></pre></div>
<p>実行するとブラウザが開いてGoogleの認証ページが表示される。デスクトップアプリやCLIのツールでアクセストークンを受け取るためのトリックとして、ローカルにWebサーバを立ててそこをOAuthフローのリダイレクト先にする、ということを行うのだが、その一連の処理は<code>google_auth_oauthlib</code>の<code>InstalledAppFlow</code>がすべてやってくれる。</p>]]></content:encoded>
       </item>
       
       <item>
         <title>単なるRLCのローパスフィルタ（2次のバターワース）</title>
         <link>https://ciscorn.net/2019/11/26/butterworth2/</link>
         <pubDate>Tue, 26 Nov 2019 23:55:31 +0900</pubDate>
         
         <guid>https://ciscorn.net/2019/11/26/butterworth2/</guid>
         <content:encoded><![CDATA[<p>シミュレーション→実装→測定、の練習。</p>

<p>まずLTSpiceでシミュレーションした。2次バターワースフィルタのつもり。</p>






<figure style="">
  <img data-original="/2019/11/26/butterworth2/feature.jpg" src="/2019/11/26/butterworth2/feature.jpg" />
  
</figure>


<p>これでいいのかどうかわからないけど、それっぽい曲線にはなっている。</p>

<p>これをブレッドボード上に実装：</p>

<figure style="">
  <img data-original="/2019/11/26/butterworth2/circuit.jpg" src="/2019/11/26/butterworth2/circuit.jpg" />
  
</figure>


<p>これをAnalog Discovery 2で見たら、ほぼシミュレーション通りの結果になった。</p>






<figure style="">
  <img data-original="/2019/11/26/butterworth2/ad2.jpg" src="/2019/11/26/butterworth2/ad2.jpg" />
  
</figure>


<p>そういえば、初めに測定したときに周波数特性のプロットがひどくガタガタになるなあと思ったら、ジャンパワイヤが錆びていたようで、紙やすりでこすっただけで解決した。</p>

<p>ちなみに矩形波を入力したときの出力はこんな感じ。</p>






<figure style="">
  <img data-original="/2019/11/26/butterworth2/rect.jpg" src="/2019/11/26/butterworth2/rect.jpg" />
  
</figure>]]></content:encoded>
       </item>
       
       <item>
         <title>最近の興味</title>
         <link>https://ciscorn.net/2019/11/25/interestsl2019/</link>
         <pubDate>Mon, 25 Nov 2019 11:00:00 +0900</pubDate>
         
         <guid>https://ciscorn.net/2019/11/25/interestsl2019/</guid>
         <content:encoded><![CDATA[<p>このまえ信号処理の勉強をしたら楽しかったので、趣味をコンピュータサイエンス寄りに傾けようと思った。</p>

<p>さいきん興味があるのは主にこんなところ。</p>

<ul>
<li>アナログ電子回路らへん

<ul>
<li>ハードウェアへの憧憬</li>
</ul></li>
<li>無線工学（？）

<ul>
<li>いままで全く興味なかったけどなんだか楽しそう。自分のほかの興味分野と関連してそうな感じがする。</li>
<li>しかし本質的に理解したいなら電子工学や電磁気学やベクトル解析のセンスが要求される感じ。その辺も楽しいのかもしれないけど、そこまで足をつっこむべきかどうか。</li>
</ul></li>
<li>色彩工学

<ul>
<li>デザイナー向けではなく、エンジニアリング寄りのやつ。重要そうな分野なのに教科書が少なくて謎深い。洋書も少ない。なんでだろ。</li>
</ul></li>
</ul>

<p>いきなり学生向けの教科書を読まずに、まずはライトな入門書をながめようとしている。</p>

<p>どの分野も事前知識がないので学習曲線がsteep。しばらく本を読んで雰囲気をつかむ感じか。</p>]]></content:encoded>
       </item>
       
       <item>
         <title>LPC11U35をPlatformIOで使おうとした</title>
         <link>https://ciscorn.net/2019/11/23/lpc11u35_platformio/</link>
         <pubDate>Sat, 23 Nov 2019 19:13:22 +0900</pubDate>
         
         <guid>https://ciscorn.net/2019/11/23/lpc11u35_platformio/</guid>
         <content:encoded><![CDATA[<p>なんとなく<a href="http://akizukidenshi.com/catalog/g/gK-12144/">秋月のLPC11U35ボード (EA LPC11U35 QuickStart Board互換) </a>を買った。macOS + PlatformIOでLチカさせようとしたら、いくつかハマったすえに、ようやくなんとか動作させられたので、それについて記録しておく。ただしMbed OS 5.xをRTOSなしでビルドすることになるため、USBDeviceなどの古いライブラリは使えないようだ&hellip;。</p>






<figure style="">
  <img data-original="/2019/11/23/lpc11u35_platformio/feature.jpg" src="/2019/11/23/lpc11u35_platformio/feature.jpg" />
  
</figure>


<p>（ちなみに私はマイコンの知識はほとんどないし、MbedもPlatformIOも今回はじめて触るので、暗中模索でやっている。）</p>

<p>このボードの特徴は、Mbed対応のボードとしては安い（1個850円）ことと、シリアル経由ではなくMbed的に単体でUSB経由で書き込めること（USBマスストレージとして認識されるので、そこにfirmware.binを突っこむだけで書き換えられる）。</p>

<h2 id="ハマった点">ハマった点</h2>

<p>Mbed公式のオンラインコンパイラでビルドしたBlinker（Lチカ）をFinderでいくら書き込んでも全く動作しなくて10分くらい悩んでいて、ふと思いついてコマンドラインからcpやddを使って書き込んだらうまくいった（Linuxでも同様？）。</p>

<p>しかし、PlatformIOで同じLチカコードをビルドしても全然動かない。PlatformIOがMbed OS 5.x系を使うからなのかなと思ったが（このボードはMbed OS 2まで対応と書いてあるので、解決は面倒くさそう&hellip;）、そういう話ではなかった。</p>

<p>どうやらマイコン側でファームウェアのチェックサムを調べるらしくて、PlatformIOでビルドしたあとにファームウェアのチェックサムを適切に書き換えてやる必要があるようだ。シリアル (SWD?) で書き込むときは書き込みツールがこの辺の面倒をみてくれるんだと思うけど、mbed的に書き込むときはチェックサムの書き換えが必要になる。</p>

<h2 id="準備">準備</h2>

<p><a href="https://github.com/basilfx/lpc_checksum">LPC用ファームウェアのチェックサムを書き換えるPythonスクリプト</a>を入れておく。</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-sh" data-lang="sh">pip3 install lpc_checksum -U</code></pre></div>
<h2 id="platformioでの開発">PlatformIOでの開発</h2>

<p>PlatformIOを初めて使うので、安直にVSCodeのPlatformIO IDEを使った。</p>

<p>まずふつうに適当なプロジェクトを作る。ボードは EA LPC11U35 QuickStart Board を選択し、フレームワークはmbedを選択する。</p>

<h3 id="アップロードスクリプトを作る">アップロードスクリプトを作る</h3>

<p><code>~/.platformio/nxplpc_osx_usbisp.sh</code>（名前はてきとう）に以下のようなシェルスクリプトを置いておく。ファイルのパーミッションを755にするのを忘れずに。</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-sh" data-lang="sh"><span style="color:#75715e">#!/bin/sh
</span><span style="color:#75715e"></span>
SOURCE<span style="color:#f92672">=</span>$1

lpc_checksum $SOURCE
rm -f /Volumes/CRP<span style="color:#ae81ff">\ </span>DISABLD/firmware.bin
dd <span style="color:#66d9ef">if</span><span style="color:#f92672">=</span>$SOURCE of<span style="color:#f92672">=</span>/Volumes/CRP<span style="color:#ae81ff">\ </span>DISABLD/firmware.bin</code></pre></div>
<p>これは一度作っておけば他のプロジェクトでも使い回せる。</p>

<h3 id="platformio-ini">platformio.ini</h3>

<p>プロジェクト直下の <code>platformio.ini</code> を開き、アップロード時に上記のアップロードスクリプトが実行されるように設定する。</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-ini" data-lang="ini"><span style="color:#66d9ef">[env:lpc11u35]</span>
<span style="color:#a6e22e">platform</span> <span style="color:#f92672">=</span> <span style="color:#e6db74">nxplpc</span>
<span style="color:#a6e22e">board</span> <span style="color:#f92672">=</span> <span style="color:#e6db74">lpc11u35</span>
<span style="color:#a6e22e">framework</span> <span style="color:#f92672">=</span> <span style="color:#e6db74">mbed</span>
<span style="color:#a6e22e">upload_protocol</span> <span style="color:#f92672">=</span> <span style="color:#e6db74">custom</span>
<span style="color:#a6e22e">upload_command</span> <span style="color:#f92672">=</span> <span style="color:#e6db74">$PROJECT_PACKAGES_DIR/nxplpc_osx_usbisp.sh $SOURCE</span></code></pre></div>
<p>これで終わり。</p>

<p>ボードを書き込みモードにして、PlatformIOのUploadを実行すれば書き込まれる。無事にLチカが動いた。</p>

<h2 id="問題">問題</h2>

<p>結局のところ、Mbed OS 5.x系のライブラリを使ってRTOSなしでビルドしているようだ。USBHIDなどのライブラリを使おうとするとビルドが通らなくなる。&hellip;だめじゃん。</p>

<p>それと、自分の設定が悪いのかもしれないけど、PlatformIOでmbedプロジェクトをビルドすると、毎回すべてのファイルをコンパイルするのでめちゃくちゃ時間がかかる。</p>

<p>その後、PlatformIOじゃなくてmbed-cliも少しだけ試したけど、Mbed 2.x 対応 (<code>--mbedlib</code>オプション) 周りのライブラリダウンロードの仕組みが壊れてるっぽい&hellip; 手動でライブラリをダウンロードしてやっと動いた。</p>

<p>つらい。</p>]]></content:encoded>
       </item>
       
       <item>
         <title>ディジタル信号処理の入門書を10年ぶりに読み返した</title>
         <link>https://ciscorn.net/2019/11/22/sigpro_review/</link>
         <pubDate>Fri, 22 Nov 2019 09:00:00 +0900</pubDate>
         
         <guid>https://ciscorn.net/2019/11/22/sigpro_review/</guid>
         <content:encoded><![CDATA[

<p>学生のときのディジタル信号処理の入門書を10年ぶりに読み返した。</p>

<p>学生のときはまったく理解できていなかったなとしみじみ思いつつ、読み直して思い出したこと、分かったことを列挙しておく（<strong>&hellip;どうせまた全部忘れるので</strong>）。</p>

<p>(こういうのを理解して使いこなしている人達ってすごいな&hellip;)</p>

<h3 id="dtft">DTFT</h3>

<ul>
<li>時間ドメインと周波数ドメインの一方を離散化すると相手は周期化する。

<ul>
<li>つまりDTFT では周波数ドメインが周期化する</li>
<li>連続信号のスペクトルとそのサンプリング信号のスペクトルの関係はよく知られている通り。</li>
<li>この関係により、周波数ドメインがナイキスト周波数に収まっているとき（つまり有限のとき）、時間ドメインを離散化しても元のスペクトルの情報は失われない。これがサンプリング定理。</li>
</ul></li>
</ul>

<h3 id="dft">DFT</h3>

<ul>
<li>DTFTから更に周波数ドメインも離散化すると、時間ドメイン（離散的）も周期化する。</li>
<li>DFTは、計算上は有限個の離散値を扱うが、実際には時間ドメインも周波数ドメインも離散的かつ「周期的」であることを忘れてはいけない。</li>
<li>（サンプリング定理と同じように…）時間ドメインが有限長のとき、周波数ドメインを離散化しても時間ドメインの情報は失われない（つまりDTFTをDFTで実行できる）。</li>
</ul>

<h3 id="エネルギー">エネルギー</h3>

<ul>
<li>「時間軸での二乗和（エネルギー）」と「パワースペクトルの積分」は定数倍の関係にある（パーセバルの関係式）。このことは、どの周波数にエネルギーが集中しているのか知ることができるということを意味する。この関係は、FTでもDTFTでもDFTでも成り立つ。</li>
</ul>

<h3 id="線形時不変システム-ltiシステム">線形時不変システム (LTIシステム)</h3>

<ul>
<li>LTIシステムの出力は、入力信号とインパルス応答の畳み込みで記述できる。これは連続システムでも離散システムでも同じ。</li>
<li>時間ドメインでの畳み込みは、フーリエ変換やDTFTのドメインにおける積である。つまりLTIシステムというのは、入力信号のスペクトルに、周波数特性（＝インパルス応答のスペクトル）をかける操作を行う。</li>
</ul>

<h3 id="伝達関数">伝達関数</h3>

<ul>
<li>ラプラス変換では、扱う信号がすべて因果的であるときに畳み込みの性質が成り立つ。つまり Y(s) = H(s)U(s) となり定数項はゼロとなる。つまり入力と出力の比 H(s)=Y(s)/U(s) が計算できる。H(s)を伝達関数と呼ぶ。（ちなみに過渡解析などでは、定数項が生じ、それが初期条件となる）</li>
<li>ラプラス変換では虚数軸上の点が周波数特性を表し、z変換では単位円周上の点が周波数特性を表す。</li>
<li>ラプラス変換では線形微分方程式システムの伝達関数が有理多項式であり、z変換では差分方程式システムの伝達関数が有理多項式である。</li>
<li>ラプラス変換の有理多項式ではすべての極が負の側にあるときに安定、z変換の有理多項式ではすべての極が単位円周内にあるときに安定。</li>
</ul>

<h3 id="フィルタ">フィルタ</h3>

<p>理想フィルタは因果的線形システムでは作れない。これをいかに近似するかを、フィルタの近似問題という。</p>

<h4 id="fir">FIR</h4>

<p>直線位相かつ常に安定。しかし急峻さを得るには高い次数が必要。</p>

<ul>
<li>窓関数法

<ul>
<li>理想フィルタを逆DTFTして得られる無限長インパルス応答を、適切な窓関数でぶった切って（周波数ドメインにスムージングがかかる）、時間方向に推移させる（周波数ドメインに直線位相が加算される）ことで、因果性信号にする。</li>
</ul></li>
</ul>

<h4 id="iir">IIR</h4>

<p>低い次数で急峻な特性が得られる。しかし直線位相は近似的にしか目指せず、安定性も設計で考慮する必要がある。FIRより設計が難しい。</p>

<p>アナログフィルタを変換して作る方法は：</p>

<ul>
<li>インパルス不変法

<ul>
<li>アナログフィルタの有理多項式による伝達関数を部分分数分解すると、各項の逆ラプラス変換は減衰する指数関数でなる。これをサンプリング (つまりtをnTで置換) したものをz変換する。</li>
<li>この仕組み上、元のインパルス応答の周波数特性がナイキスト周波数に収まっている必要がある。つまりローパスフィルタとバンドパスフィルタにしか適用できない。</li>
</ul></li>
<li>双一次変換法

<ul>
<li>sとzとのあいだに双一次有理多項式による変換関係を定義して、ラプラス変換の有理多項式をz変換の有理多項式に変換する。</li>
</ul></li>
</ul>
]]></content:encoded>
       </item>
       
       <item>
         <title>USBオシロスコープを買ったのでLEDライトのチラツキを観測してみる</title>
         <link>https://ciscorn.net/2019/11/21/ad2_ledflicker/</link>
         <pubDate>Thu, 21 Nov 2019 22:10:49 +0900</pubDate>
         
         <guid>https://ciscorn.net/2019/11/21/ad2_ledflicker/</guid>
         <content:encoded><![CDATA[<p>Analog Discovery 2の新古品が22000円で売られてたので衝動買いした。（ちなみに秋月で新品を買うと37000円くらいする）。</p>






<figure style="">
  <img data-original="/2019/11/21/ad2_ledflicker/feature.jpg" src="/2019/11/21/ad2_ledflicker/feature.jpg" />
  
</figure>


<p>写真の中央が本体で、その左右のBNCプローブアダプタとかインピーダンスアナライザとかは別途に新品を買った。</p>

<p>いわゆる「USBオシロスコープ」、つまりパソコンに繋いで使うオシロだ。シグナルジェネレータやデジタルI/Oの機能も付いていて、それらとオシロ機能が連携することで、周波数特性などの測定も手軽にできるらしい。</p>

<p>自分のような電子回路のど素人でもオシロスコープは持ってるに越したことはないらしい。本当は5万円くらいの中国メーカーのオシロ（据置型）を買おうかどうか悩んでいたんだけど、一旦保留することにした。</p>

<h2 id="機能">機能</h2>

<p>仕様を書き写してみる：</p>

<ul>
<li>オシロスコープ (2ch, ±25V, 14bit, 100MSa/s, 30MHz+)</li>
<li>任意波形生成器 (2ch, ±5V, 14bit, 100MSa/s, 12MHz+)</li>
<li>DC/AC電圧計 ±25V</li>
<li>プログラマブル電源 (2ch, 0〜+5V, 0〜-5V, ただし出力は弱い)</li>
<li>スペクトラムアナライザ (0Hz〜50MHz)</li>
<li>ネットワークアナライザ (1mHz〜25MHz)</li>
<li>インピーダンスアナライザ (100μHz〜25MHz)</li>
<li>16ch ロジックアナライザ (100MSa/s)</li>
<li>16ch ディジタルパターンジェネレータ (100MSa/s)</li>
<li>16ch プロトコルアナライザ (SPI, I2C, UART, CAN)</li>
<li>16ch ディジタルI/O（ボタン、スイッチ、LED、etc.）</li>
<li>双方向外部トリガ 2ch</li>
</ul>

<p>それぞれの機能の性能は専門の機材に及ばない（用途によっては使い物にならない）のだけど、学習者やホビーユーザならこれで十分な状況も多いと思う。というか学習者向けを志向して作られているようだ。ソフトウェアも良くできてそう。要するに、自分のような入門者の勉強用にはちょうどいいと思う。</p>

<p>詳しいことはリファレンスをみればわかる。</p>

<p><a href="https://reference.digilentinc.com/reference/software/waveforms/waveforms-3/reference-manual">https://reference.digilentinc.com/reference/software/waveforms/waveforms-3/reference-manual</a></p>

<p><a href="https://reference.digilentinc.com/reference/instrumentation/analog-discovery-2/reference-manual">https://reference.digilentinc.com/reference/instrumentation/analog-discovery-2/reference-manual</a></p>

<p>（ちなみに今5万円前後で買える中国製オシロ（単体型）は、サンプリングレート1GSa/sで、周波数帯域は100〜200MHzというのが主流だとおもう）</p>

<h2 id="ledライトのチラツキを測定してみる">LEDライトのチラツキを測定してみる</h2>

<p>なにかオシロじゃないと測定できない身近なネタはないか考えていたら、手元にフォトダイオードが１つあったので、これでLEDライトのチラツキを測定できるんじゃないかと思いついた。</p>






<figure style="">
  <img data-original="/2019/11/21/ad2_ledflicker/IMG_0068-ba1756c8-a0a9-4bea-88e8-b634db085fe5.jpeg" src="/2019/11/21/ad2_ledflicker/IMG_0068-ba1756c8-a0a9-4bea-88e8-b634db085fe5.jpeg" />
  
</figure>


<p>LED照明（天井ライト、デスクライト、etc.）の内部回路というのはピンキリで、商用電源を一切平滑化せずに100/120Hzで明滅するようなものから、ちゃんとインバータが備わっているものまで色々ある。100Hzで明滅するようなものはなんとなく視神経に悪影響がありそうだし（どうなんだろ）、その照明下で動画撮影をするとひどいフリッカーが起きたりもする。</p>

<p>実際に測定してみる。上の写真のようにフォトダイオードと抵抗 (22kΩ) を直列につないで、Analog Discoveryの電源機能で5Vを供給する。光の強さに応じてフォトダイオードに流れる電流が変化するので、抵抗の電位差をオシロ機能で測ればいい。</p>

<h3 id="パナソニックのledシーリングライト">パナソニックのLEDシーリングライト</h3>

<p>まずは部屋のLEDシーリングライト（Panasonic製）の光を測定してみる。結果はこんな感じ（実際にはリアルタイムに波形が見れる）。</p>






<figure style="">
  <img data-original="/2019/11/21/ad2_ledflicker/Panasonic_LED_Ceiling_Light-ee4411d5-8184-4ea7-8a00-644d868b9451.jpg" src="/2019/11/21/ad2_ledflicker/Panasonic_LED_Ceiling_Light-ee4411d5-8184-4ea7-8a00-644d868b9451.jpg" />
  
</figure>


<p>人間にはチラツキなど感じられないが、実際の明るさは波打っていて、その周波数は301Hz、つまり商用電源の6倍の周波数。さすがに点灯と消灯を繰り返していることはなくて、それなりに直流成分があるが、明るさの変化（波形の上下）がそこそこあるように思う。</p>

<p>このシーリングライトは低〜中価格帯の製品なので、高価格帯のものはもっとよくできているのかもしれない。</p>

<p>そういえば周波数とかRMSとかの統計値を簡単に表示させられて便利だ。こういう機能の呼び出しは、単体のオシロよりもこういうPCオシロのほうが操作しやすそう。</p>

<h3 id="ledデスクライト-taotronics-eye-protection-led-desk-lamp">LEDデスクライト (Taotronics Eye-Protection LED Desk Lamp)</h3>

<p>Amazonで売れてるやつ。タイムセールで3000円で買った。</p>

<p><a href="https://www.amazon.co.jp/dp/B00VV6BM9K/">https://www.amazon.co.jp/dp/B00VV6BM9K/</a></p>






<figure style="">
  <img data-original="/2019/11/21/ad2_ledflicker/71AllHTLsL-6501c257-ed3f-48d7-9eeb-484681201397._SL1500_.jpg" src="/2019/11/21/ad2_ledflicker/71AllHTLsL-6501c257-ed3f-48d7-9eeb-484681201397._SL1500_.jpg" />
  
</figure>


<p>波形はこんな感じ。まったく違う。</p>






<figure style="">
  <img data-original="/2019/11/21/ad2_ledflicker/Taotronics_Eye-protection_LED_Desk_Lamp-dee59a3d-db93-4933-a71e-9a05f4e35d55.jpg" src="/2019/11/21/ad2_ledflicker/Taotronics_Eye-protection_LED_Desk_Lamp-dee59a3d-db93-4933-a71e-9a05f4e35d55.jpg" />
  
</figure>


<p>さっきのPanasonicのシーリングライトと公平に比較するため、DC RMS = 200mVに合わせてある。</p>

<p>シーリングライトに比べると波形の振幅は５分の１くらい。周波数は52000Hzで、シーリングライトの170倍以上。これがまさにインバーターって感じなのだろうか。さすがにこの周波数だと人間には知覚しようがないし、そもそも振幅が小さい。</p>

<p>目に優しいといいながら実態はどうなのかと思っていたが、少なくとも平滑化に関してはちゃんとしていてすごいと思った。（Amazonレビューで不正するのをやめればいいのに）。</p>

<p>ということで、オシロスコープを初めて使った体験でした。電子回路ではなく明るさの測定に使ってしまった。</p>]]></content:encoded>
       </item>
       
       <item>
         <title>赤外線LEDでNikon一眼レフのシャッターを切る</title>
         <link>https://ciscorn.net/2019/11/18/nikon_irled_remote/</link>
         <pubDate>Mon, 18 Nov 2019 17:15:49 +0900</pubDate>
         
         <guid>https://ciscorn.net/2019/11/18/nikon_irled_remote/</guid>
         <content:encoded><![CDATA[<p>自分のような初心者の練習に良さそうなネタなので、できるかどうかやってみた。結果としては一瞬でできた。</p>






<figure style="">
  <img data-original="/2019/11/18/nikon_irled_remote/feature.jpg" src="/2019/11/18/nikon_irled_remote/feature.jpg" />
  
</figure>


<p>この手のシャッター操作の自動化はリモートコード経由で行うのが安定的で良いだろうけど、単なる遊びのために手元のリモートコードを切りたくはない。</p>

<p>NikonやCanonの一眼レフは、リモートコードのほかに赤外線リモコンでもシャッターが切れるようになっているので、今回はその機能を利用することにした。ちなみにリモートコードは半押しやバルブに対応しているけど、赤外線リモコンではそれはできない。</p>

<h2 id="発光パターン">発光パターン</h2>

<p>赤外線リモコンの仕組みはTVだろうとエアコンだろうと同じで、リモコン側はメーカー固有のパターンで赤外光をON/OFFさせて、受信側はその固有パターンを検出して反応する。</p>

<p>自分はNikon用の赤外線リモコンを持っていないので発光パターンを調べることができない。仕方がないので今回はすでに同じことをしている人の知恵を借りることにする。（ついでにいうと、自分はまだオシロスコープすら持ってないし、赤外線を受信できる素子も持ってないのでどっちにしろ計測は無理。）</p>

<p><a href="http://wpjzrn.ddo.jp/astro/D40_Interval_timer/D40_Interval_timer.html">Nikon D40用天体撮影インターバルタイマー作成</a></p>

<p>この方の調査によると、Nikonのリモコンの発光パターンは以下のようになっているそうだ。横軸が時間。</p>






<figure style="">
  <img data-original="/2019/11/18/nikon_irled_remote/Unknown-1ab00e82-cd8c-428a-bbb0-872bd64db057.png" src="/2019/11/18/nikon_irled_remote/Unknown-1ab00e82-cd8c-428a-bbb0-872bd64db057.png" />
  
</figure>


<h3 id="変調">変調</h3>

<p>じゃあこのタイミング通りにLEDをON/OFFすれば解決かというと、話はそこまで単純ではない。赤外線リモコンの発光は変調されている。</p>

<p>というのも、仮に世の中の赤外線リモコンが上のような単純なパターンを送受信していたとすると、他の光源の影響をモロに受けて誤作動する可能性が極めて高いからだ。</p>

<p>そこで大抵のリモコンは信号を38kHzに変調して送信している。そして受信側の赤外線受信モジュールには38kHzの周辺のみを取り出すアナログフィルタがついていて、それによって他の雑多な光源の影響をリジェクトするようになっている。</p>

<p>変調といっても実際にやることは簡単で、38kHzの矩形波をキャリアにすればいい&hellip;&hellip;.要するに下図のように波打たせる。</p>






<figure style="">
  <img data-original="/2019/11/18/nikon_irled_remote/mod-646295da-aa83-48a5-941b-2167935ca147.png" src="/2019/11/18/nikon_irled_remote/mod-646295da-aa83-48a5-941b-2167935ca147.png" />
  
</figure>


<p>このようなパターンでLEDを発光させれば、カメラが反応するはずだ。</p>

<h2 id="実装-簡単">実装（簡単）</h2>

<p>簡単に済ませたいのでArduinoでコードを書く。マイコンは、何かのおまけでもらった &ldquo;Adafruit Pro Trinker&rdquo; というArduino互換マイコンを使った。</p>

<p>ひとまずこんな感じで赤外線LEDをつないでD9ピンの電流で駆動させる。ちなみにピンによっては38kHzの速さでスイッチできないものもあるみたい。</p>






<figure style="">
  <img data-original="/2019/11/18/nikon_irled_remote/Screen_Shot_2019-11-18_at_19-dd5a2972-32fb-4a22-b841-65b7a1fd21b4.25.43.png" src="/2019/11/18/nikon_irled_remote/Screen_Shot_2019-11-18_at_19-dd5a2972-32fb-4a22-b841-65b7a1fd21b4.25.43.png" />
  
</figure>


<p>Arduinoのコードは以下のようにフラットに書いた。 変なビット操作をしているけど、これはArduinoの <code>digitalWrite(...)</code> を使うと動作が遅くて発光タイミングに影響するかもしれないのでレジスタの直接操作でピンの出力を切り替えているというだけ。</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-c++" data-lang="c++"><span style="color:#66d9ef">void</span> <span style="color:#a6e22e">setup</span>() {
    pinMode(<span style="color:#ae81ff">8</span>, OUTPUT);
}

<span style="color:#66d9ef">void</span> <span style="color:#a6e22e">loop</span>() {
    _delay_ms(<span style="color:#ae81ff">100</span>);

    <span style="color:#66d9ef">for</span> (<span style="color:#66d9ef">int</span> i <span style="color:#f92672">=</span> <span style="color:#ae81ff">0</span>; i <span style="color:#f92672">&lt;</span> <span style="color:#ae81ff">77</span>; i<span style="color:#f92672">++</span>) {
        PORTB <span style="color:#f92672">|=</span> B00000001;
        _delay_us(<span style="color:#ae81ff">13</span>);
        PORTB <span style="color:#f92672">&amp;=</span> <span style="color:#f92672">~</span>B00000001;
        _delay_us(<span style="color:#ae81ff">13</span>);
    }
    _delay_us(<span style="color:#ae81ff">28429</span>);

    <span style="color:#66d9ef">for</span> (<span style="color:#66d9ef">int</span> i <span style="color:#f92672">=</span> <span style="color:#ae81ff">0</span>; i <span style="color:#f92672">&lt;</span> <span style="color:#ae81ff">17</span>; i<span style="color:#f92672">++</span>) {
        PORTB <span style="color:#f92672">|=</span> B00000001;
        _delay_us(<span style="color:#ae81ff">13</span>);
        PORTB <span style="color:#f92672">&amp;=</span> <span style="color:#f92672">~</span>B00000001;
        _delay_us(<span style="color:#ae81ff">13</span>);
    }
    _delay_us(<span style="color:#ae81ff">1636</span>);

    <span style="color:#66d9ef">for</span> (<span style="color:#66d9ef">int</span> i <span style="color:#f92672">=</span> <span style="color:#ae81ff">0</span>; i <span style="color:#f92672">&lt;</span> <span style="color:#ae81ff">17</span>; i<span style="color:#f92672">++</span>) {
        PORTB <span style="color:#f92672">|=</span> B00000001;
        _delay_us(<span style="color:#ae81ff">13</span>);
        PORTB <span style="color:#f92672">&amp;=</span> <span style="color:#f92672">~</span>B00000001;
        _delay_us(<span style="color:#ae81ff">13</span>);
    }
    _delay_us(<span style="color:#ae81ff">3676</span>);

    <span style="color:#66d9ef">for</span> (<span style="color:#66d9ef">int</span> i <span style="color:#f92672">=</span> <span style="color:#ae81ff">0</span>; i <span style="color:#f92672">&lt;</span> <span style="color:#ae81ff">17</span>; i<span style="color:#f92672">++</span>) {
        PORTB <span style="color:#f92672">|=</span> B00000001;
        _delay_us(<span style="color:#ae81ff">13</span>);
        PORTB <span style="color:#f92672">&amp;=</span> <span style="color:#f92672">~</span>B00000001;
        _delay_us(<span style="color:#ae81ff">13</span>);
    }
}
</code></pre></div>
<p>一眼レフ側で赤外線リモコンの使用を有効にしておく。</p>

<p>Arduinoにコードを書きこんでカメラを近づけたら、あっさりとシャッターが切れた。</p>

<p>おわり。</p>

<h2 id="fetをかます">FETをかます</h2>

<p>マイコンの出力ピンで直接LEDを駆動させずに、FET (NMOSFET) でスイッチングするようにしてみた。そうしたほうが十分な電流を流せて赤外線の出力が上がる。マイコンの負荷も少ないだろう。（&hellip;というかFETを試しに使ってみたかっただけ）。</p>

<p>今回使った赤外線LEDは、電圧降下が1.7Vで電流は100mAくらい流しても平気のようなので、抵抗は33Ωくらいまで下げてもいい（私は47Ωを使った）。</p>






<figure style="">
  <img data-original="/2019/11/18/nikon_irled_remote/Screen_Shot_2019-11-20_at_10-0a102087-9103-499e-a85b-3a595e3cbeca.57.12.png" src="/2019/11/18/nikon_irled_remote/Screen_Shot_2019-11-20_at_10-0a102087-9103-499e-a85b-3a595e3cbeca.57.12.png" />
  
</figure>


<h2 id="ストロボでも同じことができそう">ストロボでも同じことができそう</h2>

<p>Nikonのクリップオンストロボは複数台のストロボを連携させられる機能をもっている。例えば、カメラのシャッターを切るときに内臓ストロボからコマンドを送出して、そのへんに置いてある複数台のクリップオンストロボを同調させて光らせる、とかができる。</p>

<p>この通信も赤外光なので、頑張れば自前で制御可能かもしれない。</p>]]></content:encoded>
       </item>
       
       <item>
         <title>3Dプリンター (Original Prusa i3 MK3S Kit) を買って組み立てた</title>
         <link>https://ciscorn.net/2019/11/12/prusamk3s/</link>
         <pubDate>Tue, 12 Nov 2019 19:10:00 +0900</pubDate>
         
         <guid>https://ciscorn.net/2019/11/12/prusamk3s/</guid>
         <content:encoded><![CDATA[<p>3Dプリンターキット (Original Prusa i3 MK3S Kit) を買った。少しずつ組み立てて、サンプルデータを印刷するところまでたどり着いた。組み立ては、毎日1〜2時間づつ進めて約8時間かかった。</p>






<figure style="">
  <img data-original="/2019/11/12/prusamk3s/IMG_0032-d01bcff0-5b57-4d5d-90af-64eed67a52af.jpeg" src="/2019/11/12/prusamk3s/IMG_0032-d01bcff0-5b57-4d5d-90af-64eed67a52af.jpeg" />
  
</figure>


<h2 id="original-prusa-i3">Original Prusa i3</h2>






<figure style="">
  <img data-original="/2019/11/12/prusamk3s/IMG_0006-fee375e3-67c5-4d9c-b41f-113ef7c26bf1.jpeg" src="/2019/11/12/prusamk3s/IMG_0006-fee375e3-67c5-4d9c-b41f-113ef7c26bf1.jpeg" />
  
</figure>


<p>価格と性能のバランスがいいという評判で、Makeとかでもおすすめされている。YouTubeで海外の3Dプリンタ系のチャンネルをみるとよく出てくる。世界的には良く売れている機種で、既に10万台以上売ったようだが日本での知名度は高くない。最新版はMK3Sというモデルだ。</p>

<p>&ldquo;Prusa&rdquo;という名称は開発者の Josef Prusa から。てっきりアメリカ人か何かだと思っていたら、チェコ人のお兄さんだった。髭面のオヤジに見えるが、1990年生まれで、まだ20代。</p>

<p>オーンソースハードウェアなので、これを派生させたプリンタがいろいろと存在する。Amazon.co.jpで上位にいる中国メーカーのものは実はそれが多かったりする。</p>

<p>面白いと思ったのは、このプリンタのパーツも3Dプリントで製造されている点だ。</p>






<figure style="">
  <img data-original="/2019/11/12/prusamk3s/IMG_0016-b6ea25bf-b180-4844-96b3-63dc1976ed85.jpeg" src="/2019/11/12/prusamk3s/IMG_0016-b6ea25bf-b180-4844-96b3-63dc1976ed85.jpeg" />
  
</figure>


<p>自分で組み立てるKit版と、組み立て済み版がある。組み立て済み版は値段が1.5倍くらいする。</p>

<p>購入は、チェコ本国のサイトとAmazon.co.jpでできるが、送料と消費税を考えると両者の価格に大きな差はない。自分はAmazonでKit版を買った。</p>






<figure style="">
  <img data-original="/2019/11/12/prusamk3s/IMG_0009-4cc379f6-3abb-4faf-8984-9df986768dfb.jpeg" src="/2019/11/12/prusamk3s/IMG_0009-4cc379f6-3abb-4faf-8984-9df986768dfb.jpeg" />
  
</figure>


<p>ダンボールを開けたらHARIBOが入っていて笑った。組み立てマニュアルの各章のおわりに「ここでHARIBOを5個食べましょう」とか書いてある。</p>

<p>Amazon.co.jpで買うと日本語マニュアルが付いてくるけど、公式サイトには最新の英語版マニュアルがあるので、英語がすごく苦手でなければ特に要らないかも。</p>

<h2 id="組み立てる">組み立てる</h2>

<p>自分は組み立てに8時間掛かった。作業に専門的なスキルは要らなかったしマニュアルも十分にわかりやすかったし専門的な工具も要らない。でも、組み立ては楽とは言えないし精神的にもけっこう疲れた。とはいえ一度組み立てると構造が分かるので、その後のメンテナンスのことを考えると一度は自分で組み立てたほうがいいと思う。</p>

<p>外箱を開封すると小箱が出てくる。</p>






<figure style="">
  <img data-original="/2019/11/12/prusamk3s/IMG_0014-2aed65d6-c8af-49a0-8787-21f0af1d7c9f.jpeg" src="/2019/11/12/prusamk3s/IMG_0014-2aed65d6-c8af-49a0-8787-21f0af1d7c9f.jpeg" />
  
</figure>


<p>小箱を開封すると、パーツが現れる。</p>






<figure style="">
  <img data-original="/2019/11/12/prusamk3s/IMG_0019-14151597-8c24-47d9-80a6-ccd2c0a5627c.jpeg" src="/2019/11/12/prusamk3s/IMG_0019-14151597-8c24-47d9-80a6-ccd2c0a5627c.jpeg" />
  
</figure>


<p>まずは骨格を組み立てて、そこにY軸（奥行方向の軸）を作る。骨格はさすがに剛性と精度が必要なので、3Dプリントではなく、金属を切削加工したものと、アルミの押し出しで作られている。</p>

<p>このへんの組み立ては簡単。</p>






<figure style="">
  <img data-original="/2019/11/12/prusamk3s/IMG_0021-d917ee75-2d7f-4065-960b-fe6c76ef50c0.jpeg" src="/2019/11/12/prusamk3s/IMG_0021-d917ee75-2d7f-4065-960b-fe6c76ef50c0.jpeg" />
  
</figure>


<p>次は、Z軸（高さ方向の軸）とX軸（横方向の軸）を組み立る。（写真撮り忘れた）</p>

<p>その次にエクストルーダーを組み立てる。これはインクジェットプリンタでいう「ヘッド」に相当する部分。マニュアルにも書いてある通り、これの組み立てが最難関。このエクストルーダも、電子的なパーツ以外はすべて3Dプリント部品でできている。（このへんも写真撮り忘れた）</p>

<p>組み上げたエクストルーダを、X軸に取り付ける。</p>






<figure style="">
  <img data-original="/2019/11/12/prusamk3s/IMG_0022-a4f81964-e871-4966-a1e4-3c82d0d07ca8.jpeg" src="/2019/11/12/prusamk3s/IMG_0022-a4f81964-e871-4966-a1e4-3c82d0d07ca8.jpeg" />
  
</figure>


<p>ここまでやって、こんな感じ。</p>






<figure style="">
  <img data-original="/2019/11/12/prusamk3s/IMG_0023-9ed6923f-8f2d-45c1-b680-340dd5d60335.jpeg" src="/2019/11/12/prusamk3s/IMG_0023-9ed6923f-8f2d-45c1-b680-340dd5d60335.jpeg" />
  
</figure>


<p>さらに、操作パネルを取り付けたり、電源ユニット (PSU) を取り付けたり、ヒートベッドを取り付けたりする。このへんは楽勝。</p>






<figure style="">
  <img data-original="/2019/11/12/prusamk3s/IMG_0024-ace15cfd-d33a-4d4e-8256-554a8c347e62.jpeg" src="/2019/11/12/prusamk3s/IMG_0024-ace15cfd-d33a-4d4e-8256-554a8c347e62.jpeg" />
  
</figure>







<figure style="">
  <img data-original="/2019/11/12/prusamk3s/IMG_0025-e3677a41-25e5-4763-bf3c-95dd6ccd57e8.jpeg" src="/2019/11/12/prusamk3s/IMG_0025-e3677a41-25e5-4763-bf3c-95dd6ccd57e8.jpeg" />
  
</figure>


<p>最後に、マザーボードを取り付けて配線する。ボックスが狭くてやりにくい。ケーブルの処理はけっこう厄介。</p>






<figure style="">
  <img data-original="/2019/11/12/prusamk3s/IMG_0030-ce2bb563-e79a-47a7-b524-b75675bf1188.jpeg" src="/2019/11/12/prusamk3s/IMG_0030-ce2bb563-e79a-47a7-b524-b75675bf1188.jpeg" />
  
</figure>


<p>これでおわり。完成。</p>






<figure style="">
  <img data-original="/2019/11/12/prusamk3s/IMG_0036-fceb1505-fae8-483e-afd4-c59651bc73d9.jpeg" src="/2019/11/12/prusamk3s/IMG_0036-fceb1505-fae8-483e-afd4-c59651bc73d9.jpeg" />
  
</figure>


<h2 id="キャリブレーション">キャリブレーション</h2>

<p>初回起動時はセルフテストが行われる。電気的な部品（ステッピングモータ、ファン、ヒーター、センサー、etc.）の動作をそれぞれチェックしてくれる。こういうチェックがあるのは良い。</p>

<p>その次はキャリブレーションを行う。エクストルーダにPINDAセンサーというのがついていて、そのセンサーで印刷台（ヒートベッド）の上をあちこちスキャンして、X, Y, Z軸のゆがみや、印刷台 (heatbed) のゆがみを測定する。この結果を、印刷時のソフトウェア補正に利用するらしい。</p>

<p>キャリブレーションはほぼ全自動で進む。</p>

<p>巷の評価によると、このキャリブレーションがよくできているのがPrusa i3の強みだそうだ。つまり、筐体だけでは出せない精度を、優秀なセンサーと優秀なソフトウェアで実現するということらしい。</p>

<p>キャリブレーションの途中で、ヒートベッドの上にスチールシートを取り付ける。（ちなみに実際に印刷するときは、このスチールシート上で印刷する。）</p>

<p>スチールシートの上で、さらにキャリブレーションを進めていく。</p>

<p>キャリブレーションが終わると「X軸とY軸は垂直です。おめでとう」という英語の表示が出た。どうやら最良の結果らしくて安心した（しかも詳細情報によるとskew 0.00°）。ちなみにネットの情報によると、「わずかに歪んでいるが問題ないよ。グッジョブ」という表示が出ることもあるらしい（特に旧バージョンのMK2で）。仮にそういう結果になっても、ソフトウェアが優秀なので出力品質にほぼ影響しないらしい。</p>

<h2 id="テスト印刷">テスト印刷</h2>

<p>印刷は、SDカードにデータを入れて行うか、PCと接続して行う。SDカードから印刷するならPCを接続する必要はない。</p>

<p>印刷用データは、CNC工作機械の制御で標準的に使われている “gcode” という形式を使う（このデータを自分で作るときは、3DCADや3DCGソフトで作った3Dモデルを、「スライサー」と呼ばれるソフトウェアで処理する）。</p>

<p>付属のSDカードに入っていた「Treefrog 50μm」（アマガエルみたいなモデル）を印刷してみることにした。50μm (=0.05mm) というのは積層レイヤ1枚の厚みを表している。50μmは熱溶解積層法のプリンタにとってはけっこう精密なデータらしい。他のサンプルデータは150μmや200μmが多い。</p>

<p>液晶画面の指示の通りにフィラメントをエクストルーダに挿入する。フィラメントを挿入すると、フィラメントセンサーが働いて、自動で巻き込みが始まる。</p>

<p>印刷するTreefrogのサイズはけっこう小さいが、精密なデータなので印刷に4時間弱かかるという表示が出た。</p>






<figure style="">
  <img data-original="/2019/11/12/prusamk3s/IMG_0026-94c4a993-e776-4c59-abb2-3244d61da7e0.jpeg" src="/2019/11/12/prusamk3s/IMG_0026-94c4a993-e776-4c59-abb2-3244d61da7e0.jpeg" />
  
</figure>


<p>印刷をスタートさせて、すごく疲れていたので昼寝した。で、目が覚めたら何のエラーもなく出来上がっていたので拍子抜け。</p>






<figure style="">
  <img data-original="/2019/11/12/prusamk3s/IMG_0032-d01bcff0-5b57-4d5d-90af-64eed67a52af.jpeg" src="/2019/11/12/prusamk3s/IMG_0032-d01bcff0-5b57-4d5d-90af-64eed67a52af.jpeg" />
  
</figure>


<p>スチールシートをプラットフォームから取り外して、シートをグニャリと曲げると印刷物（カエル）がシートから剥がれる。</p>






<figure style="">
  <img data-original="/2019/11/12/prusamk3s/IMG_0033-c3727670-3cfc-40f4-8197-428748328639.jpeg" src="/2019/11/12/prusamk3s/IMG_0033-c3727670-3cfc-40f4-8197-428748328639.jpeg" />
  
</figure>


<p>背中はつるつる。</p>






<figure style="">
  <img data-original="/2019/11/12/prusamk3s/IMG_0034-dcb23b63-6f47-461e-a71a-e60b6ca045bf.jpeg" src="/2019/11/12/prusamk3s/IMG_0034-dcb23b63-6f47-461e-a71a-e60b6ca045bf.jpeg" />
  
</figure>


<p>蜘蛛の糸のようなものが生じていて汚いが、複雑なモデルにおいてこれが発生するのはある程度仕方がないようだ（Stringingという）。人間の手で研磨などの処理をしてやれば綺麗に仕上がる。</p>

<h2 id="わかったこと">わかったこと</h2>

<ul>
<li>熱溶解積層法の3Dプリンタはあくまでホビー用&hellip;と思いきや、このプリンタの部品が3Dプリントで作られているように、実用可能な製品のパーツを作ることもできる。</li>
<li>動作音は想像していたよりも静かだった。置く場所や方法を工夫すれば深夜のアパートでも余裕で動作させられると思う。より静音にするためのステルスモードもあるらしいが、まだ試していない。</li>
<li>組み立て作業に高い精度は求められない。常識的な丁寧さで作ればちゃんと動く。</li>
<li>ハードウェア自体はけっこう単純なので、それを支援するソフトウェアが重要な役割を果たす。</li>
<li>機械系の人たちには常識なんだろうけど、ステッピングモータとベルトだけでこういう精度が出せるのはすごい。</li>
<li>PCをつなぎっぱなしにする必要はない。SDカードで印刷できる。良い。</li>
<li>フィラメントを押し出す部分をエクストルーダ (extrude = 押し出す) と呼ぶ。ステッピングモータは、XYZの各軸に加えて、エクストルーダにも付いている (E軸)。これによって、フィラメントをヒータ内に取り込む量、つまり射出量が制御される。</li>
<li>エクストルーダにはフィラメントを溶かすためのヒーターが付いていて、稼働時は200〜250℃になる。物体を出力するプラットフォームにも造形物が外れないようにするためのヒーターがついていて、こちらは60〜80℃くらいになる。これらの温度はフィラメントの材質 (PLA, ABS, PETG, etc.) に応じて適切に変える。</li>
</ul>

<h2 id="今後">今後</h2>

<p>自分はこういうカエルを印刷したいわけではない。</p>

<p>いずれにしても、3DCAD/CAM/CAEを覚えて損はないだろう。Fusion 360を少しだけ触ってみたが、目的の形状を作る作業はパズルを解くみたいで楽しい。単に図面を引くだけでなく、画面上で構造物を組み立てて稼働させたりできるのもすごい。まだぜんぜん分かってない。</p>

<h2 id="リニアベアリングの品質">リニアベアリングの品質</h2>

<p>組み立て時に、リニアベアリングが金属ロッド上で結構ひっかかる感じがした。実際に動作させるまでずっとこれが気がかりで、気になってしまって組み立て作業が止まったりした。</p>

<p>結果的には出力品質に問題はなさそうなので、ベアリングの品質が多少悪くても深刻な影響はないのかもしれない。ただし静音性に影響している感じはしなくもない。印刷時には各軸が高速に移動することがたまにあるが、そのときはけっこうギャーギャー鳴く。</p>

<p>フォーラムの情報を見ると、日本のMISUMI製などの上質なベアリングとロッドに交換する人もいるようだ。</p>]]></content:encoded>
       </item>
       
     </channel>
   </rss>
