Improved to "half-way usable" (version 0.5)
[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     int commandDuration = 0;
62     while (repeatCount < MAX_REPEAT_COUNT)
63     {
64       // Now, throw together an RC5 protocol command string.
65
66       if (!preData.empty())
67       {
68         // For standard RC5, the "pre-data" contains the control portion,
69         // and the key contains only the 6-bit command portion.
70
71         // First, construct the control portion:
72         commandDuration += pushControlBits(rx51device);
73
74         // Next, the key-command portion:
75         commandDuration += pushKeyCommandBits((*i).second, rx51device);
76       }
77       else
78       {
79         // For non-standard RC5, the entire 13 bits are stuffed into the
80         // key portion, as all of them can vary:
81         commandDuration += pushNonStandardRC5((*i).second, rx51device);
82       }
83
84       // Clear out the buffer, if necessary:
85       if (buffer)
86       {
87         rx51device.addSingle(buffer);
88         commandDuration += buffer;
89
90         // probably unnecessary cleanup of buffer:
91         buffer = 0;
92         bufferContainsSpace = false;
93         bufferContainsPulse = false;
94       }
95
96       // Now, tell the device to send the whole command:
97       rx51device.sendCommandToDevice();
98
99       // Sleep for an amount of time.  (Need to make this interruptable!)
100       sleepUntilRepeat(commandDuration);
101
102       // Have we been told to stop yet?
103       if (checkRepeatFlag())
104       {
105         // Ok, then we can quit now:
106         ++keypressCount;
107         QMutexLocker cifLocker(&commandIFMutex);
108         commandInFlight = false;
109         return;
110       }
111
112       ++repeatCount;
113     }
114   }
115   catch (PIRException e)
116   {
117     emit commandFailed(e.getError().c_str());
118   }
119
120   ++keypressCount;
121   QMutexLocker cifLocker(&commandIFMutex);
122   commandInFlight = false;
123 }
124
125
126 int RC5Protocol::pushControlBits(
127   PIRRX51Hardware &rx51device)
128 {
129   int duration = 0;
130
131   // Start off by pushing the lead pulse onto the buffer:
132   buffer = biphaseUnit;
133   bufferContainsPulse = true;
134   bufferContainsSpace = false;
135
136   CommandSequence::const_iterator i = preData.begin();
137
138   // Push the first bit:
139   if (i != preData.end())
140   {
141     duration += pushBit(*i, rx51device);
142     ++i;
143   }
144
145   // Toggle the second bit, if it is time to do so:
146   if (i != preData.end())
147   {
148     if (keypressCount % 2)
149     {
150       duration += pushBit(!(*i), rx51device);
151     }
152     else
153     {
154       duration += pushBit(*i, rx51device);
155     }
156
157     ++i;
158   }
159
160   // Simply push the rest of the bits:
161   while (i != preData.end())
162   {
163     pushBit(*i, rx51device);
164     ++i;
165   }
166
167   return duration;
168 }
169
170
171 int RC5Protocol::pushKeyCommandBits(
172   const CommandSequence &bits,
173   PIRRX51Hardware &rx51device)
174 {
175   int duration = 0;
176
177   // Just push all the bits:
178   CommandSequence::const_iterator i = bits.begin();
179   while (i != bits.end())
180   {
181     duration += pushBit(*i, rx51device);
182     ++i;
183   }
184
185   return duration;
186 }
187
188
189 int RC5Protocol::pushNonStandardRC5(
190   const CommandSequence &bits,
191   PIRRX51Hardware &rx51device)
192 {
193   int duration = 0;
194
195   // Start off by pushing the lead pulse onto the buffer:
196   buffer = biphaseUnit;
197   bufferContainsPulse = true;
198   bufferContainsSpace = false;
199
200   CommandSequence::const_iterator i = bits.begin();
201
202   // Push the first bit:
203   if (i != bits.end())
204   {
205     duration += pushBit(*i, rx51device);
206     ++i;
207   }
208
209   // Toggle the second bit, if it is time to do so:
210   if (i != bits.end())
211   {
212     if (keypressCount % 2)
213     {
214       duration += pushBit(!(*i), rx51device);
215     }
216     else
217     {
218       duration += pushBit(*i, rx51device);
219     }
220
221     ++i;
222   }
223
224   // Simply push the rest of the bits:
225   while (i != bits.end())
226   {
227     pushBit(*i, rx51device);
228     ++i;
229   }
230
231   return duration;
232 }
233
234
235 int RC5Protocol::pushBit(
236   bool bitValue,
237   PIRRX51Hardware &device)
238 {
239   unsigned int duration = 0;
240   // RC5 encodes a "0" by using a pulse followed by a space,
241   // and a "1" by using a space followed by a pulse.
242
243   if (bitValue)
244   {
245     // We've got a "1".  First add a space, then a pulse.
246     if (bufferContainsSpace)
247     {
248       // Merge our space with the previous space, and send them to
249       // the device.
250       device.addSingle(buffer + biphaseUnit);
251       duration += (buffer + biphaseUnit);
252       buffer = 0;
253       bufferContainsSpace = false;
254     }
255     else
256     {
257       if (bufferContainsPulse)
258       {
259         // Flush the buffer:
260         device.addSingle(buffer);
261         duration += buffer;
262         buffer = 0;
263         bufferContainsPulse = false;
264       }
265       // Add a space:
266       device.addSingle(biphaseUnit);
267       duration += biphaseUnit;
268     }
269
270     // Put a pulse into the buffer to wait.
271     buffer = biphaseUnit;
272     bufferContainsPulse = true;
273   }
274   else
275   {
276     // We've got a "0".  First add a pulse, then a space.
277     if (bufferContainsPulse)
278     {
279       // Merge our pulse with the previous one, and send them to the device:
280       device.addSingle(buffer + biphaseUnit);
281       duration += (buffer + biphaseUnit);
282       buffer = 0;
283       bufferContainsPulse = false;
284     }
285     else
286     {
287       if (bufferContainsSpace)
288       {
289         // Flush out the buffer:
290         device.addSingle(buffer);
291         duration += buffer;
292         buffer = 0;
293         bufferContainsSpace = false;
294       }
295
296       // Add a pulse:
297       device.addSingle(biphaseUnit);
298       duration += biphaseUnit;
299     }
300
301     // Put a space into the buffer to wait:
302     buffer = biphaseUnit;
303     bufferContainsSpace = true;
304   }
305
306   return duration;
307 }