CI とかで、あるディレクトリが毎回削除されて作りなおされ、そのディレクトリに package.json がある場合、毎回 npm install をすると大変非効率なので、別のディレクトリに node_modules ディレクトリを置いてそれを指定して使いたい。

しかし package.json に書かれた依存はそのディレクトリの node_modules ディレクトリにしか置けないようなので、以下のようにした。

  • 任意の場所に node_modules ディレクトリをつくる
  • npm install する前に、package.json があるディレクトリに ln -sf で、上記で作った node_modules ディレクトリへ symlink を貼る
  • npm install する

これで、インストール済みモジュールはそのまま使われるようになり、毎回 npm install で何もかもが install されなおされるということがなくなった。

  1. トップ
  2. tech
  3. node_modules を別の場所に置く

myApp = angular.module('myApp', [ 'myApp.filters', 'myApp.services' ]);

myApp.services = angular.module('myApp.services', []);
myApp.services.factory('loc', function () {
    return Location.parse(location.href);
});

myApp.filters = angular.module('myApp.filters', []);
myApp.filters.filter('page', function (loc) {
    return function (page) {
        return loc.params(angular.extend(loc.params(), { page : page })).href;
    };
});

services と filters のモジュールをわけて以上のようなのを実装した場合で、page filter をテストしたい場合、以下のようにするとエラーになる。

describe('filter', function () {
	beforeEach(module('myApp.filters'));

	describe('page', function () {
		it('should append page param', function () {
			inject(function (pageFilter) {
				expect(pageFilter(1)).toBe('foobar');
			});
		});
	});
});

//=> Error: [$injector:unpr] Unknown provider: locProvider <- loc <- pageFilter

loc の定義が別モジュールで、beforeEach で該当モジュールを読みこんでいないからだけど、そもそも DI で loc を注入してテストしたいところだと思う。

とりあえず以下のようにしたうまくいった。

describe('filter', function () {
	beforeEach(module('myApp.filters'));

	describe('page', function () {
		it('should append page param', function () {
			module(function ($provide) {
				$provide.factory('loc', function() {
					return Location.parse('http://localhost/foo');
				});
			});

			inject(function (pageFilter) {
				expect(pageFilter(1)).toBe('http://localhost/foo?page=1');
			});
		});
		it('should append page param', function () {
			module(function ($provide) {
				$provide.factory('loc', function() {
					return Location.parse('http://localhost/foo?foo=bar');
				});
			});
			inject(function (pageFilter) {
				expect(pageFilter(1)).toBe('http://localhost/foo?foo=bar&page=1');
			});
		});
	});
});

$provide でテスト用の loc factory を定義してやる。

  1. トップ
  2. tech
  3. AngularJS 依存を持つ filter のテスト

アナログ回路めちゃくちゃ難しい。考える要素が多すぎる。素子の誤差が結果にどの程度影響を及ぼすかを予測するためには、理論をしっかりわかってないと難しい。そしてそれがわかっていないと、結果誤差がなぜ発生しているかの原因をさぐることができない。設計範囲内の誤差なのか、あるいは何かしら他の要素 (寄生容量など物理現象) が関係しているのか、わかってこない。

あるいはそこまでやらなくても、カットアンドトライでなんとかできるケースは多いのかもしれない。どの程度まで机上で考えるのかというのがよくわからなくて難しい。シミュレータを活用したほうがいいのかもしれない。

DHT11 という比較的価格の安い(ただし精度はいまいちな)デジタル温湿度センサーがあるので読んでみた。1-wire ライクな (1-wireではない) プロトコルで、データシートを読みながら頑張って読む感じ。

  1. 18ms 以上バスをローにすることでデータ送信を行わせる
  2. 18ms のローを送信したあと、入力状態に変えて 50us ロー + 50us ハイを待つ (初期メッセージ)
  3. そのあと 40回 50us ロー + (28us or 70us ハイ) のセットが送られてくる。70us のほうが「1」のビット

だいたいすぐできるんだけど、waiting for slope up と書いてあるところのディレイを入れないと、バスがハイになる前に loop_until_bit_is_clear してしまって状態がズレてしまうので、立ちあがる時間を適当に待つ必要があった。

湿度・温度それぞれ16bitが送られてくるんだけど、下位8bitは、このモジュールの場合、どちらも全部0しか返ってこない。のでコードでは無視している。下位 8bit はAM2302とか精度が良いがコストが高いというモジュールがあって、そちらの場合使われるっぽい。

あと、送られてくるデータは前回測定時のデータなので、2回読み出しを行って2回目を採用しないと、正しいデータにならない。連続して読むなら関係ない。

