移動用のオールモードトランシーバーが欲しい

と思いはじめ、いろいろ調べてみた。移動といっても車も自転車もないので、バッテリーやアンテナなども含め徒歩や公共交通機関で運搬可能な範囲でなければならない。

国内だと FT-817ND というリグがこの用途ではスタンダードなようでかなりたくさんの人が使っている。FT-817ND は2001年発売とかなので、おそろしく長く売ってる無線機となっていて、国内には競合製品がない。

ほかにも候補があるか?と思ってさらに調べてみると、アメリカ Elecraft 社の KX3 というのが良さそうだということがわかった。こちらは2012年?ごろに発売された比較的新しい無線機で、FT-817ND と同じぐらい小さい。

結論からいうと表題の通り KX3 を買ったが、以下のポイントで決めた

  • 変調/復調を全部DSPでやってる
    • クラシックなスタンドアロン無線機の外観だが中身は完全にSDRでかっこいい
    • I/Q信号 (直交信号) を出力する端子があるので、サンプリングデバイスさえあればPC SDRも可能
  • 消費電力がFT-817NDに比べて多少低い (特に待機電力は低い)
    • 移動に使いたいので少しでも効率が良いと嬉しい
  • 軽い
  • ファームウェア更新の頻度が高い
    • ちゃんとメンテされており、なおかつ更新して改善できるほどの柔軟性がある

問題は主にコスト面で、FT-817ND がフィルタ込みで9万円ぐらいで買えるのに対し、KX3 は必要なオプションを入れていくと1.5倍ぐらいになる。また、技適を取得していない無線機なので、局免をとるのに結構ハードルがある。

届くまで

特に何も考えず Elecraft に直で注文した。

  1. JSTで木曜日の午前中に発注 USPS Priority Mail Express International
    1. オーダー確認メールがすぐにくる
    • なんか他の人だとこのメールに返信してる人がいるけど、ただの確認なので返信する必要ないと思う
  2. JSTで金曜日の早朝に発送
    1. PST だと木曜日の午後なので、2営業日以上かかると書いてあったけどかなり早く発送された
  3. カリフォルニア州フリーダム → サンノゼ → サンフランシスコと移動
  4. 金曜日のうちに Processed Through Sort Facility / ISC SAN FRANCISCO (USPS) になる (サンフランシスコ国際空港から発送の状態)
    1. PST では金曜早朝
  5. その日のうちに航空便に乗るかなと思ったが乗らず5日(3営業日)経過
  6. 翌週、JST 火曜日の夜に東京国際郵便局に着
    1. USPS側ではステータスは更新されず、JP (Japan Post) 側だけ先に更新
  7. 水曜日午前中、通関手続中に
  8. 2時間ぐらいで次の通関手続中
  9. 木曜日午前中に国際交換局から発送に
  10. 金曜の早朝に配達店到着
  11. 金曜の昼ごろ到着

国外にあるうちは USPS のほうが細かくステータスがアップデートされるけど、国内に入った瞬間からJPのほうが早く更新されるようになるみたい。2時間ぐらい遅れてUSPSにも反映される。


オプションや価格

モジュールキットと完成品とがあるんだけど、せっかくなのでモジュールキットにした。地味にこういうの苦手なのでちょっと心配なんだけど、Elecraft の About ページ 見てたらキットにしなければならない気がしてきたのでそうした。

ケーブルセットは案外 L 型のコネクタが手に入りにくいので買っておいた。ルーフィングフィルターは自分の用途だとたぶん必要ないけど、CW メインでやるという意気込みをもって追加した (あとから追加すると調整が面倒)。アンテナチューナーはワイヤーアンテナでもいける感じのやつなので、最悪長いケーブルさえあれば出れるように追加した。

  • KX3-K KX3 160-6 M Xcvr (Modular Kit)
  • KX3-PCKT Accessory Cable Set
  • KXFL3 dual-Passband Roofing Filter
  • KXAT3 Internal, 20-W Automatic Antenna Tuner

