From e0d9ef677227171ed7f72cee1760d8a17f40edd1 Mon Sep 17 00:00:00 2001 From: Sean Young Date: Mon, 25 Mar 2024 19:02:22 +0000 Subject: [PATCH] Decode and test lircd.conf raw_codes Signed-off-by: Sean Young --- irp/src/build_nfa.rs | 60 ++++++++++++++++++++++++++++++++++++++ src/bin/commands/decode.rs | 4 +-- src/lircd_conf/decode.rs | 54 ++++++++++++++++++++++------------ tests/lircd_conf_tests.rs | 19 ++++++++++-- 4 files changed, 113 insertions(+), 24 deletions(-) diff --git a/irp/src/build_nfa.rs b/irp/src/build_nfa.rs index 3fd6ec42..dfa678bd 100644 --- a/irp/src/build_nfa.rs +++ b/irp/src/build_nfa.rs @@ -84,6 +84,66 @@ impl NFA { pub fn dotgraphviz(&self, path: &str) { crate::graphviz::graphviz(&self.verts, "NFA", &[], path); } + + /// Add nfa states for parsing raw IR + pub fn add_raw(&mut self, raw: &[u32], event: Event, code: i64) { + assert_ne!(raw.len(), 0); + assert_eq!(raw.len() % 2, 1); + + if self.verts.is_empty() { + self.verts.push(Vertex::default()); + } + + let mut pos = 0; + let mut flash = true; + + for raw in raw { + let length = Rc::new(Expression::Number((*raw).into())); + let actions = vec![if flash { + Action::Flash { + length, + complete: true, + } + } else { + Action::Gap { + length, + complete: true, + } + }]; + + if let Some(next) = self.verts[pos].edges.iter().find_map(|edge| { + if edge.actions == actions && self.verts[edge.dest].entry.is_empty() { + Some(edge.dest) + } else { + None + } + }) { + pos = next; + } else { + let next = self.verts.len(); + + self.verts.push(Vertex::default()); + + self.verts[pos].edges.push(Edge { + actions, + dest: next, + }); + + pos = next; + } + + flash = !flash; + } + + self.verts[pos].entry.push(Action::Set { + var: "CODE".into(), + expr: Rc::new(Expression::Number(code)), + }); + + self.verts[pos] + .entry + .push(Action::Done(event, vec!["CODE".into()])); + } } pub(crate) fn gen_mask(v: i64) -> i64 { diff --git a/src/bin/commands/decode.rs b/src/bin/commands/decode.rs index 6b496d8c..a949a285 100644 --- a/src/bin/commands/decode.rs +++ b/src/bin/commands/decode.rs @@ -369,8 +369,8 @@ fn decode_lircd(matches: &clap::ArgMatches) { let mut feed_decoder = |raw: &[InfraredData]| { for (index, ir) in raw.iter().enumerate() { for decoder in &mut decoders { - decoder.input(*ir, |code| { - println!("decoded: remote:{} code:{}", decoder.remote.name, code.name); + decoder.input(*ir, |name, _| { + println!("decoded: remote:{} code:{}", decoder.remote.name, name); }); if graphviz_step { diff --git a/src/lircd_conf/decode.rs b/src/lircd_conf/decode.rs index cd64b831..f42b5bbd 100644 --- a/src/lircd_conf/decode.rs +++ b/src/lircd_conf/decode.rs @@ -1,4 +1,4 @@ -use super::{Code, Remote}; +use super::Remote; use irp::{Decoder, InfraredData, Irp, NFA}; use log::debug; @@ -16,13 +16,23 @@ impl Remote { rel_tolerance: Option, max_gap: u32, ) -> LircDecoder { - let irp = self.decode_irp(); + let nfa = if self.raw_codes.is_empty() { + let irp = self.decode_irp(); - debug!("decoding irp {irp} for remote {}", self.name); + debug!("decoding irp {irp} for remote {}", self.name); - let irp = Irp::parse(&irp).unwrap(); + let irp = Irp::parse(&irp).unwrap(); - let nfa = irp.build_nfa().unwrap(); + irp.build_nfa().unwrap() + } else { + let mut nfa = NFA::default(); + + for (i, raw) in self.raw_codes.iter().enumerate() { + nfa.add_raw(&raw.rawir, irp::Event::Down, u32::MAX as i64 + i as i64); + } + + nfa + }; let decoder = Decoder::new( abs_tolerance.unwrap_or(self.aeps as u32), @@ -41,25 +51,31 @@ impl Remote { impl<'a> LircDecoder<'a> { pub fn input(&mut self, ir: InfraredData, mut callback: F) where - F: FnMut(&'a Code), + F: FnMut(&'a str, u64), { self.decoder.nfa_input(ir, &self.nfa, |_, vars| { if let Some(decoded) = vars.get("CODE") { - // TODO: ignore mask, toggle_bit_mask with many bits set - let mask = if self.remote.toggle_bit_mask.count_ones() == 1 { - !self.remote.toggle_bit_mask - } else { - !0 - }; + if self.remote.raw_codes.is_empty() { + // TODO: ignore mask, toggle_bit_mask with many bits set + let mask = if self.remote.toggle_bit_mask.count_ones() == 1 { + !self.remote.toggle_bit_mask + } else { + !0 + }; + + let decoded = *decoded as u64; + if let Some(key_code) = self.remote.codes.iter().find(|code| { + let code = code.code[0] & mask; + let decoded_masked = decoded & mask; - let decoded = *decoded as u64; - if let Some(key_code) = self.remote.codes.iter().find(|code| { - let code = code.code[0] & mask; - let decoded = decoded & mask; + code == decoded_masked || code == (decoded_masked ^ self.remote.repeat_mask) + }) { + callback(&key_code.name, decoded); + } + } else { + let decoded: usize = *decoded as usize - u32::MAX as usize; - code == decoded || code == (decoded ^ self.remote.repeat_mask) - }) { - callback(key_code); + callback(&self.remote.raw_codes[decoded].name, decoded as u64); } } }) diff --git a/tests/lircd_conf_tests.rs b/tests/lircd_conf_tests.rs index 14de5ba7..d3aca93f 100644 --- a/tests/lircd_conf_tests.rs +++ b/tests/lircd_conf_tests.rs @@ -53,6 +53,8 @@ fn lircd_encode_decode(path: &Path) { let our_remote = our_conf.next().unwrap(); + let mut decoder = our_remote.decoder(Some(10), Some(1), 200000); + if lircd_remote.is_raw() { for (our_code, lircd_code) in our_remote.raw_codes.iter().zip(lircd_remote.codes_iter()) { @@ -85,13 +87,24 @@ fn lircd_encode_decode(path: &Path) { println!("cir {}", message.print_rawir()); panic!("RAW CODE: {}", our_code.name); } + + let mut decoded = Vec::new(); + + decoder.reset(); + + for ir in InfraredData::from_u32_slice(&message.raw) { + decoder.input(ir, |name, _| { + decoded.push(name); + }); + } + + assert!(decoded.contains(&our_code.name.as_str())); } } if !our_remote.codes.is_empty() { let irp = our_remote.encode_irp(); println!("remote {} irp:{}", our_remote.name, irp); - let mut decoder = our_remote.decoder(Some(10), Some(1), 200000); for (our_code, lircd_code) in our_remote.codes.iter().zip(lircd_remote.codes_iter()) { if our_code.dup { @@ -205,8 +218,8 @@ fn lircd_encode_decode(path: &Path) { .expect("encode should succeed"); for ir in InfraredData::from_u32_slice(&message.raw) { - decoder.input(ir, |code| { - decoded.push(code.code[0] & !lircd_remote.toggle_bit_mask()); + decoder.input(ir, |_, code| { + decoded.push(code & !lircd_remote.toggle_bit_mask()); }); }