First attempt to implement RC6 Protocol
[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     while (repeatCount < MAX_REPEAT_COUNT)
55     {
56       int duration = 0;
57
58       // First, construct the "Header" segment of the pulse train.  For now,
59       // I'm only supporting the "consumer electronics mode" of RC6; this code
60       // must be changed if we want to support more than that!
61       //
62       // The header involves:
63       // a) a "lead" of 2666 us pulse, 889 us space;
64       // b) a "start bit", value 1 (so 444 us pulse, 444 us space)
65       // c) three control bits, always set to 0 (so 444 us space,
66       //    444 us pulse each)
67       // d) the double-sized "trailer" bit, toggled on each keypress (so
68       //    either 889 pulse 889 space, or 889 space 889 pulse)
69
70       rx51device.addSingle(HEADER_PULSE); // lead pulse
71       duration += HEADER_PULSE;
72       rx51device.addSingle(HEADER_SPACE); // lead space
73       duration += HEADER_SPACE;
74       rx51device.addSingle(biphaseUnit); // start bit pulse
75       duration += biphaseUnit;
76
77       // start bit space + control bit 1 space:
78       rx51device.addSingle(2 * biphaseUnit);
79       duration += 2 * biphaseUnit;
80       rx51device.addSingle(biphaseUnit); // bit 1 pulse;
81       duration += biphaseUnit;
82       rx51device.addSingle(biphaseUnit); // bit 2 space;
83       duration += biphaseUnit;
84       rx51device.addSingle(biphaseUnit); // bit 2 pulse;
85       duration += biphaseUnit;
86       rx51device.addSingle(biphaseUnit); // bit 3 space;
87       duration += biphaseUnit;
88
89       // Next, need to check whether we should toggle or not:
90       if (keypressCount % 2)
91       {
92         // bit 3 pulse plus long trailer bit pulse:
93         rx51device.addSingle(3 * biphaseUnit);
94         duration += 3 * biphaseUnit;
95
96         // load the trailer bit space onto the buffer:
97         buffer = 2 * biphaseUnit;
98         bufferContainsSpace = true;
99         bufferContainsPulse = false;
100       }
101       else
102       {
103         rx51device.addSingle(biphaseUnit); // bit three pulse
104         duration += biphaseUnit;
105         rx51device.addSingle(2 * biphaseUnit); // trailer bit space
106         duration += 2 * biphaseUnit;
107
108         // load the trailer bit pulse onto the buffer:
109         buffer = 2 * biphaseUnit;
110         bufferContainsPulse = true;
111         bufferContainsSpace = false;
112       }
113
114       // Now, we can start the normal buffering process:
115
116       // push any pre-data onto the device:
117       duration += pushBits(preData, rx51device);
118
119       // push the actual command:
120       duration += pushBits((*i).second, rx51device);
121
122       // Flush out the buffer, if necessary:
123       if (buffer)
124       {
125         rx51device.addSingle(buffer);
126         duration += buffer;
127         buffer = 0;
128       }
129
130       // Actually send out the command:
131       rx51device.sendCommandToDevice();
132
133       // Sleep for an amount of time.  (RC6 demands an addtional 6 unit space
134       // at the end of any command...)
135       sleepUntilRepeat(duration + 6 * biphaseUnit);
136
137       // Have we been told to stop yet?
138       if (checkRepeatFlag())
139       {
140         // Yes, we can now quit repeating:
141         ++keypressCount;
142         QMutexLocker ciflocker(&commandIFMutex);
143         commandInFlight = false;
144         return;
145       }
146     }
147   }
148   catch (PIRException e)
149   {
150     emit commandFailed(e.getError().c_str());
151   }
152
153   ++keypressCount;
154   QMutexLocker cifLocker(&commandIFMutex);
155   commandInFlight = false;
156 }
157
158
159 int RC6Protocol::pushBits(
160   const CommandSequence &bits,
161   PIRRX51Hardware &rx51device)
162 {
163   int bitsDuration = 0;
164
165   CommandSequence::const_iterator i = bits.begin();
166   bool bitValue;
167
168   while (i != bits.end())
169   {
170     bitValue = *i;
171
172     // In RC6, a "0" is represented by a space followed by a pulse,
173     // and a "1" is represented by a pulse followed by a space.
174     if (bitValue)
175     {
176       // This is a 1, so add a pulse, then a space.
177       // First, the pulse:
178       if (bufferContainsPulse)
179       {
180         rx51device.addSingle(buffer + biphaseUnit);
181         bitsDuration += (buffer + biphaseUnit);
182         buffer = 0;
183         bufferContainsPulse = false;
184       }
185       else
186       {
187         if (bufferContainsSpace)
188         {
189           // Flush the buffer:
190           rx51device.addSingle(buffer);
191           bitsDuration += buffer;
192           buffer = 0;
193           bufferContainsSpace = false;
194         }
195         // Now, add the pulse:
196         rx51device.addSingle(biphaseUnit);
197         bitsDuration += biphaseUnit;
198       }
199
200       // Next, push a space onto the buffer:
201       buffer = biphaseUnit;
202       bufferContainsSpace = true;
203     }
204     else
205     {
206       // This is a 0, so add a space, then a pulse.
207       if (bufferContainsSpace)
208       {
209         // Merge this space and the previous one, and send to device:
210         rx51device.addSingle(buffer + biphaseUnit);
211         bitsDuration += (buffer + biphaseUnit);
212         buffer = 0;
213         bufferContainsSpace = false;
214       }
215       else
216       {
217         if (bufferContainsPulse)
218         {
219           // Flush out the buffer:
220           rx51device.addSingle(buffer);
221           bitsDuration += buffer;
222           buffer = 0;
223           bufferContainsPulse = false;
224         }
225
226         // push a space onto the device:
227         rx51device.addSingle(biphaseUnit);
228         bitsDuration += biphaseUnit;
229       }
230
231       // Put a pulse into the buffer to wait:
232       buffer = biphaseUnit;
233       bufferContainsPulse = true;
234     }
235
236     ++i;
237   }
238
239   return bitsDuration;
240 }