5. リモコン信号定義データの相互変換

黒豆や IRKit, KURO-RS, クロッサム2+ などのプログラマブルな学習リモコン (スマートリモコン) の信号データを、リモコン同士ツノつき合わせて学習させるのではなく、PC 上で理想的な波形に合成するためのノウハウです。

  1. 赤外線リモコンの信号定義データの合成 [表紙]
  2. 家製協フォーマットの信号データ合成
  3. NEC フォーマットの信号データ合成
  4. ソニーフォーマットの信号データ合成
  5. SHARP フォーマットの信号データ合成
  6. リモコン信号定義データの相互変換 (このページ)
  7. リモコン信号の解析
  8. メーカー純正リモコンのコード

このページでは、黒豆、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 (" ", @_));
}