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