なかなかかかる。無線機に関税はかからないけど、消費税は税関でかかる。

注文しちゃったあとで調べたら国内代理店だと、ちょうど代理店の手数料分1万ちょいだけ浮く感じだった。直販だと届くまでに日数がかかるのと、ちゃんと届くのが不安とかがあるので、代理店で買ってもいいレベルの差ではあるかもなあという印象。

組立

思いのほか小さい箱で届く。

検品からはじめて、最初に動かすまで4時間ぐらいかかってしまった。めちゃくちゃ難しいみたいなことはないんだけど、小さいだけあって中身が狭いので、ビス締めるのが微妙にむずかったり、ケーブルの接続がしにくいとかがあった。とりあえずちゃんと動いたのでよかった。

ビスの長さが一部 (スピーカーまわり) で違ったんだけどなんとかなったっぽい。アメリカンな感じ…

ファームウェアはなぜか最初から最新 (2014-07-11) の beta 版 (production 版と別れてるんだけど) が書きこまれていたのでアップデートはしていない。

  1. トップ
  2. ham
  3. Elecraft KX3

よくあるツールなんだけど、なかなか希望に叶うものというと見つけにくく、どうせなら自分で書いたらいいかと思ったので書いてみた。やってみたら割とすぐ書けた。

MacRuby のインストールが必要。1ファイルにしたかったので、XCode なしで使っている。あんまり XCode なしでの作例がないが普通に NSApplication.sharedApplication を取得したらいいだけだった。

NSEvent.addGlobalMonitorForEventsMatchingMask:handler: は「システム環境設定」→「セキュリティとプライバシー」→「アクセシビリティ」で macruby を許可しないと使えない。これができるということは、すなわちキーロガーが実装できるということなので、必要なときだけ許可するほうがいいと思う。

#!macruby

framework "Cocoa"

class MainView < NSView
	def init
		super
		@log = ""
	end

	def drawRect(rect)
		super
		NSColor.clearColor.set
		NSRectFill(bounds)

		font = NSFont.boldSystemFontOfSize(24)

		shadow = NSShadow.alloc.init
		shadow.setShadowColor(NSColor.blackColor)
		shadow.setShadowBlurRadius(2)
		shadow.setShadowOffset([0, 0])
		attrs = NSMutableDictionary.alloc.initWithDictionary({
			NSForegroundColorAttributeName => NSColor.whiteColor,
			NSFontAttributeName            => font,
			NSShadowAttributeName          => shadow,
		})

		y = 0
		@log.split(/\n/).reverse.each do |line|
			storage   = NSTextStorage.alloc.initWithString(line, attributes: attrs)
			manager   = NSLayoutManager.alloc.init
			container = NSTextContainer.alloc.init

			manager.addTextContainer(container)
			storage.addLayoutManager(manager)

			range = manager.glyphRangeForTextContainer(container)
			10.times do
				manager.drawGlyphsForGlyphRange(range, atPoint: [0, y])
			end
			rect = manager.boundingRectForGlyphRange([0, manager.numberOfGlyphs], inTextContainer: container)
			y+= rect.size.height
		end
	end

	def <<(log)
		@log << log
		@log = @log.split(/\n+/, -1).last(5).join("\n")
	end

	def clear
		@log.clear
	end

end

