Advanced Settings Panel
[pierogi] / protocols / kaseikyoprotocol.cpp
1 #include "kaseikyoprotocol.h"
2
3 #include "pirrx51hardware.h"
4
5 #include "pirexception.h"
6
7 // Some global communications stuff:
8 #include <QMutex>
9 extern bool commandInFlight;
10 extern QMutex commandIFMutex;
11
12 // Getting good data on the Kaseikyo protocol(s) is difficult.  The following
13 // is my current understanding:
14 // A "zero" is encoded with a 432 usec pulse, 432 usec space.
15 // A "one" is encoded with a 432 usec pulse, and 3*432 (1296) usec space.
16 // The header has a 3456 usec pulse, and a 1728 usec space.
17 // Commands end with a trailing 432 usec pulse.
18 // When repeating, the entire pulse train is re-sent.
19 // There is a 74736 usec gap between repeated commands.
20 // The carrier frequency is 37 kHz.
21
22 KaseikyoProtocol::KaseikyoProtocol(
23   QObject *guiObject,
24   unsigned int index)
25   : SpaceProtocol(
26       guiObject, index,
27       432, 432,
28       432, 1296,
29       3456, 1728,
30       432,
31       74736, false)
32 {
33   setCarrierFrequency(37000);
34 }
35
36
37 void KaseikyoProtocol::startSendingCommand(
38   unsigned int threadableID,
39   PIRKeyName command)
40 {
41   // Exceptions here are problematic; I'll try to weed them out by putting the
42   // whole thing in a try/catch block:
43   try
44   {
45     // First, check if we are meant to be the recipient of this command:
46     if (threadableID != id) return;
47
48     clearRepeatFlag();
49
50     KeycodeCollection::const_iterator i = keycodes.find(command);
51
52     // Do we even have this key defined?
53     if (i == keycodes.end())
54     {
55       QMutexLocker cifLocker(&commandIFMutex);
56       commandInFlight = false;
57       return;
58 //      std::string s = "Tried to send a non-existent command.\n";
59 //      throw PIRException(s);
60     }
61
62     // construct the device:
63     PIRRX51Hardware rx51device(carrierFrequency, dutyCycle);
64
65     int repeatCount = 0;
66     int commandDuration = 0;
67     while (repeatCount < MAX_REPEAT_COUNT)
68     {
69       commandDuration = generateStandardCommand((*i).second, rx51device);
70
71       // Now, tell the device to send the whole command:
72       rx51device.sendCommandToDevice();
73
74       // sleep until the next repetition of command:
75       sleepUntilRepeat(commandDuration);
76
77       // Check whether we've reached the minimum required number of repetitons:
78       if (repeatCount >= minimumRepetitions)
79       {
80         // Check whether we've been asked to stop:
81         if (checkRepeatFlag())
82         {
83           break;
84 /*
85           QMutexLocker cifLocker(&commandIFMutex);
86           commandInFlight = false;
87           return;
88 */
89         }
90       }
91
92       ++repeatCount;
93     }
94
95     QMutexLocker cifLocker(&commandIFMutex);
96     commandInFlight = false;
97   }
98   catch (PIRException e)
99   {
100     // inform the gui:
101     emit commandFailed(e.getError().c_str());
102   }
103 }
104
105
106 int KaseikyoProtocol::generateStandardCommand(
107   const PIRKeyBits &pkb,
108   PIRRX51Hardware &rx51device)
109 {
110   int duration = 0;
111
112   // First, the header pulse:
113   rx51device.addPair(headerPulse, headerSpace);
114   duration += (headerPulse + headerSpace);
115
116   // While I don't yet fully understand the contents of the protocol, the
117   // data is obviously split into a first half (supposedly containing
118   // manufacturer ID codes) and a second half (containing address and command
119   // data).  The first half is 16 bits long plus a 4 bit checksum, and the
120   // second half is 24 bits long plus a 4 bit checksum.  Of that second half,
121   // the first 12 bits are probably the address, and the last 12 bits are split
122   // into 8 bits of command followed by 4 bits that turn out to be the last
123   // four bits of the command xored with the middle 4 bits of the address.
124   // (At least for Panasonic.)  Very strange.
125   // For now, I'm going with this game plan:
126   // -- The "preData" will contain the 16 bits of manufacturer data as a
127   // single value.
128   // -- The "firstCode" will contain the 12 address bits.
129   // -- The "secondCode" will contain the 8 command bits.
130   // -- I'll generate the three checksums below.
131
132   CommandSequence checksum;
133
134   // The "manufacturer codes":
135   duration += pushReverseBits(preData, rx51device);
136
137   generateChecksum(preData, checksum);
138   duration += pushReverseBits(checksum, rx51device);
139
140   // The command portion:
141   // First, the address and command:
142   duration += pushReverseBits(pkb.firstCode, rx51device);
143   duration += pushReverseBits(pkb.secondCode, rx51device);
144
145   // Next, the odd little checksum:
146   CommandSequence littleChecksum;
147   generateLittleChecksum(pkb.firstCode, pkb.secondCode, littleChecksum);
148   duration += pushReverseBits(littleChecksum, rx51device);
149   
150   // Finally, the last checksum:
151   checksum.clear();
152   generateChecksum(pkb.firstCode, checksum);
153   generateChecksum(pkb.secondCode, checksum);
154   generateChecksum(littleChecksum, checksum);
155   duration += pushReverseBits(checksum, rx51device);
156
157   // Add the trailer pulse:
158   rx51device.addSingle(trailerPulse);
159   duration += trailerPulse;
160
161   return duration;
162 }
163
164
165 void KaseikyoProtocol::generateChecksum(
166   const CommandSequence &bits,
167   CommandSequence &checksum)
168 {
169   bool bit1;
170   bool bit2;
171   bool bit3;
172   bool bit4;
173
174   CommandSequence::const_iterator i = bits.begin();
175
176   // Set up the first four bits:
177   if (i == bits.end()) return; // this shouldn't happen, throw an error?
178   bit1 = *i;
179   ++i;
180
181   if (i == bits.end()) return;
182   bit2 = *i;
183   ++i;
184
185   if (i == bits.end()) return;
186   bit3 = *i;
187   ++i;
188
189   if (i == bits.end()) return;
190   bit4 = *i;
191   ++i;
192
193   while (i != bits.end())
194   {
195     bit1 = bit1 ^ *i;
196     ++i;  
197
198     if (i != bits.end())
199     {
200       bit2 = bit2 ^ *i;
201       ++i;
202
203       if (i != bits.end())
204       {
205         bit3 = bit3 ^ *i;
206         ++i;
207
208         if (i != bits.end())
209         {
210           bit4 = bit4 ^ *i;
211           ++i;
212         }
213       }
214     }
215   }
216
217   // Now, either insert these bits into the checksum, or xor them with the
218   // bits already in the checksum.
219   if (checksum.empty())
220   {
221     checksum.push_back(bit1);
222     checksum.push_back(bit2);
223     checksum.push_back(bit3);
224     checksum.push_back(bit4);
225   }
226   else
227   {
228     CommandSequence::iterator j = checksum.begin();
229     if (j != checksum.end())
230     {
231       *j = *j ^ bit1;
232       ++j;
233     }
234
235     if (j != checksum.end())
236     {
237       *j = *j ^ bit2;
238       ++j;
239     }
240
241     if (j != checksum.end())
242     {
243       *j = *j ^ bit3;
244       ++j;
245     }
246
247     if (j != checksum.end())
248     {
249       *j = *j ^ bit4;
250     }
251   }
252 }
253
254
255 void KaseikyoProtocol::generateLittleChecksum(
256   const CommandSequence &firstBits,
257   const CommandSequence &secondBits,
258   CommandSequence &littleChecksum)
259 {
260   CommandSequence::const_iterator i = firstBits.begin();
261   CommandSequence::const_iterator j = secondBits.begin();
262
263   // Advance both iterators by 4 bits:
264   int index = 0;
265   while ((index < 4) && (i != firstBits.end()) && (j != secondBits.end()))
266   {
267     ++i;
268     ++j;
269     ++index;
270   }
271
272   // Xor the next four bits and store them:
273   index = 0;
274   while ((index < 4) && (i != firstBits.end()) && (j != secondBits.end()))
275   {
276     littleChecksum.push_back(*i ^ *j);
277     ++i;
278     ++j;
279     ++index;
280   }
281 }