-
Notifications
You must be signed in to change notification settings - Fork 14
/
Copy pathIRAnalysis.py
154 lines (141 loc) · 6.29 KB
/
IRAnalysis.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
# Tick length (in microseconds) as set in IRremote lib
g_TickMicroSec = 50.0
class AcSample(object):
def __init__(self, pwr, mode, fan, temp, decoded_value=None, raws=None):
self.pwr = pwr
self.mode = mode
self.fan = fan
self.temp = temp
self.decoded_value = decoded_value
self.signals = list()
if raws:
self.add_raw_signals(raws)
self.av_signal = None
def str(self):
return '%s %s %s %d' % (self.pwr, self.mode, self.fan, self.temp)
def add_raw_signals(self, raws):
# The raw data for received IR measures the duration of successive
# spaces and marks in 50us ticks.
# The first measurement is the gap,
# the space before the transmission starts.
# The last measurement is the final mark.
# Gaps are listed as negative numbers,
# while marks as positive numbers.
raws_list = raws
if type(raws) == str:
raws_list = [raws]
self.signals.extend([map(int, rs.split()[3:]) for rs in raws_list])
# (leaving out the prefix "Raw (XX):", and the initial gap)
def calc_average_signal(self):
if 1 < len(set([len(sig) for sig in self.signals])):
raise RuntimeError('Raw signals are not of the same size')
self.av_signal = list(self.signals[0])
for sig in self.signals[1:]:
for i, t in enumerate(sig):
self.av_signal[i] += t
for i in xrange(len(self.av_signal)):
self.av_signal[i] /= len(self.signals)
def normalize(self):
# Calculate average signal over all raw signals
self.calc_average_signal()
# Check that every raw signal is "similar enough" to the average one
for n, sig in enumerate(self.signals):
for i, t in enumerate(sig):
diff = abs(t - self.av_signal[i])
if diff > int(2 * g_TickMicroSec):
print 'Signal %s #%d differs by %d in position %d' % \
(self.str(), n+1, diff, i)
return False
return True
def arr_name(self):
return 'uSendBuff_%s_%s_%s_%d' % (
self.pwr, self.mode, self.fan, self.temp)
def to_arduino_array(cmd):
return 'PROGMEM prog_uint16_t %s[] = {%d, %s};' % (cmd.arr_name(),
len(cmd.av_signal),
', '.join(str(int(g_TickMicroSec *
round(abs(t / g_TickMicroSec))))
for t in cmd.av_signal))
def to_time_sig(sig):
values = [1]
for tl in sig:
val = 0.9 if tl > 0 else 0.1
for t in range(int(abs(tl) / g_TickMicroSec)):
values.append(val)
return values
def graph_av_signals(commands):
import matplotlib.pyplot as plt
base = len(commands) * 100 + 11
plt.figure(1)
for idx, cmd in enumerate(commands):
plt.subplot(base + idx)
plt.title('A/C-Remote, %s' % (cmd.str()))
time_sig = to_time_sig(cmd.av_signal)
plt.plot([t * 0.05 for t in xrange(len(time_sig))],
time_sig, lw=0.7)
plt.ylim([0.0,1.0])
plt.ylabel('IR Mark / Space')
plt.xlabel('time [msec]')
plt.show()
def generate_arduino_code(commands):
for cmd in commands:
print cmd.to_arduino_array()
print ''
# Assume other Arduino code created a state using the following variables:
# pwr - enum { Toggle, Leave }
# mode - enum { Cool, Heat, Fan, Dry }
# fan - enum { Auto, Low, Medium, High }
# temp - int
print 'prog_uint16_t * getAcSendBuff() {'
for cmd in commands:
print '\tif ( (%s == pwr) && (%s == mode) &&(%s == fan) && ' \
'(%s == temp) ) { return %s; }' % (cmd.pwr, cmd.mode, cmd.fan,
cmd.temp, cmd.arr_name())
print '\treturn 0;\n}'
def load_from_raw(data_dir):
# Go over files in the data dir, and parse as an AC command sample file
# Each file should be named according to the command state, in the form:
# `pwr-mode-fan-temp`
# and contain in plain text the output of the Arduino IRACReceiver
# program, possibly multiple results per file (to improve accuracy)
import os
commands = list()
for root, _, files in os.walk(data_dir):
for raw_file in files:
# Initialize an AcSample object from raw file name
pwr, mode, fan, temp = raw_file.split('-')
sample = AcSample(pwr, mode, fan, int(temp))
value = None
with open(os.path.join(root, raw_file), 'r') as f:
for line in f:
line = line.strip()
if 0 == line.find('0x'):
# it's a decoded-value line
newval = int(line[:line.find(' ')], 16)
if not sample.decoded_value:
sample.decoded_value = newval
elif sample.decoded_value != newval:
print 'There are inconsistencies in file "%s"' % \
(raw_file)
elif 0 == line.find('Raw'):
# it's a raw-list line
sample.add_raw_signals(line)
if sample.normalize():
commands.append(sample)
else:
print 'Skipping sample for command', sample.str()
return commands
if '__main__' == __name__:
import argparse
parser = argparse.ArgumentParser(description='IR Analysis Tool.')
parser.add_argument('-d', '--dumps-dir', default='test-data/IRdumps',
help='Path to a directory containing IR dump files')
subparsers = parser.add_subparsers()
# Add IR signals graphing subcommand
parser_graph = subparsers.add_parser('graph', help='Graph the IR signals')
parser_graph.set_defaults(func=graph_av_signals)
# Add Arduino code generation subcommand
parser_code = subparsers.add_parser('code', help='Generate Arduino code')
parser_code.set_defaults(func=generate_arduino_code)
args = parser.parse_args()
args.func(load_from_raw(args.dumps_dir))