Advanced Settings Panel
[pierogi] / protocols / mceprotocol.cpp
1 #include "mceprotocol.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 // These defines might need to be turned into variables, for odd devices.
12 #define HEADER_PULSE 2666
13 #define HEADER_SPACE 888
14 #define TRAILER_BIPHASE 888
15
16 // Ok, what I've got on MCE protocol is this:
17 // It is based on RC6 mode 6A, with a few odd tweaks.
18 // The biphase unit of time is 444 usec.
19 // The RC6 header block starts with the normal 2666 usec pulse, 888 usec space.
20 // The next bit is fixed as a "1", as usual.
21 // The next three bits are 110, marking this as a mode 6 protocol.
22 // The trailer bit has an 888 usec biphase, is set to 0, and is _not_ toggled!
23 // The next 16 bits I'm not sure about.  Hopefully they will be a fixed value
24 // for any given device...
25 // Then, the next bit is the new toggle bit.
26 // Following this, there are seven "device address" bits, and then eight
27 // "command" bits.
28 // A space of (at least) 2666 usec must follow any command.
29 // The carrier frequency is 36 kHZ, duty cycle between 25 and 50 %.
30
31 MCEProtocol::MCEProtocol(
32   QObject *guiObject,
33   unsigned int index,
34   unsigned int oemData)
35   : PIRProtocol(guiObject, index, 2666, false),
36     biphaseUnit(444),
37     buffer(0),
38     keypressCount(0)
39 {
40   setCarrierFrequency(36000);
41   appendToBitSeq(oemBits, oemData, 16);
42 }
43
44
45 void MCEProtocol::startSendingCommand(
46   unsigned int threadableID,
47   PIRKeyName command)
48 {
49   try
50   {
51     // Is this command meant for us?
52     if (threadableID != id) return;
53
54     clearRepeatFlag();
55
56     KeycodeCollection::const_iterator i = keycodes.find(command);
57
58     // Sanity check:
59     if (i == keycodes.end())
60     {
61       QMutexLocker cifLocker(&commandIFMutex);
62       commandInFlight = false;
63       return;
64 //      std::string s = "Tried to send a non-existent command.\n";
65 //      throw PIRException(s);
66     }
67
68     PIRRX51Hardware rx51device(carrierFrequency, dutyCycle);
69
70     int repeatCount = 0;
71     int duration = 0;
72     while (repeatCount < MAX_REPEAT_COUNT)
73     {
74       bufferContainsSpace = false;
75       bufferContainsPulse = false;
76       // First, construct the "Header" segment of the pulse train.
77       //
78       // The header involves:
79       // a) a "lead" of 2666 us pulse, 888 us space;
80       // b) a "start bit", value 1 (so 444 us pulse, 444 us space)
81       // c) three control bits, set to "110" (i.e., mode "6")
82       // d) the double-sized "trailer" bit, set to 0.
83
84       rx51device.addSingle(HEADER_PULSE); // lead pulse
85       duration += HEADER_PULSE;
86       rx51device.addSingle(HEADER_SPACE); // lead space
87       duration += HEADER_SPACE;
88       rx51device.addSingle(biphaseUnit); // start bit pulse
89       duration += biphaseUnit;
90       rx51device.addSingle(biphaseUnit); // start bit space
91       duration += biphaseUnit;
92       rx51device.addSingle(biphaseUnit); // bit 1 pulse;
93       duration += biphaseUnit;
94       rx51device.addSingle(biphaseUnit); // bit 1 space;
95       duration += biphaseUnit;
96       rx51device.addSingle(biphaseUnit); // bit 2 pulse;
97       duration += biphaseUnit;
98       rx51device.addSingle(2 * biphaseUnit); // bit 2 space + bit 3 space;
99       duration += 2 * biphaseUnit;
100       rx51device.addSingle(biphaseUnit); // bit 3 pulse;
101       duration += biphaseUnit;
102       rx51device.addSingle(2 * biphaseUnit); // trailer space
103       duration += 2 * biphaseUnit;
104       buffer = 2 * biphaseUnit; // trailer pulse goes into the buffer
105       bufferContainsPulse = true;
106
107       // Now, we can start the normal buffering process:
108
109       // push the "OEM" data:
110       duration += pushBits(oemBits, rx51device);
111
112       // The next bit is the MCE toggle bit:
113       if (keypressCount % 2)
114       {
115         pushOne(rx51device);
116       }
117       else
118       {
119         pushZero(rx51device);
120       }
121
122       // push the device address data:
123       duration += pushBits(preData, rx51device);
124
125       // push the command data:
126       duration += pushBits((*i).second.firstCode, rx51device);
127
128       // Flush out the buffer, if necessary:
129       if (buffer)
130       {
131         rx51device.addSingle(buffer);
132         duration += buffer;
133         buffer = 0;
134       }
135
136       // Actually send out the command:
137       rx51device.sendCommandToDevice();
138
139       // Sleep for an amount of time.  (RC6 demands an addtional 6 unit space
140       // at the end of any command...)
141       sleepUntilRepeat(duration + 6 * biphaseUnit);
142
143       // Have we been told to stop yet?
144       if (checkRepeatFlag())
145       {
146         // Yes, we can now quit repeating:
147         break;
148 /*
149         ++keypressCount;
150         QMutexLocker ciflocker(&commandIFMutex);
151         commandInFlight = false;
152         return;
153 */
154       }
155     }
156
157     ++keypressCount;
158     QMutexLocker cifLocker(&commandIFMutex);
159     commandInFlight = false;
160   }
161   catch (PIRException e)
162   {
163     emit commandFailed(e.getError().c_str());
164   }
165 }
166
167
168 int MCEProtocol::pushBits(
169   const CommandSequence &bits,
170   PIRRX51Hardware &rx51device)
171 {
172   int duration = 0;
173
174   CommandSequence::const_iterator i = bits.begin();
175
176   while (i != bits.end())
177   {
178     if (*i)
179     {
180       duration += pushOne(rx51device);
181     }
182     else
183     {
184       duration += pushZero(rx51device);
185     }
186
187     ++i;
188   }
189
190   return duration;
191 }
192
193
194 // This should be part of a general RC6 parent maybe?
195 int MCEProtocol::pushZero(
196   PIRRX51Hardware &rx51device)
197 {
198   // Need to add a space, then a pulse.
199   int duration = 0;
200
201   if (bufferContainsSpace)
202   {
203     // Merge this space and the previous one, and send to device:
204     rx51device.addSingle(buffer + biphaseUnit);
205     duration += (buffer + biphaseUnit);
206     buffer = 0;
207      bufferContainsSpace = false;
208   }
209   else
210   {
211     if (bufferContainsPulse)
212     {
213       // Flush out the buffer:
214       rx51device.addSingle(buffer);
215       duration += buffer;
216       buffer = 0;
217       bufferContainsPulse = false;
218     }
219
220     // push a space onto the device:
221     rx51device.addSingle(biphaseUnit);
222     duration += biphaseUnit;
223   }
224
225   // Put a pulse into the buffer to wait:
226   buffer = biphaseUnit;
227   bufferContainsPulse = true;
228
229   return duration;
230 }
231
232
233 int MCEProtocol::pushOne(
234   PIRRX51Hardware &rx51device)
235 {
236   // Need to add a pulse, then a space.
237   int duration = 0;
238
239   // First, the pulse:
240   if (bufferContainsPulse)
241   {
242     rx51device.addSingle(buffer + biphaseUnit);
243     duration += (buffer + biphaseUnit);
244     buffer = 0;
245     bufferContainsPulse = false;
246   }
247   else
248   {
249     if (bufferContainsSpace)
250     {
251       // Flush the buffer:
252       rx51device.addSingle(buffer);
253       duration += buffer;
254       buffer = 0;
255       bufferContainsSpace = false;
256     }
257     // Now, add the pulse:
258     rx51device.addSingle(biphaseUnit);
259     duration += biphaseUnit;
260   }
261
262   // Next, push a space onto the buffer:
263   buffer = biphaseUnit;
264   bufferContainsSpace = true;
265
266   return duration;
267 }