class AppDelegate
	attr_accessor :window
	def applicationDidFinishLaunching(a_notification)
		@enable = true

		prevKeyed = 0
		unless AXIsProcessTrusted()
			$stderr.puts "Require setting"
			system('open', '/System/Library/PreferencePanes/Security.prefPane')
			exit
		end

		NSEvent.addGlobalMonitorForEventsMatchingMask(NSKeyDownMask, handler: lambda {|e|
			return unless e.type == NSKeyDown

			mod = ""
			if e.modifierFlags & NSShiftKeyMask != 0
				mod += "⇧"
			end

			if e.modifierFlags & NSControlKeyMask != 0
				mod += "⌃"
			end

			if e.modifierFlags & NSAlternateKeyMask != 0
				mod += "⌥"
			end

			if e.modifierFlags & NSCommandKeyMask != 0
				mod += "⌘"
			end

			if (e.modifierFlags & NSControlKeyMask != 0) && (e.modifierFlags & NSCommandKeyMask != 0) && e.charactersIgnoringModifiers == 'l'
				@enable = !@enable
				@view.clear
				@view << (@enable ? "[enabled]" : "[disabled]")
				@view.needsDisplay = true
				return
			end

			if @enable
				if e.modifierFlags & (NSControlKeyMask | NSAlternateKeyMask | NSCommandKeyMask) == 0
					char = readable(e.characters)
					if Time.now.to_i - prevKeyed > 1
						@view << "\n#{char}"
					else
						@view << char
					end
				else
					@view << "\n#{mod}#{readable(e.charactersIgnoringModifiers).upcase}\n"
				end
				@view.needsDisplay = true
			end

			prevKeyed = Time.now.to_i
		})
		rect = [0, 0, 800, 500]
		@window = NSWindow.alloc.initWithContentRect(rect, styleMask: NSBorderlessWindowMask, backing: NSBackingStoreBuffered, defer: 0)
		@window.opaque = false
		@window.hasShadow = false
		@window.level = 1000
		@window.movableByWindowBackground = true
		# @window.ignoresMouseEvents = true
		@window.makeKeyAndOrderFront(nil)
		@window.orderFrontRegardless

		@view = MainView.alloc.initWithFrame(rect)
		@view.init
		@view << "Initialized"

		@window.contentView = @view
	end

	REPLACE_MAP = {
		"\r"   => "↵\n",
		"\e"   => "⎋",
		"\t"   => "⇥",
		"\x19" => "⇤",
		" "    => "␣",
		"\x7f" => "⌫",
		"\x03" => "⌤",
		"\xEF\x9C\xA8" => "⌦",
		"\xEF\x9C\x84" => "[F1]",
		"\xEF\x9C\x85" => "[F2]",
		"\xEF\x9C\x86" => "[F3]",
		"\xEF\x9C\x87" => "[F4]",
		"\xEF\x9C\x88" => "[F5]",
		"\xEF\x9C\x89" => "[F6]",
		"\xEF\x9C\x8A" => "[F7]",
		"\xEF\x9C\x8B" => "[F8]",
		"\xEF\x9C\x8C" => "[F9]",
		"\xEF\x9C\x8D" => "[F10]",
		"\xEF\x9C\x8E" => "[F11]",
		"\xEF\x9C\x8F" => "[F12]",

		"\xEF\x9C\x80" => "↑",
		"\xEF\x9C\x81" => "↓",
		"\xEF\x9C\x82" => "←",
		"\xEF\x9C\x83" => "→",
		"\xEF\x9C\xAC" => "⇞",
		"\xEF\x9C\xAD" => "⇟",
		"\xEF\x9C\xA9" => "↖",
		"\xEF\x9C\xAB" => "↘",
	}

	def readable(char)
		p char
		re = Regexp.new(REPLACE_MAP.keys.map {|i| Regexp.escape(i) }.join("|"))
		char.gsub(re, REPLACE_MAP)
	end
end

app = NSApplication.sharedApplication
app.delegate = AppDelegate.new
app.run
  1. トップ
  2. tech
  3. MacRuby でスクリーンキャスト用のキー履歴表示ツールを作る

電子負荷

電源のテストを行いたいときは、適当な抵抗を繋いだりするわけだが、特定の抵抗値を狙ってつくるのはめんどうくさく、また許容損失が大きいものはつくりにくい。

そこでパワーバイポーラトランジスタやパワーFETを使って可変抵抗にする、というのが電子負荷らしい。

仕様

電子負荷には定抵抗モード・定電流モード・定電力モードなどいろいろあるが、今回は面倒なので定電流モードのだけを考えてある。

  • 14V 3A (42W) ぐらい流したい
  • 温度によりシャットダウンをつけたい
  • 簡易電流・電圧・電力計をつけたい

