8 个版本

0.3.3 2024年4月28日
0.3.2 2023年2月7日
0.3.1 2022年12月10日
0.3.0 2022年8月27日
0.1.0 2021年4月20日

#557编码


用于 cir

MIT 许可证

295KB
7K SLoC

IRP

这是一个使用 红外 语法或 IRP 语法Pronto Hex 编码和解码红外协议的 Rust 库。电视、HiFi 设备、空调等遥控器发送的消息使用了多种不同的协议。使用此库,您可以解码从遥控器接收到的红外信号,或将红外信号编码成与遥控器相同的格式。

当启用 bpf 功能时,解码器可以编译成 BPF。此解码器适用于 Linux 的基于 BPF 的内核解码。

此库只处理编码和解码,并不了解如何与红外设备通信以接收或发送信号;您可以在 Linux 上使用 cir crate 来实现这一点。您还需要您的遥控器协议的 IRP 定义或 Pronto hex 定义。由 IrpTransmogrifierhifi-remote 维护了一个长的 IRP 定义列表。

还有一些用于解析原始红外和 mode2 输出的实用函数。

查看 文档 以查看完整的接口,或使用下面的示例。

原始红外是什么意思?

此库编码为 原始红外原始红外 是红外光交替开启和关闭的持续时间,以微秒为单位。例如:

+500 -100 +500

这意味着红外光开启 500 微秒,关闭 100 微秒,然后再次开启 500 微秒。这也被称为 闪烁间隔,lirc 使用 脉冲空间 这些术语。

原始红外通常以间隔结束。这确保了消息之间的间隔时间是正确的。

什么是 IRP?

这是一个简单的 IRP 示例

{40k,600}<1,-1|2,-1>(4,-1,F:8,^45m)[F:0..255]

IRP 是红外协议的表示法,此库用于编码和解码红外。这通常涉及一些参数,如:

  • F 表示功能,如 播放音量增加
  • D 表示设备;HiFi 设备可以包含多个单元,因此您是想让磁带播放器 播放 还是 CD 播放器?
  • S 表示子设备
  • T 表示切换。按钮是否被按下或释放后再次按下,即 切换T 的值无关紧要,重要的是它是否从一个数据包变为下一个数据包发生变化。
  • 其他特定于协议的值,如空调的加热或冷却。

解码是指从原始红外信号中恢复参数,而编码是指从某些参数值创建原始红外信号。

我们还有一篇关于 IRP 语法 的更深入介绍。

什么是 pronto hex?

这是 Philips Pronto 通用遥控器使用的符号,它是一系列十六进制数字,例如

0000 0070 0003 0002 0006 0002 0004 0002 0004 0006 0006 0003 0003 000С

每个按钮都有一个 pronto hex 代码;它不像 IRP 那样参数化。

重复

当在遥控器上按住按钮时,红外信号会重复,直到按钮释放。即使按钮短暂按下,红外信号也可能重复几次。

一些红外接收器在解码红外之前需要重复。例如,Sony LBT-V702 至少需要一个重复,否则红外信号将被忽略。

编码 IRP

此示例使用 NEC 编码编码按钮按下,然后编码并简单地打印编码结果。

use irp::{Irp, Vartable};

