Skip to content

Commit

Permalink
Add decode using keymap
Browse files Browse the repository at this point in the history
Signed-off-by: Sean Young <[email protected]>
  • Loading branch information
seanyoung committed Apr 27, 2024
1 parent 3e30d2f commit b10d0b1
Show file tree
Hide file tree
Showing 15 changed files with 533 additions and 44 deletions.
4 changes: 1 addition & 3 deletions TODO
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,9 @@
- DFA edges can be overlapping - removing overlapping parts
- rc_mapping() kfunc
- cir decode irp should read IrpProtocols.xml
- cir decode keymap should support keymap.toml
- access to io_error in aya
- parse of keymap.toml should ensure that raw codes have trailing spaces
-> remove Message.extend() hack
- cir transmit rawir -S sony15:12 -r 2 ??
- keymap needs aeps/eps/ignore_mask/gap?

irp language oddities

Expand Down
2 changes: 1 addition & 1 deletion irp/src/decoder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use log::trace;
use std::{collections::HashMap, fmt, fmt::Write};

/// NFA Decoder state
#[derive(Debug)]
#[derive(Debug, Clone)]
pub struct Decoder<'a> {
pos: Vec<(usize, Vartable<'a>)>,
options: Options<'a>,
Expand Down
2 changes: 1 addition & 1 deletion irp/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -222,7 +222,7 @@ impl fmt::Display for Event {
}

/// Options for the decoder
#[derive(Default, Debug)]
#[derive(Default, Debug, Clone)]
pub struct Options<'a> {
/// Name of the decoder
pub name: &'a str,
Expand Down
8 changes: 3 additions & 5 deletions irp/src/message.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,10 @@ impl Message {
}
}

/// Concatenate to packets
/// Concatenate two messages. Self must have a trailing gap.
pub fn extend(&mut self, other: &Message) {
assert!(self.raw.is_empty() || self.has_trailing_gap());

if self.carrier.is_none() {
self.carrier = other.carrier;
}
Expand All @@ -27,10 +29,6 @@ impl Message {
self.duty_cycle = other.duty_cycle;
}

if !self.raw.is_empty() && !self.has_trailing_gap() {
self.raw.push(20000);
}

self.raw.extend_from_slice(&other.raw);
}

Expand Down
18 changes: 11 additions & 7 deletions src/bin/cir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -125,8 +125,8 @@ enum DecodeCommands {
#[command(about = "Decode using IRP Notation")]
Irp(DecodeIrp),

#[command(about = "Decode using lircd.conf file")]
Lircd(DecodeLircd),
#[command(about = "Decode using keymap or lircd.conf file")]
Keymap(DecodeKeymap),
}

#[derive(Args)]
Expand All @@ -136,9 +136,9 @@ struct DecodeIrp {
}

