Advanced Settings Panel
[pierogi] / protocols / rc5protocol.cpp
1 #include "rc5protocol.h"
2
3 #include "pirrx51hardware.h"
4
5 #include "pirexception.h"
6
7 #include <QMutex>
8 extern bool commandInFlight;
9 extern QMutex commandIFMutex;
10
11 RC5Protocol::RC5Protocol(
12   QObject *guiObject,
13   unsigned int index,
14   unsigned int sevenBitControl)
15   : PIRProtocol(guiObject, index, 114000, true),
16     biphaseUnit(889),
17     buffer(0),
18     keypressCount(0)
19 {
20   setCarrierFrequency(36000);
21   setPreData(sevenBitControl, 7);
22 }
23
24
25 RC5Protocol::RC5Protocol(
26   QObject *guiObject,
27   unsigned int index)
28   : PIRProtocol(guiObject, index, 114000, true),
29     biphaseUnit(889),
30     buffer(0),
31     keypressCount(0)
32 {
33   setCarrierFrequency(36000);
34 }
35
36
37 void RC5Protocol::startSendingCommand(
38   unsigned int threadableID,
39   PIRKeyName command)
40 {
41   // Dumping entire method inside of try/catch, to deal with cross-thread
42   // exception handling:
43   try
44   {
45     // Check if this command is meant for us:
46     if (threadableID != id) return;
47
48     clearRepeatFlag();
49
50     KeycodeCollection::const_iterator i = keycodes.find(command);
51
52     // Sanity check, make sure command exists first:
53     if (i == keycodes.end())
54     {
55       QMutexLocker cifLocker(&commandIFMutex);
56       commandInFlight = false;
57       return;
58 //      std::string s = "Tried to send a non-existent command.\n";
59 //      throw PIRException(s);
60     }
61
62     // Construct the object that communicates with the device driver:
63     PIRRX51Hardware rx51device(carrierFrequency, dutyCycle);
64
65     int repeatCount = 0;
66     int commandDuration = 0;
67     while (repeatCount < MAX_REPEAT_COUNT)
68     {
69       // Now, throw together an RC5 protocol command string.
70
71       if (!preData.empty())
72       {
73         // For standard RC5, the "pre-data" contains the control portion,
74         // and the key contains only the 6-bit command portion.
75
76         // First, construct the control portion:
77         commandDuration += pushControlBits(rx51device);
78
79         // Next, the key-command portion:
80         commandDuration += pushKeyCommandBits((*i).second, rx51device);
81       }
82       else
83       {
84         // For non-standard RC5, the entire 13 bits are stuffed into the
85         // key portion, as all of them can vary:
86         commandDuration += pushNonStandardRC5((*i).second, rx51device);
87       }
88
89       // Clear out the buffer, if necessary:
90       if (buffer)
91       {
92         rx51device.addSingle(buffer);
93         commandDuration += buffer;
94
95         // probably unnecessary cleanup of buffer:
96         buffer = 0;
97         bufferContainsSpace = false;
98         bufferContainsPulse = false;
99       }
100
101       // Now, tell the device to send the whole command:
102       rx51device.sendCommandToDevice();
103
104       // Sleep for an amount of time.  (Need to make this interruptable!)
105       sleepUntilRepeat(commandDuration);
106
107       // Have we been told to stop yet?
108       if (checkRepeatFlag())
109       {
110         break;
111 /*
112         // Ok, then we can quit now:
113         ++keypressCount;
114         QMutexLocker cifLocker(&commandIFMutex);
115         commandInFlight = false;
116         return;
117 */
118       }
119
120       ++repeatCount;
121     }
122
123     ++keypressCount;
124     QMutexLocker cifLocker(&commandIFMutex);
125     commandInFlight = false;
126   }
127   catch (PIRException e)
128   {
129     emit commandFailed(e.getError().c_str());
130   }
131 }
132
133
134 int RC5Protocol::pushControlBits(
135   PIRRX51Hardware &rx51device)
136 {
137   int duration = 0;
138
139   // Start off by pushing the lead pulse onto the buffer:
140   buffer = biphaseUnit;
141   bufferContainsPulse = true;
142   bufferContainsSpace = false;
143
144   CommandSequence::const_iterator i = preData.begin();
145
146   // Push the first bit:
147   if (i != preData.end())
148   {
149     duration += pushBit(*i, rx51device);
150     ++i;
151   }
152
153   // Toggle the second bit, if it is time to do so:
154   if (i != preData.end())
155   {
156     if (keypressCount % 2)
157     {
158       duration += pushBit(!(*i), rx51device);
159     }
160     else
161     {
162       duration += pushBit(*i, rx51device);
163     }
164
165     ++i;
166   }
167
168   // Simply push the rest of the bits:
169   while (i != preData.end())
170   {
171     duration += pushBit(*i, rx51device);
172     ++i;
173   }
174
175   return duration;
176 }
177
178
179 int RC5Protocol::pushKeyCommandBits(
180   const PIRKeyBits &pkb,
181   PIRRX51Hardware &rx51device)
182 {
183   int duration = 0;
184
185   // Just push all the bits:
186   CommandSequence::const_iterator i = pkb.firstCode.begin();
187   while (i != pkb.firstCode.end())
188   {
189     duration += pushBit(*i, rx51device);
190     ++i;
191   }
192
193   return duration;
194 }
195
196
197 int RC5Protocol::pushNonStandardRC5(
198   const PIRKeyBits &pkb,
199   PIRRX51Hardware &rx51device)
200 {
201   int duration = 0;
202
203   // Start off by pushing the lead pulse onto the buffer:
204   buffer = biphaseUnit;
205   bufferContainsPulse = true;
206   bufferContainsSpace = false;
207
208   CommandSequence::const_iterator i = pkb.firstCode.begin();
209
210   // Push the first bit:
211   if (i != pkb.firstCode.end())
212   {
213     duration += pushBit(*i, rx51device);
214     ++i;
215   }
216
217   // Toggle the second bit, if it is time to do so:
218   if (i != pkb.firstCode.end())
219   {
220     if (keypressCount % 2)
221     {
222       duration += pushBit(!(*i), rx51device);
223     }
224     else
225     {
226       duration += pushBit(*i, rx51device);
227     }
228
229     ++i;
230   }
231
232   // Simply push the rest of the bits:
233   while (i != pkb.firstCode.end())
234   {
235     duration += pushBit(*i, rx51device);
236     ++i;
237   }
238
239   return duration;
240 }
241
242
243 int RC5Protocol::pushBit(
244   bool bitValue,
245   PIRRX51Hardware &device)
246 {
247   unsigned int duration = 0;
248   // RC5 encodes a "0" by using a pulse followed by a space,
249   // and a "1" by using a space followed by a pulse.
250
251   if (bitValue)
252   {
253     // We've got a "1".  First add a space, then a pulse.
254     if (bufferContainsSpace)
255     {
256       // Merge our space with the previous space, and send them to
257       // the device.
258       device.addSingle(buffer + biphaseUnit);
259       duration += (buffer + biphaseUnit);
260       buffer = 0;
261       bufferContainsSpace = false;
262     }
263     else
264     {
265       if (bufferContainsPulse)
266       {
267         // Flush the buffer:
268         device.addSingle(buffer);
269         duration += buffer;
270         buffer = 0;
271         bufferContainsPulse = false;
272       }
273       // Add a space:
274       device.addSingle(biphaseUnit);
275       duration += biphaseUnit;
276     }
277
278     // Put a pulse into the buffer to wait.
279     buffer = biphaseUnit;
280     bufferContainsPulse = true;
281   }
282   else
283   {
284     // We've got a "0".  First add a pulse, then a space.
285     if (bufferContainsPulse)
286     {
287       // Merge our pulse with the previous one, and send them to the device:
288       device.addSingle(buffer + biphaseUnit);
289       duration += (buffer + biphaseUnit);
290       buffer = 0;
291       bufferContainsPulse = false;
292     }
293     else
294     {
295       if (bufferContainsSpace)
296       {
297         // Flush out the buffer:
298         device.addSingle(buffer);
299         duration += buffer;
300         buffer = 0;
301         bufferContainsSpace = false;
302       }
303
304       // Add a pulse:
305       device.addSingle(biphaseUnit);
306       duration += biphaseUnit;
307     }
308
309     // Put a space into the buffer to wait:
310     buffer = biphaseUnit;
311     bufferContainsSpace = true;
312   }
313
314   return duration;
315 }