Keyset update
[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       std::string s = "Tried to send a non-existent command.\n";
56       throw PIRException(s);
57     }
58
59     // construct the device:
60     PIRRX51Hardware rx51device(carrierFrequency, dutyCycle);
61
62     int repeatCount = 0;
63     int commandDuration = 0;
64     while (repeatCount < MAX_REPEAT_COUNT)
65     {
66       commandDuration = generateStandardCommand((*i).second, rx51device);
67
68       // Now, tell the device to send the whole command:
69       rx51device.sendCommandToDevice();
70
71       // sleep until the next repetition of command:
72       sleepUntilRepeat(commandDuration);
73
74       // Check whether we've reached the minimum required number of repetitons:
75       if (repeatCount >= minimumRepetitions)
76       {
77         // Check whether we've been asked to stop:
78         if (checkRepeatFlag())
79         {
80           QMutexLocker cifLocker(&commandIFMutex);
81           commandInFlight = false;
82           return;
83         }
84       }
85
86       ++repeatCount;
87     }
88   }
89   catch (PIRException e)
90   {
91     // inform the gui:
92     emit commandFailed(e.getError().c_str());
93   }
94
95   QMutexLocker cifLocker(&commandIFMutex);
96   commandInFlight = false;
97 }
98
99
100 int KaseikyoProtocol::generateStandardCommand(
101   const PIRKeyBits &pkb,
102   PIRRX51Hardware &rx51device)
103 {
104   int duration = 0;
105
106   // First, the header pulse:
107   rx51device.addPair(headerPulse, headerSpace);
108   duration += (headerPulse + headerSpace);
109
110   // While I don't yet fully understand the contents of the protocol, the
111   // data is obviously split into a first half (supposedly containing
112   // manufacturer ID codes) and a second half (containing address and command
113   // data).  The first half is 16 bits long plus a 4 bit checksum, and the
114   // second half is 24 bits long plus a 4 bit checksum.  Of that second half,
115   // the first 12 bits are probably the address, and the last 12 bits are split
116   // into 8 bits of command followed by 4 bits that turn out to be the last
117   // four bits of the command xored with the middle 4 bits of the address.
118   // (At least for Panasonic.)  Very strange.
119   // For now, I'm going with this game plan:
120   // -- The "preData" will contain the 16 bits of manufacturer data as a
121   // single value.
122   // -- The "firstCode" will contain the 12 address bits.
123   // -- The "secondCode" will contain the 8 command bits.
124   // -- I'll generate the three checksums below.
125
126   CommandSequence checksum;
127
128   // The "manufacturer codes":
129   duration += pushReverseBits(preData, rx51device);
130
131   generateChecksum(preData, checksum);
132   duration += pushReverseBits(checksum, rx51device);
133
134   // The command portion:
135   // First, the address and command:
136   duration += pushReverseBits(pkb.firstCode, rx51device);
137   duration += pushReverseBits(pkb.secondCode, rx51device);
138
139   // Next, the odd little checksum:
140   CommandSequence littleChecksum;
141   generateLittleChecksum(pkb.firstCode, pkb.secondCode, littleChecksum);
142   duration += pushReverseBits(littleChecksum, rx51device);
143   
144   // Finally, the last checksum:
145   checksum.clear();
146   generateChecksum(pkb.firstCode, checksum);
147   generateChecksum(pkb.secondCode, checksum);
148   generateChecksum(littleChecksum, checksum);
149   duration += pushReverseBits(checksum, rx51device);
150
151   // Add the trailer pulse:
152   rx51device.addSingle(trailerPulse);
153   duration += trailerPulse;
154
155   return duration;
156 }
157
158
159 void KaseikyoProtocol::generateChecksum(
160   const CommandSequence &bits,
161   CommandSequence &checksum)
162 {
163   bool bit1;
164   bool bit2;
165   bool bit3;
166   bool bit4;
167
168   CommandSequence::const_iterator i = bits.begin();
169
170   // Set up the first four bits:
171   if (i == bits.end()) return; // this shouldn't happen, throw an error?
172   bit1 = *i;
173   ++i;
174
175   if (i == bits.end()) return;
176   bit2 = *i;
177   ++i;
178
179   if (i == bits.end()) return;
180   bit3 = *i;
181   ++i;
182
183   if (i == bits.end()) return;
184   bit4 = *i;
185   ++i;
186
187   while (i != bits.end())
188   {
189     bit1 = bit1 ^ *i;
190     ++i;  
191
192     if (i != bits.end())
193     {
194       bit2 = bit2 ^ *i;
195       ++i;
196
197       if (i != bits.end())
198       {
199         bit3 = bit3 ^ *i;
200         ++i;
201
202         if (i != bits.end())
203         {
204           bit4 = bit4 ^ *i;
205           ++i;
206         }
207       }
208     }
209   }
210
211   // Now, either insert these bits into the checksum, or xor them with the
212   // bits already in the checksum.
213   if (checksum.empty())
214   {
215     checksum.push_back(bit1);
216     checksum.push_back(bit2);
217     checksum.push_back(bit3);
218     checksum.push_back(bit4);
219   }
220   else
221   {
222     CommandSequence::iterator j = checksum.begin();
223     if (j != checksum.end())
224     {
225       *j = *j ^ bit1;
226       ++j;
227     }
228
229     if (j != checksum.end())
230     {
231       *j = *j ^ bit2;
232       ++j;
233     }
234
235     if (j != checksum.end())
236     {
237       *j = *j ^ bit3;
238       ++j;
239     }
240
241     if (j != checksum.end())
242     {
243       *j = *j ^ bit4;
244     }
245   }
246 }
247
248
249 void KaseikyoProtocol::generateLittleChecksum(
250   const CommandSequence &firstBits,
251   const CommandSequence &secondBits,
252   CommandSequence &littleChecksum)
253 {
254   CommandSequence::const_iterator i = firstBits.begin();
255   CommandSequence::const_iterator j = secondBits.begin();
256
257   // Advance both iterators by 4 bits:
258   int index = 0;
259   while ((index < 4) && (i != firstBits.end()) && (j != secondBits.end()))
260   {
261     ++i;
262     ++j;
263     ++index;
264   }
265
266   // Xor the next four bits and store them:
267   index = 0;
268   while ((index < 4) && (i != firstBits.end()) && (j != secondBits.end()))
269   {
270     littleChecksum.push_back(*i ^ *j);
271     ++i;
272     ++j;
273     ++index;
274   }
275 }