1 #include "xmpprotocol.h"
3 #include "pirrx51hardware.h"
5 #include "pirexception.h"
7 // Some global communications stuff:
9 extern bool commandInFlight;
10 extern QMutex commandIFMutex;
12 // The XMP protocol is a real beast, packed full of checksums, toggle bits,
13 // large command codes and fancy repeat mechanisms.
14 // Each pulse/space pair represents four bits, as so:
15 // A "zero" is encoded with a 210 usec pulse, 760 usec space.
16 // Each value after that adds an additional 136 usec to the space, so
17 // a "one" has a 1*136 + 760 = 896 usec space,
18 // a "two" has a 2*136 + 760 = 1032 usec space,
20 // and a "fifteen" has a 15*136 + 760 = 2800 usec space.
21 // There is no header pulse.
22 // There is a 210 usec pulse, 13800 usec space in the middle...
23 // Commands end with a trailing 210 usec pulse.
24 // The first "frame" has a 4-bit "toggle" value of 0; repeat frames following
25 // this one are identical, except for the "toggle" value changed to 8.
26 // There is a gap of 80000 usec between each frame.
27 // An optional "final" frame can also exist, with a toggle value of 9, and
28 // separated from the previous frame by only 13800 usec.
29 // The carrier frequency should be 38 kHz.
31 XMPProtocol::XMPProtocol(
51 void XMPProtocol::startSendingCommand(
52 unsigned int threadableID,
55 // Exceptions here are problematic; I'll try to weed them out by putting the
56 // whole thing in a try/catch block:
59 // First, check if we are meant to be the recipient of this command:
60 if (threadableID != id) return;
64 KeycodeCollection::const_iterator i = keycodes.find(command);
66 // Do we even have this key defined?
67 if (i == keycodes.end())
69 std::string s = "Tried to send a non-existent command.\n";
70 throw PIRException(s);
73 // construct the device:
74 PIRRX51Hardware rx51device(carrierFrequency, dutyCycle);
77 int commandDuration = 0;
78 while (repeatCount < MAX_REPEAT_COUNT)
82 commandDuration = generateRepeatCommand(i->second, rx51device);
86 commandDuration = generateStandardCommand(i->second, rx51device);
89 // Now, tell the device to send the whole command:
90 rx51device.sendCommandToDevice();
92 // sleep until the next repetition of command:
93 sleepUntilRepeat(commandDuration);
95 // Check whether we've reached the minimum required number of repetitons:
96 if (repeatCount >= minimumRepetitions)
98 // Check whether we've been asked to stop:
99 if (checkRepeatFlag())
101 // Do we need to send out a final frame?
104 commandDuration = generateFinalCommand(i->second, rx51device);
105 rx51device.sendCommandToDevice();
106 sleepUntilRepeat(commandDuration);
109 QMutexLocker cifLocker(&commandIFMutex);
110 commandInFlight = false;
118 catch (PIRException e)
121 emit commandFailed(e.getError().c_str());
124 QMutexLocker cifLocker(&commandIFMutex);
125 commandInFlight = false;
129 int XMPProtocol::generateStandardCommand(
130 const PIRKeyBits &pkb,
131 PIRRX51Hardware &rx51device)
135 // XMP frames have the following structure:
136 // 1) The first 4 bits of the "sub-device" code
137 // 2) A four-bit checksum value
138 // 3) The second 4 bits of the "sub-device" code
139 // 4) The four-bit value 0xF
140 // 5) An eight-bit OEM code (normally 0x44)
141 // 6) An eight-bit device code
142 // 7) a 210 usec pulse, 13800 usec space divider
143 // 8) The first 4 bits of the "sub-device" code (again)
144 // 9) Another four-bit checksum value
145 // 10) The four-bit toggle value
146 // 11) The second 4 bits of the "sub-device" code (again)
147 // 12) A pair of 8-bit command codes (often one of them will be 0)
148 // All of this is sent in MSB order.
149 // The checksums are constructed by adding up all the half-bytes in
150 // their side of the frame to 15, taking the complement, and modding the
153 duration += pushHalfByte(subDeviceOne, rx51device);
154 duration += pushHalfByte(calculateChecksumOne(), rx51device);
155 duration += pushHalfByte(subDeviceTwo, rx51device);
156 duration += pushHalfByte(0xF, rx51device);
157 duration += pushFullByte(oemCode, rx51device);
158 duration += pushFullByte(deviceCode, rx51device);
160 rx51device.addPair(210, 13800);
163 duration += pushHalfByte(subDeviceOne, rx51device);
164 duration += pushHalfByte(
165 calculateChecksumTwo(0x0, pkb.firstCode, pkb.secondCode),
167 duration += pushHalfByte(0x0, rx51device);
168 duration += pushHalfByte(subDeviceTwo, rx51device);
169 duration += pushBits(pkb.firstCode, rx51device);
170 duration += pushBits(pkb.secondCode, rx51device);
172 // Finally add the "trail":
173 rx51device.addSingle(210);
180 int XMPProtocol::generateRepeatCommand(
181 const PIRKeyBits &pkb,
182 PIRRX51Hardware &rx51device)
186 // an XMP repeat frame is identical to the start frame, except that
187 // the "toggle" value is now 8.
189 duration += pushHalfByte(subDeviceOne, rx51device);
190 duration += pushHalfByte(calculateChecksumOne(), rx51device);
191 duration += pushHalfByte(subDeviceTwo, rx51device);
192 duration += pushHalfByte(0xF, rx51device);
193 duration += pushFullByte(oemCode, rx51device);
194 duration += pushFullByte(deviceCode, rx51device);
196 rx51device.addPair(210, 13800);
199 duration += pushHalfByte(subDeviceOne, rx51device);
200 duration += pushHalfByte(
201 calculateChecksumTwo(0x8, pkb.firstCode, pkb.secondCode),
203 duration += pushHalfByte(0x8, rx51device);
204 duration += pushHalfByte(subDeviceTwo, rx51device);
205 duration += pushBits(pkb.firstCode, rx51device);
206 duration += pushBits(pkb.secondCode, rx51device);
208 // Finally add the "trail":
209 rx51device.addSingle(210);
216 int XMPProtocol::generateFinalCommand(
217 const PIRKeyBits &pkb,
218 PIRRX51Hardware &rx51device)
222 // an XMP final frame is basically a pair of repeat frames, but the
223 // gap between them is only 13800 usec, and the "toggle" value of the
224 // second frame is 9.
226 duration += pushHalfByte(subDeviceOne, rx51device);
227 duration += pushHalfByte(calculateChecksumOne(), rx51device);
228 duration += pushHalfByte(subDeviceTwo, rx51device);
229 duration += pushHalfByte(0xF, rx51device);
230 duration += pushFullByte(oemCode, rx51device);
231 duration += pushFullByte(deviceCode, rx51device);
233 rx51device.addPair(210, 13800);
236 duration += pushHalfByte(subDeviceOne, rx51device);
237 duration += pushHalfByte(
238 calculateChecksumTwo(0x8, pkb.firstCode, pkb.secondCode),
240 duration += pushHalfByte(0x8, rx51device);
241 duration += pushHalfByte(subDeviceTwo, rx51device);
242 duration += pushBits(pkb.firstCode, rx51device);
243 duration += pushBits(pkb.secondCode, rx51device);
245 rx51device.addPair(210, 13800);
248 duration += pushHalfByte(subDeviceOne, rx51device);
249 duration += pushHalfByte(calculateChecksumOne(), rx51device);
250 duration += pushHalfByte(subDeviceTwo, rx51device);
251 duration += pushHalfByte(0xF, rx51device);
252 duration += pushFullByte(oemCode, rx51device);
253 duration += pushFullByte(deviceCode, rx51device);
255 rx51device.addPair(210, 13800);
258 duration += pushHalfByte(subDeviceOne, rx51device);
259 duration += pushHalfByte(
260 calculateChecksumTwo(0x9, pkb.firstCode, pkb.secondCode),
262 duration += pushHalfByte(0x9, rx51device);
263 duration += pushHalfByte(subDeviceTwo, rx51device);
264 duration += pushBits(pkb.firstCode, rx51device);
265 duration += pushBits(pkb.secondCode, rx51device);
267 // Finally add the "trail":
268 rx51device.addSingle(210);
275 unsigned int XMPProtocol::calculateChecksumOne()
277 // Start with the value 0xF:
278 unsigned int total = 0xF;
280 // Add the other half-bytes in the first part of the frame:
281 total += subDeviceOne;
282 total += subDeviceTwo;
284 total += oemCode >> 4;
285 total += oemCode & 0x0F;
286 total += deviceCode >> 4;
287 total += deviceCode & 0x0F;
292 // Finally, mod 0x10:
293 total = total % 0x10;
299 unsigned int XMPProtocol::calculateChecksumTwo(
301 const CommandSequence &firstCode,
302 const CommandSequence &secondCode)
304 // Start with the value 0xF:
305 unsigned int total = 0xF;
307 // Add the other half-bytes in the second part of the frame:
308 total += subDeviceOne;
310 total += subDeviceTwo;
312 unsigned int codeValue = 0;
313 CommandSequence::const_iterator i = firstCode.begin();
315 while (i != firstCode.end())
317 // Shift codeValue over and add the bit:
318 codeValue = codeValue << 1;
323 total += codeValue >> 4;
324 total += codeValue & 0xF;
327 i = secondCode.begin();
329 while (i != secondCode.end())
331 codeValue = codeValue << 1;
336 total += codeValue >> 4;
337 total += codeValue & 0xF;
342 // Finally, mod 0x10:
343 total = total % 0x10;
349 int XMPProtocol::pushHalfByte(
350 unsigned int halfByte,
351 PIRRX51Hardware &rx51device)
353 unsigned int space = 760 + (136 * halfByte);
354 rx51device.addPair(210, space);
356 return (210 + space);
360 int XMPProtocol::pushFullByte(
361 unsigned int fullByte,
362 PIRRX51Hardware &rx51device)
364 unsigned int firstSpace = 760 + (136 * (fullByte >> 4));
365 unsigned int secondSpace = 760 + (136 * (fullByte & 0xF));
367 rx51device.addPair(210, firstSpace);
368 rx51device.addPair(210, secondSpace);
370 return (420 + firstSpace + secondSpace);
374 int XMPProtocol::pushBits(
375 const CommandSequence &bits,
376 PIRRX51Hardware &rx51device)
378 unsigned int duration = 0;
380 // We can only sent 4-bit values in XMP, so need to collect bits up into
383 unsigned int bitsValue = 0;
385 CommandSequence::const_iterator i = bits.begin();
387 while (i != bits.end())
391 bitsValue = bitsValue << 1;
397 rx51device.addPair(210, 760 + (136 * bitsValue));
398 duration += 970 + (136 * bitsValue);
409 rx51device.addPair(210, 760 + (136 * bitsValue));
410 duration += 970 + (136 * bitsValue);