5. リモコン信号定義データの相互変換¶
黒豆や IRKit, KURO-RS, クロッサム2+ などのプログラマブルな学習リモコン (スマートリモコン) の信号データを、リモコン同士ツノつき合わせて学習させるのではなく、PC 上で理想的な波形に合成するためのノウハウです。
- 赤外線リモコンの信号定義データの合成 [表紙]
- 家製協フォーマットの信号データ合成
- NEC フォーマットの信号データ合成
- ソニーフォーマットの信号データ合成
- SHARP フォーマットの信号データ合成
- リモコン信号定義データの相互変換 (このページ)
- リモコン信号の解析
- メーカー純正リモコンのコード
このページでは、黒豆、IRKit 用の定義データや、事実上の世界標準である Pronto HEX 形式の信号定義データを、相互に変換するサンプルコードを紹介します。本稿の他のページで使用している汎用定義データ形式との変換も可能です。
5.1. 信号定義データの相互変換¶
使い方¶
irconvert [--in FORMAT] [--out FORMAT] [--repeat nn] [ファイル名 ...]
- 入力は、1行に定義データ一つの形式のテキストファイル。ファイル名をしていないときは標準入力を読む。
- 出力は、入力の各行を変換したテキスト (標準出力)。
なお、変換時に丸め誤差が出るため、変換を繰り返すと信号波形が劣化します。
オプション¶
--in ; 入力の形式
pronto, mame (黒豆), irkit, kurors, generic (汎用定義)
デフォルトは generic
--out ; 出力の形式
pronto, mame (黒豆), irkit, kurors, generic (汎用定義)
デフォルトは generic
--repeat ; 信号を指定回数繰り返す定義を出力する
使用例¶
IRKit で学習済みの定義データを黒豆用に変換する
$ irconvert --in irkit --out mame 入力ファイル
インターネットで検索して入手した Pronto HEX 形式の定義データを黒豆用に変換する
$ echo 0000 006D .... 0235 | irconvert --in pronto --out mame
ソニー BRAVIA に「電源 OFF」を 3 回送信する IRKit 用定義データを作成する (irsony は こちら を参照)
$ irsony 1 46 | irconvert --repeat 3 --out irkit
シャープ AQUOS に「電源 ON」を 2 回送信する黒豆用定義データを作成する (irsharp は こちら を参照)
$ irsharp 17 74 1 | irconvert --repeat 2 --out mame
パナソニック VIERA に「電源 OFF」を送信する KURO-RS 用定義データを作成する (iraeha は こちら を参照)
$ iraeha 02 20 80 00 3f bf | irconvert --out kurors
サンプルコード¶
#! /usr/bin/perl
#
# Usage: irconvert [options ...] [input_file ...]
# options:
# --in FORMAT
# --out FORMAT
# --repeat num
# FORMAT:
# pronto, rm, irkit, kurors, generic
#---------------------------------------------------------------- 定数値
%input_functions = (
'pronto' => \&pronto2ic, 'hex' => \&pronto2ic,
'rm' => \&rm2ic, 'mame' => \&rm2ic, 'kuromame' => \&rm2ic,
'irkit' => \&irkit2ic,
'kurors' => \&kurors2ic, 'pcoprs1' => \&kurors2ic,
'generic' => \&gen2ic, 'raw' => \&gen2ic,
);
%output_functions = (
'pronto' => \&ic2pronto, 'hex' => \&ic2pronto,
'rm' => \&ic2rm, 'mame' => \&ic2rm, 'kuromame' => \&ic2rm,
'irkit' => \&ic2irkit,
'kurors' => \&ic2kurors, 'pcoprs1' => \&ic2kurors,
'generic' => \&ic2gen, 'raw' => \&ic2gen,
);
#-----------------------------------------------------------デフォルト値
$input_function = $input_functions{'generic'};
$output_function = $output_functions{'generic'};
$repeat_user_specified = 0;
#---------------------------------------------- コマンドライン引数の解析
push (@ARGV, $eol = "--##END_OF_ARGV##--");
while (@ARGV > 0 && ($argv = shift (@ARGV)) ne $eol) {
if ($argv =~ /^--(.*)/) {
$option = $1;
if ($option eq 'in') {
$format = shift (@ARGV);
if (! defined ($input_functions{$format})) {
print STDERR "$0: unknown input format '$format'\n";
exit 1;
}
$input_function = $input_functions{$format};
} elsif ($option eq 'out') {
$format = shift (@ARGV);
if (! defined ($output_functions{$format})) {
print STDERR "$0: unknown output format '$format'\n";
exit 1;
}
$output_function = $output_functions{$format};
} elsif ($option eq 'repeat') {
$repeat_user_specified = shift (@ARGV);
} else {
print STDERR "$0: unknown option '--$option'\n";
exit 1;
}
} else {
push (@ARGV, $argv);
}
}
#---------------------------------------------------------- メインループ
while (<>) {
($freq, $repeat, @data) = &$input_function ($_);
if ($freq < 0) {
print "$0: $.: $repeat\n";
} else {
$repeat = $repeat_user_specified if ($repeat_user_specified > 0);
$code = &$output_function ($freq, $repeat, @data);
print $code, "\n";
}
}
exit 0;
#------------------------------------------ フォーマット変換サブルーチン
sub pronto2ic { # pronto hex -> 内部表現
my ($str) = shift (@_);
my (@run) = ($str =~ /\b([0-9a-f]{4})\b/isg);
return (-1, "pronto2ic: invalid form") if (shift (@run) != 0);
@run = map { $_ = hex ($_); } @run;
my ($freq) = 1000 / (shift (@run) * .241246);
my ($length) = shift (@run) + shift (@run);
return (-1, "pronto2ic: too short") if (scalar (@run) != $length * 2);
@run = map { $_ = $_ / $freq * 1000; } @run;
return ($freq, 1, @run);
}
sub ic2pronto { # 内部表現 -> pronto hex
my ($freq, $repeat, @run) = @_;
push (@run, 40000) if (@run % 2 != 0);
$freq = int (1000 / $freq / .241246 + 0.5);
my ($clock) = 1 / ($freq * .241246);
my ($r) = 0.5;
my (@run1) = ();
while ($repeat-- > 0) {
foreach (@run) {
my ($t) = $_ * $clock + $r;
push (@run1, int ($t));
$r = $t - int ($t);
}
}
unshift (@run1, 0, $freq, 0, scalar (@run1) / 2);
@run1 = map { sprintf ("%04x", $_); } @run1;
my ($code) = join (" ", @run1);
return ($code);
}
sub rm2ic { # 黒豆 -> 内部表現
my ($str) = shift (@_);
$str =~ s/[^0-9a-f]//isg;
my (@run) = map (hex, ($str =~ /(..)/isg));
return (-1, "rm2ic: invalid form") if (shift (@run) != 0x26);
my ($repeat) = shift (@run) + 1;
my ($length) = shift (@run) + shift (@run) * 256;
return (-1, "rm2ic: too short") if (@run < $length);
@run = @run[0..$length - 1];
my (@run1) = ();
while (@run > 0) {
if (($_ = shift (@run)) == 0) {
$_ = shift (@run) * 256 + shift (@run);
}
push (@run1, $_ * 1000 / 32.768);
}
return (38, $repeat, @run1);
}
sub ic2rm { # 内部表現 -> 黒豆
my ($freq, $repeat, @run) = @_;
push (@run, 40000) if (@run % 2 != 0);
my ($clock) = 32768 / 1000000;
my ($r) = 0.5;
my (@run1) = ();
while ($repeat-- > 0) {
foreach (@run) {
my ($t) = $_ * $clock + $r;
push (@run1, int ($t));
$r = $t - int ($t);
}
}
$run1[$#run1] = 0x0d05;
@run1 = map { sprintf ($_ >= 256 ? "00%04x" : "%02x", $_); } @run1;
my ($code) = join ("", @run1);
my ($len) = length ($code) / 2;
$code = sprintf ("2600%02x%02x", $len % 256, $len / 256) . $code;
return ($code);
}
sub irkit2ic { # IRKit -> 内部表現
my ($str) = shift (@_);
return (-1, "irkit2ic: invalid form") if ($str !~ /"format":"raw"/);
return (-1, "irkit2ic: invalid form") if ($str !~ /"freq":(\d+)/);
my ($freq) = $1;
return (-1, "irkit2ic: invalid form") if ($str !~ /"data":\[(.*?)\]/);
my (@run) = split (",", $1);
my (@run1) = ();
while (@run > 0) {
my ($t) = shift (@run);
if ($t == 0) {
$t = shift (@run);
@run1[$#run1] += $t / 2;
} else {
push (@run1, $t / 2);
}
}
return ($freq, 1, @run1);
}
sub ic2irkit { # 内部表現 -> IRKit
my ($freq, $repeat, @run) = @_;
push (@run, 40000) if (@run % 2 != 0);
$freq = ($freq > 39) ? 40 : 38;
my ($clock) = 2;
my ($r) = 0.5;
my ($code) = qq({);
$code .= qq("format":"raw",);
$code .= qq("freq":$freq,);
$code .= qq("data":[);
my ($delim) = "";
while ($repeat-- > 0) {
my (@run1) = @run;
while (@run1) {
my ($t) = shift (@run1) * $clock + $r;
while ($t > 65535) {
$code .= $delim . "65535,0";
$delim = ",";
$t -= 65535;
}
$code .= $delim . sprintf ("%d", int ($t));
$delim = ",";
$r = $t - int ($t);
}
}
$code .= "]}";
return ($code);
}
sub kurors2ic { # KURO-RS -> 内部表現
my ($str) = shift (@_);
$str =~ s/[^0-9a-f]//isg;
return (-1, "kurors2ic: invalid form") if (length ($str) != 480);
my ($data) = pack ("H*", $str);
$data = unpack ("b1920", $data);
my (@run) = map (length, $data =~ /(0+|1+)/g);
@run = map { $_ *= 100; } @run;
return (38, 1, @run)
}
sub ic2kurors { # 内部表現 -> KURO-RS
my ($freq, $repeat, @run) = @_;
push (@run, 40000) if (@run % 2 != 0);
my ($clock) = 0.01;
my ($r) = 0.5;
my ($code) = "";
my ($phase) = 1;
while ($repeat-- > 0) {
foreach (@run) {
my ($t) = $_ * $clock + $r;
$code .= $phase x $t;
$r = $t - int ($t);
$phase = 1 - $phase;
}
}
$code = substr ($code . "0" x 1920, 0, 1920);
$code = unpack ("H*", pack ("b1920", $code));
return ($code);
}
sub gen2ic { # generic -> 内部表現
my (@run) = split (" ", shift (@_));
if ($run[0] <= 0) {
return (-1, "gen2ic: invalid form");
}
return (@run);
}
sub ic2gen { # 内部表現 -> generic
return (join (" ", @_));
}