#[derive(Args)]
struct DecodeLircd {
/// lircd.conf file
lircdconf: OsString,
struct DecodeKeymap {
/// Keymap or lircd.conf file
keymap: PathBuf,
}

#[cfg(target_os = "linux")]
Expand Down Expand Up @@ -541,8 +541,12 @@ fn main() {
DecodeCommands::Irp(irp) => {
commands::decode::decode_irp(decode, &irp.irp);
}
DecodeCommands::Lircd(lircd) => {
commands::decode::decode_lircd(decode, &lircd.lircdconf);
DecodeCommands::Keymap(keymap) => {
if keymap.keymap.to_string_lossy().ends_with(".lircd.conf") {
commands::decode::decode_lircd(decode, &keymap.keymap);
} else {
commands::decode::decode_keymap(decode, &keymap.keymap);
}
}
},
Commands::Transmit(transmit) => commands::transmit::transmit(transmit),
Expand Down
226 changes: 223 additions & 3 deletions src/bin/commands/decode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@
use super::config::{find_devices, Purpose};
#[cfg(target_os = "linux")]
use cir::lirc;
use cir::lircd_conf::parse;
use cir::{keymap::Keymap, lircd_conf::parse};
use irp::{Decoder, InfraredData, Irp, Message, Options};
use itertools::Itertools;
use log::{error, info};
#[cfg(target_os = "linux")]
use std::path::PathBuf;
use std::{ffi::OsString, fs, path::Path};
use std::{fs, path::Path};

pub fn decode_irp(decode: &crate::Decode, irp_str: &String) {
#[allow(unused_mut)]
Expand Down Expand Up @@ -232,7 +232,227 @@ pub fn decode_irp(decode: &crate::Decode, irp_str: &String) {
}
}

pub fn decode_lircd(decode: &crate::Decode, conf: &OsString) {
pub fn decode_keymap(decode: &crate::Decode, path: &Path) {
#[allow(unused_mut)]
let mut abs_tolerance = decode.options.aeps;
let rel_tolerance = decode.options.eps;
#[allow(unused_mut)]
let mut max_gap = 100000;

let keymaps = match Keymap::parse(path) {
Ok(r) => r,
Err(e) => {
log::error!("{e}");
std::process::exit(2);
}
};

let input_on_cli = !decode.file.is_empty() || !decode.rawir.is_empty();
#[cfg(not(target_os = "linux"))]
if !input_on_cli {
eprintln!("no infrared input provided");
std::process::exit(2);
}

#[cfg(target_os = "linux")]
let lircdev = if !input_on_cli {
// open lirc
let rcdev = find_devices(&decode.device, Purpose::Receive);

if let Some(lircdev) = rcdev.lircdev {
let lircpath = PathBuf::from(lircdev);

log::trace!("opening lirc device: {}", lircpath.display());

let mut lircdev = match lirc::open(&lircpath) {
Ok(l) => l,
Err(s) => {
eprintln!("error: {}: {}", lircpath.display(), s);
std::process::exit(1);
}
};

if decode.learning {
let mut learning_mode = false;

if lircdev.can_measure_carrier() {
if let Err(err) = lircdev.set_measure_carrier(true) {
eprintln!("error: {lircdev}: failed to enable measure carrier: {err}");
std::process::exit(1);
}
learning_mode = true;
}

if lircdev.can_use_wideband_receiver() {
if let Err(err) = lircdev.set_wideband_receiver(true) {
eprintln!("error: {lircdev}: failed to enable wideband receiver: {err}");
std::process::exit(1);
}
learning_mode = true;
}

if !learning_mode {
eprintln!("error: {lircdev}: lirc device does not support learning mode");
std::process::exit(1);
}
}

if lircdev.can_receive_raw() {
if let Ok(resolution) = lircdev.receiver_resolution() {
if resolution > abs_tolerance {
info!(
"{} resolution is {}, using absolute tolerance {} rather than {}",
lircdev, resolution, resolution, abs_tolerance
);

abs_tolerance = resolution;
}
}

if let Ok(timeout) = lircdev.get_timeout() {
let dev_max_gap = (timeout * 9) / 10;

log::trace!(
"device reports timeout of {}, using 90% of that as {} max_gap",
timeout,
dev_max_gap
);

max_gap = dev_max_gap;
}

Some(lircdev)
} else {
error!("{}: device cannot receive raw", lircdev);
std::process::exit(1);
}
} else {
error!("{}: no lirc device found", rcdev.name);
std::process::exit(1);
}
} else {
None
};

let mut decoders = keymaps
.iter()
.map(|keymap| {
let mut options = Options {
name: &keymap.name,
max_gap,
aeps: abs_tolerance,
eps: rel_tolerance,
..Default::default()
};

options.nfa = decode.options.save_nfa;
options.dfa = decode.options.save_dfa;

match keymap.decoder(options) {
Ok(decoder) => decoder,
Err(e) => {
log::error!("{}: {e}", path.display());
std::process::exit(2);
}
}
})
.collect::<Vec<_>>();

let mut feed_decoder = |raw: &[InfraredData]| {
for ir in raw {
for decoder in &mut decoders {
decoder.input(*ir, |name, _| {
println!("decoded: keymap:{} code:{}", decoder.remote.name, name);
});
}
}
};

for filename in &decode.file {
let input = match fs::read_to_string(filename) {
Ok(s) => s,
Err(s) => {
error!("{}: {}", Path::new(filename).display(), s);
std::process::exit(2);
}
};

info!("parsing ‘{}’ as rawir", filename.to_string_lossy());

match Message::parse(&input) {
Ok(raw) => {
info!("decoding: {}", raw.print_rawir());
feed_decoder(&InfraredData::from_u32_slice(&raw.raw));
}
Err(msg) => {
info!("parsing ‘{}’ as mode2", filename.to_string_lossy());

match Message::parse_mode2(&input) {
Ok(m) => {
info!("decoding: {}", m.print_rawir());
feed_decoder(&InfraredData::from_u32_slice(&m.raw));
}
Err((line_no, error)) => {
error!("{}: parse as rawir: {}", Path::new(filename).display(), msg);
error!(
"{}:{}: parse as mode2: {}",
Path::new(filename).display(),
line_no,
error
);
std::process::exit(2);
}
}
}
}
}

for rawir in &decode.rawir {
match Message::parse(rawir) {
Ok(raw) => {
info!("decoding: {}", raw.print_rawir());
feed_decoder(&InfraredData::from_u32_slice(&raw.raw));
}
Err(msg) => {
error!("parsing ‘{}’: {}", rawir, msg);
std::process::exit(2);
}
}
}

#[cfg(target_os = "linux")]
if let Some(mut lircdev) = lircdev {
let mut rawbuf = Vec::with_capacity(1024);

loop {
if let Err(err) = lircdev.receive_raw(&mut rawbuf) {
eprintln!("error: {err}");
std::process::exit(1);
}

let raw: Vec<_> = rawbuf
.iter()
.filter_map(|raw| {
if raw.is_pulse() {
Some(InfraredData::Flash(raw.value()))
} else if raw.is_space() || raw.is_timeout() {
Some(InfraredData::Gap(raw.value()))
} else if raw.is_overflow() {
Some(InfraredData::Reset)
} else {
None
}
})
.collect();

log::trace!("decoding: {}", raw.iter().join(" "));

feed_decoder(&raw);
}
}
}

pub fn decode_lircd(decode: &crate::Decode, conf: &PathBuf) {
#[allow(unused_mut)]
let mut abs_tolerance = decode.options.aeps;
let rel_tolerance = decode.options.eps;
Expand Down
Loading

0 comments on commit b10d0b1

Please sign in to comment.