int main(void) {
	logger_init(9600);
	setup_io();

	printf("initializing...\r\n");
	sei();
	printf("initialized\r\n");

	uint8_t i, j, us;
	int16_t humidity, temperature;
	uint8_t res[5];
	set_input(DDRD, PD7); clear_bit(PORTD, PD7);  // hi-z

	for (;;) {
		set_output(DDRD, PD7); clear_bit(PORTD, PD7);
		_delay_ms(20);
		set_input(DDRD, PD7); // hi-z

		// waiting for slope up
		_delay_us(5);

		// waiting for slave initial response
		loop_until_bit_is_clear(PIND, PD7);
		loop_until_bit_is_set(PIND, PD7);
		loop_until_bit_is_clear(PIND, PD7);

		for (i = 0; i < 5; i++) {
			for (j = 0; j < 8; j++) {
				us = 0;
				// 50us
				loop_until_bit_is_set(PIND, PD7);
				while (bit_is_set(PIND, PD7)) {
					us++;
					_delay_us(1);
				}

				res[i] <<= 1;
				if (us > 50) { // 0: <28us 1: <70us
					res[i] |= 1;
				}
			}
		}

		printf("%02x %02x %02x %02x %02x\r\n", res[0], res[1], res[2], res[3], res[4]);
		if (( (res[0] + res[1] + res[2] + res[3]) & 0xff) == res[4]) {
			humidity    = res[0];
			temperature = res[2];
			printf("%d%%RH %dC\r\n", humidity, temperature);
		} else {
			printf("invalid data\r\n");
		}

		_delay_ms(5000);
	}
}

信号全体

最初の長いロー部分が、AVR 側からのローで 18ms ある。その後、ちょっとハイになってるところは、センサー待ちの部分、そして 50us ロー・50us ハイに続いてデータが流れてくる。目で見てビット読める感じでたのしい。

立ちあがり時間

これは 18ms 待ちあとで AVR 側を入力に切り替えてハイインピーダンス状態にした直後の立ちあがり。立ちあがりにちょっとかかっていて、その後11usぐらい待ち時間がある。

参考

DHT22 というプロトコル互換デバイスについてのものがあったのでコードほぼそのままです。

DHT22を使った温度計・湿度計の製作 - henteko.org

  1. トップ
  2. tech
  3. DHT11 という温湿度センサーを読む

概要

連続信号をADCでサンプリングする場合、前段にローパスフィルタが必要だが、それをどう設計したらいいかわからない、という話。

ローパスフィルタの設計

ADC する場合、サンプリング周波数の2分の1 (ナイキスト周波数) 以上の周波数はローパスで完全に遮断する必要がある。さもなければ折り返し雑音が入り、これは後段では正常なデータと区別がつかないので対処しようがない。

ある周波数で「遮断」するというのは、その周波数において、分解能ないしノイズレベル未満まで減衰するということ。最大 1V の信号を 10bit ADC (1024段階)する場合、0.97mV 未満であれば分解能未満になる。この場合 20 * Math.log10(1024) = 60.2dB 減衰すれば、遮断したといえる。

そしてこの場合、サンプリング周波数が 10kHz であれば、5kHz 時点で 60.2dB 減衰させるようにフィルタを設計する。次に、どの程度まで完全に通過させるかでフィルタの性能 (次数) が決定する。当然通過帯域がナイキスト周波数に近ければ近いほどADCの性能を生かせるが、そのためには性能の良いフィルタが必要になる。

フィルタの特性にはいくつか種類がある。(同じぐらいの回路規模での比較)

  • バターワース特性
    • 通過帯域内はフラットだが、減衰傾度がゆるい
  • チェビシェフ特性
    • 減衰傾度はきついが、通過帯域内にリプルが発生する

ローパスフィルタの次数はそのまま減衰傾度になる。1次のバターワースフィルタは周波数が倍で出力が半減 (=-6dB/octave)。次数が高いほど減衰が強くなる (2次=-12dB/octave, 3次=-18dB/octave)。高い性能のフィルタが必要な場合は高次のフィルタ (部品点数の増加) が必要になる。

ADCの前段に入れる場合、できる限り急激な減衰が欲しくなるので、まずチェビシェフを検討することになる。そしてチェビシェフ特性の場合、リプルがどの程度発生するかというのも設計で考慮する必要がある。もしリプルが分解能ないしノイズレベル未満にできれば、それは問題にはならない。

10bitの場合リプルは 20 * Math.log10(1 / (1 - 1/1024.0)) = 0.0085dB 未満であれば完全に問題にならないが、リプルの大きさと減衰傾度はトレードオフの関係にある。

