Improved to "half-way usable" (version 0.5)
[pierogi] / necprotocol.cpp
1 #include "necprotocol.h"
2
3 #include "pirexception.h"
4 #include <string>
5 //#include <iostream>
6
7 // Some global communications stuff:
8 #include <QMutex>
9 extern bool commandInFlight;
10 extern QMutex commandIFMutex;
11
12 // The official NEC protocol, as I understand it, has the following attributes:
13 // A "zero" is encoded with a 560 usec pulse, 560 usec space.
14 // A "one" is encoded with a 560 usec pulse, and 3*560 (1680) usec space.
15 // The header is a 9000 usec pulse, 4500 usec space.
16 // Commands end with a trailing 560 usec pulse.
17 // A repeat block is a 9000 usec pulse, 2250 usec space, then trailing pulse.
18 // Each command runs for 110000 usec before another can be executed.
19
20 // For standard NEC, use this constructor:
21 NECProtocol::NECProtocol(
22   QObject *guiObject,
23   unsigned int index,
24   NECKeyFormat fmt)
25   : PIRProtocol(guiObject, index, 110000, true),
26     zeroPulse(560),
27     zeroSpace(560),
28     onePulse(560),
29     oneSpace(1680),
30     headerPulse(9000),
31     headerSpace(4500),
32     hasHeaderPair(true),
33     trailerPulse(560),
34     hasTrailerPulse(true),
35     repeatPulse(9000),
36     repeatSpace(2250),
37     hasRepeatPair(true),
38     repeatNeedsHeader(false),
39     fullHeadlessRepeat(false),
40     elevenBitToggle(false),
41     encodingFormat(fmt)
42 {
43 }
44
45 // For non-standard NEC, use this constructor:
46 NECProtocol::NECProtocol(
47   QObject *guiObject,
48   unsigned int index,
49   unsigned int zPulse,
50   unsigned int zSpace,
51   unsigned int oPulse,
52   unsigned int oSpace,
53   unsigned int gSpace,
54   bool iclflag,
55   NECKeyFormat fmt)
56   : PIRProtocol(guiObject, index, gSpace, iclflag),
57     zeroPulse(zPulse),
58     zeroSpace(zSpace),
59     onePulse(oPulse),
60     oneSpace(oSpace),
61     hasHeaderPair(false),
62     hasTrailerPulse(false),
63     hasRepeatPair(false),
64     repeatNeedsHeader(false),
65     fullHeadlessRepeat(false),
66     elevenBitToggle(false),
67     encodingFormat(fmt)
68 {
69 }
70
71 void NECProtocol::setHeaderPair(
72   unsigned int pulse,
73   unsigned int space)
74 {
75   headerPulse = pulse;
76   headerSpace = space;
77   hasHeaderPair = true;
78 }
79
80 void NECProtocol::setTrailerPulse(
81   unsigned int pulse)
82 {
83   trailerPulse = pulse;
84   hasTrailerPulse = true;
85 }
86
87 void NECProtocol::setRepeatPair(
88   unsigned int pulse,
89   unsigned int space)
90 {
91   repeatPulse = pulse;
92   repeatSpace = space;
93   hasRepeatPair = true;
94 }
95
96 void NECProtocol::setRepeatNeedsHeader(
97   bool flag)
98 {
99   repeatNeedsHeader = flag;
100 }
101
102 void NECProtocol::setFullHeadlessRepeat(
103   bool flag)
104 {
105   fullHeadlessRepeat = flag;
106 }
107
108 void NECProtocol::setElevenBitToggle(
109   bool flag)
110 {
111   elevenBitToggle = flag;
112 }
113
114 void NECProtocol::startSendingCommand(
115   unsigned int threadableID,
116   PIRKeyName command)
117 {
118   // Exceptions here are problematic; I'll try to weed them out by putting the
119   // whole thing in a try/catch block:
120   try
121   {
122     // First, check if we are meant to be the recipient of this command:
123     if (threadableID != id) return;
124
125     // An object that helps keep track of the number of commands:
126 //    PIRCommandCounter commandCounter;
127
128     // Ok, we're going to lock down this method and make sure
129     // only one guy at a time passes this point:
130 //    QMutexLocker commandLocker(&commandMutex);
131
132     clearRepeatFlag();
133
134     KeycodeCollection::const_iterator i = keycodes.find(command);
135
136     // Do we even have this key defined?
137     if (i == keycodes.end())
138     {
139       std::string s = "Tried to send a non-existent command.\n";
140       throw PIRException(s);
141     }
142
143     // construct the device:
144     PIRRX51Hardware rx51device(carrierFrequency, dutyCycle);
145
146     int repeatCount = 0;
147     int commandDuration = 0;
148     while (repeatCount < MAX_REPEAT_COUNT)
149     {
150       // If we are currently repeating, and have a special "repeat signal",
151       // use that signal.  Otherwise, generate a normal command string.
152       if (hasRepeatPair && repeatCount)
153       {
154         commandDuration = generateRepeatCommand(rx51device);
155       }
156       else if (fullHeadlessRepeat && repeatCount)
157       {
158         commandDuration = generateHeadlessCommand((*i).second, rx51device);
159       }
160       else if (elevenBitToggle && (repeatCount % 2))
161       {
162         commandDuration = generateToggledCommand((*i).second, rx51device);
163       }
164       else
165       {
166         commandDuration = generateStandardCommand((*i).second, rx51device);
167       }
168
169       // Now, tell the device to send the whole command:
170       rx51device.sendCommandToDevice();
171
172       // sleep until the next repetition of command:
173       sleepUntilRepeat(commandDuration);
174
175       // Check whether we've reached the minimum required number of repetitons:
176       if (repeatCount >= minimumRepetitions)
177       {
178         // Check whether we've been asked to stop:
179         if (checkRepeatFlag())
180         {
181           QMutexLocker cifLocker(&commandIFMutex);
182           commandInFlight = false;
183           return;
184         }
185       }
186
187       ++repeatCount;
188     }
189   }
190   catch (PIRException e)
191   {
192     // inform the gui:
193     emit commandFailed(e.getError().c_str());
194   }
195
196   QMutexLocker cifLocker(&commandIFMutex);
197   commandInFlight = false;
198 }
199
200
201 int NECProtocol::generateStandardCommand(
202   const CommandSequence &bits,
203   PIRRX51Hardware &rx51device)
204 {
205   int duration = 0;
206
207   // First, the "header" pulse (if any):
208   if (hasHeaderPair)
209   {
210     rx51device.addPair(headerPulse, headerSpace);
211     duration += (headerPulse + headerSpace);
212   }
213
214   // Now, check the encoding format:
215   switch(encodingFormat)
216   {
217   case Standard_NEC:
218     // Standard NEC is made up of an eight-bit "address" and an eight-bit
219     // "command".  First the address bits are sent (in reverse order), then
220     // the address bits are inverted and sent again (in reverse order).
221     // Next, we do the same to the command bits.
222     // - "preData" should contain 8-bit value
223     // - "bits" should contain 8-bit value
224     duration += pushReverseBits(preData, rx51device);
225     duration += pushInvertedReverseBits(preData, rx51device);
226     duration += pushReverseBits(bits, rx51device);
227     duration += pushInvertedReverseBits(bits, rx51device);
228     break;
229   case Extended_NEC:
230     // In extended NEC, the address has been extended to 16 bits, but only
231     // the reversed bits are sent, not inverted.  The command portion stays
232     // the same.
233     // - "preData" should contain 16-bit value
234     // - "bits" should contain 8-bit value
235     duration += pushReverseBits(preData, rx51device);
236     duration += pushReverseBits(bits, rx51device);
237     duration += pushInvertedReverseBits(bits, rx51device);
238     break;
239   case LIRC_NEC: default:
240     // In this case, we just dump the raw bits into the device:
241     duration += pushBits(preData, rx51device);
242     duration += pushBits(bits, rx51device);
243     duration += pushBits(postData, rx51device);
244     break;
245   }
246
247   // Finally add the "trail":
248   if (hasTrailerPulse)
249   {
250     rx51device.addSingle(trailerPulse);
251     duration += trailerPulse;
252   }
253
254   return duration;
255 }
256
257
258 int NECProtocol::generateHeadlessCommand(
259   const CommandSequence &bits,
260   PIRRX51Hardware &rx51device)
261 {
262   int duration = 0;
263
264   // First, the "pre" data:
265   duration += pushBits(preData, rx51device);
266
267   // Next, add the actual command:
268   duration += pushBits(bits, rx51device);
269
270   // Next, add the "post" data:
271   duration += pushBits(postData, rx51device);
272
273   // Finally add the "trail":
274   if (hasTrailerPulse)
275   {
276     rx51device.addSingle(trailerPulse);
277     duration += trailerPulse;
278   }
279
280   return duration;
281 }
282
283
284 int NECProtocol::generateRepeatCommand(
285   PIRRX51Hardware &rx51device)
286 {
287   int duration = 0;
288
289   // Do we need the header?
290   if (repeatNeedsHeader)
291   {
292     // Do we even have a header?
293     if (hasHeaderPair)
294     {
295       // Ok, then add the header to the repeat:
296       rx51device.addPair(headerPulse, headerSpace);
297       duration += (headerPulse + headerSpace);
298     }
299   }
300
301   // Add the repeat pulse:
302   rx51device.addPair(repeatPulse, repeatSpace);
303   duration += (repeatPulse + repeatSpace);
304
305   // Finally add the trailer:
306   if (hasTrailerPulse)
307   {
308     rx51device.addSingle(trailerPulse);
309     duration += trailerPulse;
310   }
311
312   return duration;
313 }
314
315
316 // NOTE!  The following is a special command to toggle the last eleven bits
317 // of the fifteen-bit commands used by Denon, Sharp, and a few others.  It
318 // assumes the command sequence will contain all fifteen bits.  If this
319 // is not the case, it will work incorrectly!
320 int NECProtocol::generateToggledCommand(
321   const CommandSequence &bits,
322   PIRRX51Hardware &rx51device)
323 {
324   int duration = 0;
325
326   CommandSequence::const_iterator i = bits.begin();
327
328   int bitcount = 0;
329   // First 4 bits:
330   while ((bitcount < 4) && (i != bits.end()))
331   {
332     if (*i)
333     {
334       // Send pulse for "one":
335       rx51device.addPair(onePulse, oneSpace);
336       duration += (onePulse + oneSpace);
337     }
338     else
339     {
340       // Send pulse for "zero":
341       rx51device.addPair(zeroPulse, zeroSpace);
342       duration += (zeroPulse + zeroSpace);
343     }
344     ++i;
345     ++bitcount;
346   }
347
348   // Now, invert the last eleven bits:
349   while (i != bits.end())
350   {
351     if (*i)
352     {
353       // Send pulse for "zero":
354       rx51device.addPair(zeroPulse, zeroSpace);
355       duration += (zeroPulse + zeroSpace);
356     }
357     else
358     {
359       // Send pulse for "one":
360       rx51device.addPair(onePulse, oneSpace);
361       duration += (onePulse + oneSpace);
362     }
363     ++i;
364   }
365
366   // Add trail on end:
367   if (hasTrailerPulse)
368   {
369     rx51device.addSingle(trailerPulse);
370     duration += trailerPulse;
371   }
372
373   return duration;
374 }
375
376
377 int NECProtocol::pushBits(
378   const CommandSequence &bits,
379   PIRRX51Hardware &rx51device)
380 {
381   int duration = 0;
382   CommandSequence::const_iterator i = bits.begin();
383   while (i != bits.end())
384   {
385     if (*i)
386     {
387       // Send the pulse for "One":
388       rx51device.addPair(onePulse, oneSpace);
389       duration += (onePulse + oneSpace);
390     }
391     else
392     {
393       // Send the pulse for "Zero":
394       rx51device.addPair(zeroPulse, zeroSpace);
395       duration += (zeroPulse + zeroSpace);
396     }
397     ++i;
398   }
399
400   return duration;
401 }
402
403
404 int NECProtocol::pushReverseBits(
405   const CommandSequence &bits,
406   PIRRX51Hardware &rx51device)
407 {
408   int duration = 0;
409   CommandSequence::const_reverse_iterator i = bits.rbegin();
410   while (i != bits.rend())
411   {
412     if (*i)
413     {
414       // Send the pulse for "One":
415       rx51device.addPair(onePulse, oneSpace);
416       duration += (onePulse + oneSpace);
417     }
418     else
419     {
420       // Send the pulse for "Zero":
421       rx51device.addPair(zeroPulse, zeroSpace);
422       duration += (zeroPulse + zeroSpace);
423     }
424     ++i;
425   }
426
427   return duration;
428 }
429
430
431 int NECProtocol::pushInvertedReverseBits(
432   const CommandSequence &bits,
433   PIRRX51Hardware &rx51device)
434 {
435   int duration = 0;
436   CommandSequence::const_reverse_iterator i = bits.rbegin();
437   while (i != bits.rend())
438   {
439     if (*i)
440     {
441       // Send the pulse for "Zero":
442       rx51device.addPair(zeroPulse, zeroSpace);
443       duration += (zeroPulse + zeroSpace);
444     }
445     else
446     {
447       // Send the pulse for "One":
448       rx51device.addPair(onePulse, oneSpace);
449       duration += (onePulse + oneSpace);
450     }
451     ++i;
452   }
453
454   return duration;
455 }