Improved to "half-way usable" (version 0.5)
[pierogi] / rc6protocol.cpp
1 #include "rc6protocol.h"
2
3 #include "pirexception.h"
4
5 #include <QMutex>
6 extern bool commandInFlight;
7 extern QMutex commandIFMutex;
8
9 // These defines might need to be turned into variables, for odd devices.
10 #define HEADER_PULSE 2666
11 #define HEADER_SPACE 889
12 #define TRAILER_PULSE 889
13 #define TRAILER_SPACE 889
14
15 // I'm requiring standard RC6 initialization to include the 8-bit control
16 // section:
17 RC6Protocol::RC6Protocol(
18   QObject *guiObject,
19   unsigned int index,
20   unsigned int eightBitControl)
21   : PIRProtocol(guiObject, index, 108000, true),
22     biphaseUnit(444),
23     buffer(0),
24     keypressCount(0)
25 {
26   setCarrierFrequency(36000);
27   setPreData(eightBitControl, 8);
28 }
29
30
31 void RC6Protocol::startSendingCommand(
32   unsigned int threadableID,
33   PIRKeyName command)
34 {
35   try
36   {
37     // Is this command meant for us?
38     if (threadableID != id) return;
39
40     clearRepeatFlag();
41
42     KeycodeCollection::const_iterator i = keycodes.find(command);
43
44     // Sanity check:
45     if (i == keycodes.end())
46     {
47       std::string s = "Tried to send a non-existent command.\n";
48       throw PIRException(s);
49     }
50
51     PIRRX51Hardware rx51device(carrierFrequency, dutyCycle);
52
53     int repeatCount = 0;
54     int duration = 0;
55     while (repeatCount < MAX_REPEAT_COUNT)
56     {
57       // First, construct the "Header" segment of the pulse train.  For now,
58       // I'm only supporting the "consumer electronics mode" of RC6; this code
59       // must be changed if we want to support more than that!
60       //
61       // The header involves:
62       // a) a "lead" of 2666 us pulse, 889 us space;
63       // b) a "start bit", value 1 (so 444 us pulse, 444 us space)
64       // c) three control bits, always set to 0 (so 444 us space,
65       //    444 us pulse each)
66       // d) the double-sized "trailer" bit, toggled on each keypress (so
67       //    either 889 pulse 889 space, or 889 space 889 pulse)
68
69       rx51device.addSingle(HEADER_PULSE); // lead pulse
70       duration += HEADER_PULSE;
71       rx51device.addSingle(HEADER_SPACE); // lead space
72       duration += HEADER_SPACE;
73       rx51device.addSingle(biphaseUnit); // start bit pulse
74       duration += biphaseUnit;
75
76       // start bit space + control bit 1 space:
77       rx51device.addSingle(2 * biphaseUnit);
78       duration += 2 * biphaseUnit;
79       rx51device.addSingle(biphaseUnit); // bit 1 pulse;
80       duration += biphaseUnit;
81       rx51device.addSingle(biphaseUnit); // bit 2 space;
82       duration += biphaseUnit;
83       rx51device.addSingle(biphaseUnit); // bit 2 pulse;
84       duration += biphaseUnit;
85       rx51device.addSingle(biphaseUnit); // bit 3 space;
86       duration += biphaseUnit;
87
88       // Next, need to check whether we should toggle or not:
89       if (keypressCount % 2)
90       {
91         // bit 3 pulse plus long trailer bit pulse:
92         rx51device.addSingle(3 * biphaseUnit);
93         duration += 3 * biphaseUnit;
94
95         // load the trailer bit space onto the buffer:
96         buffer = 2 * biphaseUnit;
97         bufferContainsSpace = true;
98         bufferContainsPulse = false;
99       }
100       else
101       {
102         rx51device.addSingle(biphaseUnit); // bit three pulse
103         duration += biphaseUnit;
104         rx51device.addSingle(2 * biphaseUnit); // trailer bit space
105         duration += 2 * biphaseUnit;
106
107         // load the trailer bit pulse onto the buffer:
108         buffer = 2 * biphaseUnit;
109         bufferContainsPulse = true;
110         bufferContainsSpace = false;
111       }
112
113       // Now, we can start the normal buffering process:
114
115       // push any pre-data onto the device:
116       duration += pushBits(preData, rx51device);
117
118       // push the actual command:
119       duration += pushBits((*i).second, rx51device);
120
121       // Flush out the buffer, if necessary:
122       if (buffer)
123       {
124         rx51device.addSingle(buffer);
125         duration += buffer;
126         buffer = 0;
127       }
128
129       // Actually send out the command:
130       rx51device.sendCommandToDevice();
131
132       // Sleep for an amount of time.  (RC6 demands an addtional 6 unit space
133       // at the end of any command...)
134       sleepUntilRepeat(duration + 6 * biphaseUnit);
135
136       // Have we been told to stop yet?
137       if (checkRepeatFlag())
138       {
139         // Yes, we can now quit repeating:
140         ++keypressCount;
141         QMutexLocker ciflocker(&commandIFMutex);
142         commandInFlight = false;
143         return;
144       }
145     }
146   }
147   catch (PIRException e)
148   {
149     emit commandFailed(e.getError().c_str());
150   }
151
152   ++keypressCount;
153   QMutexLocker cifLocker(&commandIFMutex);
154   commandInFlight = false;
155 }
156
157
158 int RC6Protocol::pushBits(
159   const CommandSequence &bits,
160   PIRRX51Hardware &rx51device)
161 {
162   int bitsDuration = 0;
163
164   CommandSequence::const_iterator i = bits.begin();
165   bool bitValue;
166
167   while (i != bits.end())
168   {
169     bitValue = *i;
170
171     // In RC6, a "0" is represented by a space followed by a pulse,
172     // and a "1" is represented by a pulse followed by a space.
173     if (bitValue)
174     {
175       // This is a 1, so add a pulse, then a space.
176       // First, the pulse:
177       if (bufferContainsPulse)
178       {
179         rx51device.addSingle(buffer + biphaseUnit);
180         bitsDuration += (buffer + biphaseUnit);
181         buffer = 0;
182         bufferContainsPulse = false;
183       }
184       else
185       {
186         if (bufferContainsSpace)
187         {
188           // Flush the buffer:
189           rx51device.addSingle(buffer);
190           bitsDuration += buffer;
191           buffer = 0;
192           bufferContainsSpace = false;
193         }
194         // Now, add the pulse:
195         rx51device.addSingle(biphaseUnit);
196         bitsDuration += biphaseUnit;
197       }
198
199       // Next, push a space onto the buffer:
200       buffer = biphaseUnit;
201       bufferContainsSpace = true;
202     }
203     else
204     {
205       // This is a 0, so add a space, then a pulse.
206       if (bufferContainsSpace)
207       {
208         // Merge this space and the previous one, and send to device:
209         rx51device.addSingle(buffer + biphaseUnit);
210         bitsDuration += (buffer + biphaseUnit);
211         buffer = 0;
212         bufferContainsSpace = false;
213       }
214       else
215       {
216         if (bufferContainsPulse)
217         {
218           // Flush out the buffer:
219           rx51device.addSingle(buffer);
220           bitsDuration += buffer;
221           buffer = 0;
222           bufferContainsPulse = false;
223         }
224
225         // push a space onto the device:
226         rx51device.addSingle(biphaseUnit);
227         bitsDuration += biphaseUnit;
228       }
229
230       // Put a pulse into the buffer to wait:
231       buffer = biphaseUnit;
232       bufferContainsPulse = true;
233     }
234
235     ++i;
236   }
237
238   return bitsDuration;
239 }