完成

とりあえずこんな感じになった。

回路図

試行錯誤

メインのパワーFETは2SK1122に

  • 25℃時の許容損失が100W
  • エンハンスメント型かつ Vth が低い
  • 1個150円ぐらいで割と安い

許容損失は温度上昇に伴なって下がっていくが、このFETの場合 60℃ぐらいでは70W、90℃までいくと50Wになる。

放熱が必須なのでとりあえず安いCPUクーラーを買った。1000円もしないが PWM 制御のファン付きでお得

実験1

まず単体の 2SK1122 のゲートに可変抵抗で分圧した電圧を加えつつ、放熱器に貼りつけて 12V 2A 程度まであげつつ流してみた。

指で触れられないぐらい熱くなるということがまずわかったので、ちゃんと温度を測りながらにするため、直読温度センサーも一緒に貼りつけて温度を実測しながらに切り替えた。

FET単体でも定電流素子として使われることがある通り、これでもある程度は安定して流れてくれる。ただ、FET が定電流なのは温度が変わらないことや、負荷電圧が変化しないことが条件なので、温度上昇に伴なって電流量は増えていってしまい、電流量が増えるとさらに発熱するという、ある種の暴走状態になる。この実験では放熱器をつけて、電源側で最大2Aに制限しているので、65℃ぐらいで安定する感じだった。

実験2

電流制限を安定させるため、オペアンプのフィードバックをつけた。オペアンプは単電源でつかえて安い LM385 にした。動作が遅いので発振もしにくそう。

オペアンプの入力の +/- は常に同じ電位になるように調整される (バーチャルショート/イマジナリショート)

+ に入れた電圧と、抵抗上部の電圧が等しくなるようにオペアンプの出力が自動でいい感じに変化するという挙動になるため、+ に入れた電圧と抵抗の比 (I=V/R) にのみ依存する形で定電流動作をするようになる。いろいろ別に抵抗がついているのは動作安定のため (FETの入力の抵抗はオペアンプの出力電流を制限するため・帰還の抵抗は発振防止)。

入力側の分圧抵抗によってレンジを決めている。入力する最大電圧を設定することで最大電流を決められる。

実験3

放熱器に素子をもっと押しつけるといいみたいな話を聞いたので、FETまわりだけケースに取り付けることにした。CPU クーラーなのでプッシュピンがついており、固定にはこれを使える。だいたい 75mm の幅でM4の穴をあけると丁度いいみたい。

ただ、これだけだとFETがケースにちゃんと接触せず、圧力がかからないので、適当なナットを貼りつけて厚さを増やしている。

この状態で 2A 12V かけると 55℃程度で安定する。13.8V 5A かけると90℃ぐらいで安定する (69W 90℃なので許容損失はオーバーだけど)

その他実装

コントローラはAVRで、以下のような機能を実装してある

  • 温度によってファン速度の変更 (ヒステリシスに動く)
  • 電圧計・電流計・電力計
    • ボタンで切り替え
  • 現在温度からFETの許容損失を計算し、一定の範囲内から超えたことが観測された場合強制的に停止する
    • 強制的に温度表示に切り替わって点滅することで状態表示

表示は4桁の7セグを1セグごとにダイナミック点灯させている。1桁ずつ表示するのが普通っぽい?けど、部品を増やしたくなかったので、同時に電流が流れすぎないようにした。

やばそうなとき強制停止する機能をつけたかったので、オペアンプの入力用の電圧はAVRのポートから出している。ただ、あんまりこれは電圧が安定していないので、シャントレギュレータで基準電圧をつくってからボリュームで分圧してる。上側のボリュームは半固定で、最大値を調整するためにつけてる。実際操作するのは下側のボリュームになってる。

  1. トップ
  2. tech
  3. 今 (物理的に) 半導体が熱い!!! (電子負荷)