回路構成などによっては妥協する必要がある。妥協点は

  • 分解能
  • 通過帯域

そして、フィルタの実現方法として

  • パッシブ型
    • CR, LCR のみ
  • アクティブ型
    • オペアンプを使う

がある。パッシブ型は減衰のみしかできない。アクティブ型は同時に増幅もでき、特性を改善できる、という特徴があるらしい。あとどうも L を含む回路は好まれないらしく (誤差が多いから?) あまり使われないみたいだ。

わからないこと

  • 応答特性
  • 位相変化特性

フィルタ特性としてこれらもでてくるけどどのように影響していくるかわかってない。

また、ADC の場合で、特に例えば後段で FFT する場合、減衰傾度をゆるやかにしてデジタルでフィルタ特性にあわせたレベル補正をかけたり、という小手先テクニックも使えそうだけど、それがうまくいくかわかっていない。

  1. トップ
  2. tech
  3. ADC とローパスフィルタについてのメモ

これを試しにブレッドボードでやってみた。

  • 上側の「T型」がローパス、下側の「T型」がハイパスフィルタとして働き、ツインT型部分でバンドリジェクトフィルタを構成する?
  • 上側は位相が遅れ、下側は位相が進む
  • トランジスタのコレクタ側から負帰還させて発振させてる

定数決定がむずかしそう。いまいちよくわからない。

Wikipedia によると

R1 = R2, C2 = C3, R3 = R1 / x, C1 = C2 * x で x 倍以上増幅すれば発振器になるとのことらしい。このとき、周波数は以下の式

手元のやつでやって実際計ってみた。電源電圧は 3V ぐらい。電圧をあげると周波数もあがるっぽい…

R1=10k, C2=0.012u

C1 は 0.1u

R = 10e3; C = 0.012e-6; 1 / (2 * Math.PI * R * C )
//=> 1326.2911924324612

実際は 1650Hz ぐらいで発振してる。このぐらい誤差なのかがよくわからない。

R1=22k, C2=0.012u

C1 は 0.1u

R = 22e3; C = 0.012e-6; 1 / (2 * Math.PI * R * C )
//=> 602.859632923846

しかし発振せず。

R1=10k, C2=0.027u

R = 10e3; C = 0.027e-6; 1 / (2 * Math.PI * R * C )
//=> 589.4627521922049

786Hz

R1=10k, C2=0.1u

C1 も C2 も 0.1u

R = 10e3; C = 0.1e-6; 1 / (2 * Math.PI * R * C )
159.15494309189535

244Hz

この状態で C1 に 0.1u 並列でつないで 0.2u にすると、210Hz 付近に発振周波数がさがる。

  1. トップ
  2. tech
  3. ツインT型発振回路
  1. トップ
  2. ham
  3. ツインT型発振回路
  1. トップ
  2. 発振回路
  3. ツインT型発振回路

現状、あんまり効率的な開発手法みたいなことに興味が沸いていない。自分が楽できるように効率的にすることには興味があるけど、チーム的にこう進めたほうがいいみたいなことには興味がない。自分でいくらそれを考えても、どうも意味がないようだという感じだからだと思う。

Raspberry Pi を中心にやってみようとしているところ | avr | raspberrypi | ham - 氾濫原 を実現しようとケース加工して、ひとまず収めた。ただしもやもやと考えていた以下の機能を実現せず、基本機能に絞って実装した。

  • センサー類
    • 外に出すべきか悩んで結局やめた
  • 無線機の操作性改善的なこと
    • たとえば width を変えられるボタンをつけるとか

モデリング

どのぐらいの大きさのケースが必要か? というレベルから検討が必要だったので、Raspberry Pi 及び周辺回路のくみあがりをおおよそで SketchUp でモデリングして配置を検討した。

これはそこそこ意味があったと思う。組みあがった基板をちゃんと計ってフロントパネルの穴開け部分を事前に書いたりしてる。

回路全体を実装

一部ブレッドボードになっていた部分をユニバーサル基板に全ての機能を実装しなおした。一発で動かなかったけど回路図を間違えて作っていて、回路図を間違えていると実体配線図も自動的に間違えるので残念な感じの間違いになっていた……

ケース加工

モデリングしたデータを等倍印刷して、普通の紙用のスティック糊ではりつけて、穴開けをした。

フロント

ボタンとLEDあたりが一番位置決めがシビアでうまくいくか不安だったけど、モデリングして印刷作戦がうまくいってピッタリにできた。

四角の穴をあけるのも初めてだったので不安だったけどなんとかあけられた。