fn main() {
    // nec protocol
    let irp = Irp::parse(r#"
        {38.4k,564}<1,-1|1,-3>(16,-8,D:8,S:8,F:8,~F:8,1,^108m,(16,-4,1,^108m)*)
        [D:0..255,S:0..255=255-D,F:0..255]"#)
        .expect("parse should succeed");
    // Set some values for D, S, and F
    let mut vars = Vartable::new();
    vars.set(String::from("D"), 255);
    vars.set(String::from("S"), 52);
    vars.set(String::from("F"), 1);
    // encode message with 0 repeats
    let message = irp.encode_raw(vars, 0).expect("encode should succeed");
    if let Some(carrier) = &message.carrier {
        println!("carrier: {}Hz", carrier);
    }
    if let Some(duty_cycle) = &message.duty_cycle {
        println!("duty cycle: {}%", duty_cycle);
    }
    println!("{}", message.print_rawir());
}

输出是原始红外格式,如下所示

carrier: 38400Hz
+9024 -4512 +564 -1692 +564 -1692 +564 -1692 +564 -1692 +564 -1692 +564 -1692 +564 -1692 +564 -1692 +564 -564 +564 -564 +564 -1692 +564 -564 +564 -1692 +564 -1692 +564 -564 +564 -564 +564 -1692 +564 -564 +564 -564 +564 -564 +564 -564 +564 -564 +564 -564 +564 -564 +564 -564 +564 -1692 +564 -1692 +564 -1692 +564 -1692 +564 -1692 +564 -1692 +564 -1692 +564 -36372

每条记录都是一个以微秒为单位的持续时间,前面有 + 用于 闪光(红外灯亮)和 - 用于 间隔(红外灯关闭)。这也被称为 脉冲空间

编码 Pronto Hex

由 Philips Pronto 通用遥控器普及的 Pronto Hex。格式是一系列四位十六进制数字。

use irp::Pronto;

fn main() {
    let pronto = Pronto::parse(r#"
        0000 006C 0000 0022 00AD 00AD 0016 0041 0016 0041 0016 0041 0016 0016 0016
        0016 0016 0016 0016 0016 0016 0016 0016 0041 0016 0041 0016 0041 0016 0016
        0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0041 0016 0016 0016
        0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0041 0016 0016 0016 0041
        0016 0041 0016 0041 0016 0041 0016 0041 0016 0041 0016 06FB
        "#).expect("parse should succeed");
    // encode using 1 repeats
    let message = pronto.encode(1);
    if let Some(carrier) = &message.carrier {
        println!("carrier: {}Hz", carrier);
    }
    println!("{}", message.print_rawir());
}

输出

+4507 -4507 +573 -1693 +573 -1693 +573 -1693 +573 -573 +573 -573 +573 -573 +573 -573 +573 -573 +573 -1693 +573 -1693 +573 -1693 +573 -573 +573 -573 +573 -573 +573 -573 +573 -573 +573 -573 +573 -1693 +573 -573 +573 -573 +573 -573 +573 -573 +573 -573 +573 -573 +573 -1693 +573 -573 +573 -1693 +573 -1693 +573 -1693 +573 -1693 +573 -1693 +573 -1693 +573 -46559

将 IRP 编码为 Pronto Hex

IRP 也可以编码为 pronto hex 代码。Pronto hex 代码有一个重复部分,因此不需要重复参数。

use irp::{Irp, Vartable};

fn main() {
    // sony8 protocol
    let irp = Irp::parse("{40k,600}<1,-1|2,-1>(4,-1,F:8,^45m)[F:0..255]")
        .expect("parse should succeed");
    let mut vars = Vartable::new();
    vars.set(String::from("F"), 1);
    let pronto = irp.encode_pronto(vars).expect("encode should succeed");
    println!("{}", pronto);
}

输出

0000 0068 0009 0000 0060 0018 0030 0018 0018 0018 0018 0018 0018 0018 0018 0018 0018 0018 0018 0018 0018 0510

使用 IRP 解码

此示例使用 rc5 协议解码一些红外信号。首先解析 IRP 语法,然后编译 DFA 状态机以进行解码。然后创建一个解码器,它需要一些匹配参数,然后可以提供输入。

use irp::{Irp, InfraredData, Options, Decoder};

fn main() {
    let irp = Irp::parse(r#"
        {36k,msb,889}<1,-1|-1,1>((1,~F:1:6,T:1,D:5,F:6,^114m)*,T=1-T)
        [D:0..31,F:0..127,T@:0..1=0]"#)
        .expect("parse should succeed");
    let options = Options {
        aeps: 100,
        eps: 30,
        max_gap: 20000,
        ..Default::default()
    };
    let dfa = irp.compile(&options).expect("build dfa should succeed");
    // Create a decoder with 100 microsecond tolerance, 30% relative tolerance,
    // and 20000 microseconds maximum gap.
    let mut decoder = Decoder::new(options);
    for ir in InfraredData::from_rawir(
        "+940 -860 +1790 -1750 +880 -880 +900 -890 +870 -900 +1750
        -900 +890 -910 +840 -920 +870 -920 +840 -920 +870 -1810 +840 -125000").unwrap() {
        decoder.dfa_input(ir, &dfa, |event, vars| {
            println!("decoded: {} F={} D={} T={}", event, vars["F"], vars["D"], vars["T"]);
        });
    }
}

这将打印

decoded: down F=1 D=30 T=0

解析原始红外格式

原始红外格式如下所示 +100 -100 +100。开头的 +- 可省略,但如果存在,则需要检查一致性。解析函数返回一个 Message

use irp::Message;

fn main() {
    let rawir = Message::parse("+100 -100 +100").expect("parse should succeed");
    println!("{}", rawir.print_rawir());
}

解析 lirc mode2 脉冲间隔文件

此格式由 mode2 工具 普及,该工具为每个闪光和间隔打印一行,但将其称为 pulsespace。如下所示

carrier 38400
pulse 9024
space 4512
pulse 4512

以下是解析此格式的一个示例。结果以更简洁的原始红外格式打印。

use irp::Message;

fn main() {
    let message = Message::parse_mode2(r#"
        carrier 38400
        pulse 9024
        space 4512
        pulse 4512
    "#).expect("parse should succeed");
    if let Some(carrier) = &message.carrier {
        println!("carrier: {}Hz", carrier);
    }
    if let Some(duty_cycle) = &message.duty_cycle {
        println!("duty cycle: {}%", duty_cycle);
    }
    println!("{}", message.print_rawir());
}

使用 cir crate 发送红外

此示例打开第一个 lirc 设备 /dev/lirc0 并发送 Hauppauge 遥控器的 1 按钮。

# extern crate cir;
use cir::lirc;
use irp::{Irp, Vartable};

const RC5_IRP: &str =
    "{36k,msb,889}<1,-1|-1,1>((1,~F:1:6,T:1,D:5,F:6,^114m)*,T=1-T)[D:0..31,F:0..127,T@:0..1=0]";

fn main() {
    let mut dev = lirc::open("/dev/lirc0").unwrap();

    let mut vars = Vartable::new();
    vars.set("F".into(), 30);
    vars.set("D".into(), 0);
    let irp = Irp::parse(RC5_IRP).unwrap();

    let message = irp.encode_raw(vars, 1).unwrap();

    if let Some(carrier) = &message.carrier {
        // set the carrier frequency (see the 36k in the IRP definition)
        dev.set_send_carrier(*carrier as u32).unwrap();
    }

    // send the message
    dev.send(&message.raw).unwrap();

    println!("done");
}

依赖关系

~2.7–4MB
~82K SLoC