ぴったりサイズの線ではなく、1m ぐらい大きい枠線を書くべきだったのが反省点だった。しかし四角の穴あけるの大変すぎて二度とやりたくない感が強い。

バック

裏側は Raspberry Pi の USB 端子を出す穴 (Wi-Fi があるので外に出す必要がある) と、RS-232 コネクタを取り付ける穴、DC ジャックの穴が主で、Raspberry Pi の USB 端子の穴だけ、位置が決まっているので気をつける必要があった。結果的にはうまくいったけど、取り付け穴がぎりぎりの位置になってしまって作業しにくかった。

RS-232 コネクタは最終的に穴の形がわからなくなるので適当にあけた。

完成

液晶の表示内容は考え中で、ソフトウェア側は (だいたいできてはいるけど) ちょいちょいいい感じにする。

  1. トップ
  2. tech
  3. ケース加工してハードウェア部分をフィックス
  1. トップ
  2. ham
  3. ケース加工してハードウェア部分をフィックス

アルゴリズムを原理から実装するみたいなのが相変らずできないわけだけど、そうでないものなら比較的すぐに実装を書けるようになってはきているので、普段の生活でちょっとしたハックみたいなのはコストパフォーマンスが非常に高く、やっていて面白い。

なんとなく以下らへんのスキルを Lv 7/10 ぐらい (Lv5 でギリギリ実用、みたいな) まで上げれたら上から下までできます!!って言えそうだけど、だいぶ遠く感じる。書いたレベルは現状の甘めの自己評価です。スキルポイントが足りてないんで、スキルシミュレーターとかが欲しいですね。

  • ウェブのフロントエンド (JS, HTML, CSS)
    • Lv7
  • ウェブのサーバーサイド (Ruby, Perl, JS)
    • Lv7
  • サーバーミドルウェア (httpd, memcached etc...)
    • Lv5
  • ネットワーク (ルーティングとか、ネットワーク冗長性とか)
    • Lv3
  • OS (マルチタスクとかメモリ管理とか)
    • Lv2
  • ハードウェアドライバ (OS 上で USB のドライバを自力で書くとか)
    • Lv2
  • ハードウェア組み込み (OS なしでハードウェアIOを叩くとか)
    • Lv2
  • 電子回路 (特にデジタル回路・デジタル回路実装に必要なアナログ回路の知識)
    • Lv2

ネットワークとかミドルウェアあたりは、会社にいると専門の人がいるから自分でほぼやることはなくて、あんまりスキルあがっていかない。それと独学だと解決すべき大きな問題が発生しないので、スキル上げしにくい。パーティプレイ必須はぼっちプレイヤーにはつらい。

ハードウェア以下の低いレイヤーは、全く仕事の役には立たないけどモチベーションは比較的高く、解決したいちょうどいい問題が結構ころがっているのでソロプレイでもスキル上げしやすい感じがする。ただ、一定以上からパーティプレイ必須で、ソロだと決して上がらなそう。

ウェブよりのレイヤーはソロプレイでスキル上げしやすい。わからないことは結局ソース見たら解決するし、バグってるの見つけたら pull-req する、みたいなのでレベルが上がっていく。

なので、ソロで上げれる限界的には Lv10, Lv10, Lv7, Lv5, Lv6, Lv5, Lv5, Lv6, Lv7 みたいな感じになりそう。

土曜日午後から頭痛がひどくなり寝た。翌日日曜日も頭痛がひどく、体温を計ると37度台後半でずっと寝てた。月曜日もまだ微熱で頭痛で会社休んで寝てた。

  1. トップ
  2. 体調不良
  3. 風邪

電子部品の足の間隔とかは 0.1inch == 100mil で作られている。ユニバーサル基板のピッチが 0.1inch == 100mil。mil は1000分の1 inch

  1. トップ
  2. tech
  3. mil

Raspberry Pi を中心にやってみようとしているところ | avr | raspberrypi | ham - 氾濫原 について、まず電源をつくった。

手元にサンハヤトのICB-90という基板がたくさんあるので、これに実装した。

とりあえずこれで Raspberry Pi を動かしてみたけど、スイッチング部分はちょっとあたたかいぐらいだった。

Eagle で配置を検討したら Print を Mirror とUpside down にチェックをいれて行うと裏面から見た感じがつくれるので配線が楽だった。あとなんか単位 (inch, mm) を切り替えながらやっていたらグリッドからずれることがあって困ったけど、これは Cmd を押しながら移動を行うことでグリッドにフィットするようだった。

  1. トップ
  2. tech
  3. 12V, 5V, 3.3V 電源
  1. トップ
  2. ham
  3. 12V, 5V, 3.3V 電源