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 QMutexLocker cifLocker(&commandIFMutex);
70 commandInFlight = false;
72 // std::string s = "Tried to send a non-existent command.\n";
73 // throw PIRException(s);
76 // construct the device:
77 PIRRX51Hardware rx51device(carrierFrequency, dutyCycle);
80 int commandDuration = 0;
81 while (repeatCount < MAX_REPEAT_COUNT)
85 commandDuration = generateRepeatCommand(i->second, rx51device);
89 commandDuration = generateStandardCommand(i->second, rx51device);
92 // Now, tell the device to send the whole command:
93 rx51device.sendCommandToDevice();
95 // sleep until the next repetition of command:
96 sleepUntilRepeat(commandDuration);
98 // Check whether we've reached the minimum required number of repetitons:
99 if (repeatCount >= minimumRepetitions)
101 // Check whether we've been asked to stop:
102 if (checkRepeatFlag())
104 // Do we need to send out a final frame?
107 commandDuration = generateFinalCommand(i->second, rx51device);
108 rx51device.sendCommandToDevice();
109 sleepUntilRepeat(commandDuration);
114 QMutexLocker cifLocker(&commandIFMutex);
115 commandInFlight = false;
124 QMutexLocker cifLocker(&commandIFMutex);
125 commandInFlight = false;
127 catch (PIRException e)
130 emit commandFailed(e.getError().c_str());
135 int XMPProtocol::generateStandardCommand(
136 const PIRKeyBits &pkb,
137 PIRRX51Hardware &rx51device)
141 // XMP frames have the following structure:
142 // 1) The first 4 bits of the "sub-device" code
143 // 2) A four-bit checksum value
144 // 3) The second 4 bits of the "sub-device" code
145 // 4) The four-bit value 0xF
146 // 5) An eight-bit OEM code (normally 0x44)
147 // 6) An eight-bit device code
148 // 7) a 210 usec pulse, 13800 usec space divider
149 // 8) The first 4 bits of the "sub-device" code (again)
150 // 9) Another four-bit checksum value
151 // 10) The four-bit toggle value
152 // 11) The second 4 bits of the "sub-device" code (again)
153 // 12) A pair of 8-bit command codes (often one of them will be 0)
154 // All of this is sent in MSB order.
155 // The checksums are constructed by adding up all the half-bytes in
156 // their side of the frame to 15, taking the complement, and modding the
159 duration += pushHalfByte(subDeviceOne, rx51device);
160 duration += pushHalfByte(calculateChecksumOne(), rx51device);
161 duration += pushHalfByte(subDeviceTwo, rx51device);
162 duration += pushHalfByte(0xF, rx51device);
163 duration += pushFullByte(oemCode, rx51device);
164 duration += pushFullByte(deviceCode, rx51device);
166 rx51device.addPair(210, 13800);
169 duration += pushHalfByte(subDeviceOne, rx51device);
170 duration += pushHalfByte(
171 calculateChecksumTwo(0x0, pkb.firstCode, pkb.secondCode),
173 duration += pushHalfByte(0x0, rx51device);
174 duration += pushHalfByte(subDeviceTwo, rx51device);
175 duration += pushBits(pkb.firstCode, rx51device);
176 duration += pushBits(pkb.secondCode, rx51device);
178 // Finally add the "trail":
179 rx51device.addSingle(210);
186 int XMPProtocol::generateRepeatCommand(
187 const PIRKeyBits &pkb,
188 PIRRX51Hardware &rx51device)
192 // an XMP repeat frame is identical to the start frame, except that
193 // the "toggle" value is now 8.
195 duration += pushHalfByte(subDeviceOne, rx51device);
196 duration += pushHalfByte(calculateChecksumOne(), rx51device);
197 duration += pushHalfByte(subDeviceTwo, rx51device);
198 duration += pushHalfByte(0xF, rx51device);
199 duration += pushFullByte(oemCode, rx51device);
200 duration += pushFullByte(deviceCode, rx51device);
202 rx51device.addPair(210, 13800);
205 duration += pushHalfByte(subDeviceOne, rx51device);
206 duration += pushHalfByte(
207 calculateChecksumTwo(0x8, pkb.firstCode, pkb.secondCode),
209 duration += pushHalfByte(0x8, rx51device);
210 duration += pushHalfByte(subDeviceTwo, rx51device);
211 duration += pushBits(pkb.firstCode, rx51device);
212 duration += pushBits(pkb.secondCode, rx51device);
214 // Finally add the "trail":
215 rx51device.addSingle(210);
222 int XMPProtocol::generateFinalCommand(
223 const PIRKeyBits &pkb,
224 PIRRX51Hardware &rx51device)
228 // an XMP final frame is basically a pair of repeat frames, but the
229 // gap between them is only 13800 usec, and the "toggle" value of the
230 // second frame is 9.
232 duration += pushHalfByte(subDeviceOne, rx51device);
233 duration += pushHalfByte(calculateChecksumOne(), rx51device);
234 duration += pushHalfByte(subDeviceTwo, rx51device);
235 duration += pushHalfByte(0xF, rx51device);
236 duration += pushFullByte(oemCode, rx51device);
237 duration += pushFullByte(deviceCode, rx51device);
239 rx51device.addPair(210, 13800);
242 duration += pushHalfByte(subDeviceOne, rx51device);
243 duration += pushHalfByte(
244 calculateChecksumTwo(0x8, pkb.firstCode, pkb.secondCode),
246 duration += pushHalfByte(0x8, rx51device);
247 duration += pushHalfByte(subDeviceTwo, rx51device);
248 duration += pushBits(pkb.firstCode, rx51device);
249 duration += pushBits(pkb.secondCode, rx51device);
251 rx51device.addPair(210, 13800);
254 duration += pushHalfByte(subDeviceOne, rx51device);
255 duration += pushHalfByte(calculateChecksumOne(), rx51device);
256 duration += pushHalfByte(subDeviceTwo, rx51device);
257 duration += pushHalfByte(0xF, rx51device);
258 duration += pushFullByte(oemCode, rx51device);
259 duration += pushFullByte(deviceCode, rx51device);
261 rx51device.addPair(210, 13800);
264 duration += pushHalfByte(subDeviceOne, rx51device);
265 duration += pushHalfByte(
266 calculateChecksumTwo(0x9, pkb.firstCode, pkb.secondCode),
268 duration += pushHalfByte(0x9, rx51device);
269 duration += pushHalfByte(subDeviceTwo, rx51device);
270 duration += pushBits(pkb.firstCode, rx51device);
271 duration += pushBits(pkb.secondCode, rx51device);
273 // Finally add the "trail":
274 rx51device.addSingle(210);
281 unsigned int XMPProtocol::calculateChecksumOne()
283 // Start with the value 0xF:
284 unsigned int total = 0xF;
286 // Add the other half-bytes in the first part of the frame:
287 total += subDeviceOne;
288 total += subDeviceTwo;
290 total += oemCode >> 4;
291 total += oemCode & 0x0F;
292 total += deviceCode >> 4;
293 total += deviceCode & 0x0F;
298 // Finally, mod 0x10:
299 total = total % 0x10;
305 unsigned int XMPProtocol::calculateChecksumTwo(
307 const CommandSequence &firstCode,
308 const CommandSequence &secondCode)
310 // Start with the value 0xF:
311 unsigned int total = 0xF;
313 // Add the other half-bytes in the second part of the frame:
314 total += subDeviceOne;
316 total += subDeviceTwo;
318 unsigned int codeValue = 0;
319 CommandSequence::const_iterator i = firstCode.begin();
321 while (i != firstCode.end())
323 // Shift codeValue over and add the bit:
324 codeValue = codeValue << 1;
329 total += codeValue >> 4;
330 total += codeValue & 0xF;
333 i = secondCode.begin();
335 while (i != secondCode.end())
337 codeValue = codeValue << 1;
342 total += codeValue >> 4;
343 total += codeValue & 0xF;
348 // Finally, mod 0x10:
349 total = total % 0x10;
355 int XMPProtocol::pushHalfByte(
356 unsigned int halfByte,
357 PIRRX51Hardware &rx51device)
359 unsigned int space = 760 + (136 * halfByte);
360 rx51device.addPair(210, space);
362 return (210 + space);
366 int XMPProtocol::pushFullByte(
367 unsigned int fullByte,
368 PIRRX51Hardware &rx51device)
370 unsigned int firstSpace = 760 + (136 * (fullByte >> 4));
371 unsigned int secondSpace = 760 + (136 * (fullByte & 0xF));
373 rx51device.addPair(210, firstSpace);
374 rx51device.addPair(210, secondSpace);
376 return (420 + firstSpace + secondSpace);
380 int XMPProtocol::pushBits(
381 const CommandSequence &bits,
382 PIRRX51Hardware &rx51device)
384 unsigned int duration = 0;
386 // We can only sent 4-bit values in XMP, so need to collect bits up into
389 unsigned int bitsValue = 0;
391 CommandSequence::const_iterator i = bits.begin();
393 while (i != bits.end())
397 bitsValue = bitsValue << 1;
403 rx51device.addPair(210, 760 + (136 * bitsValue));
404 duration += 970 + (136 * bitsValue);
415 rx51device.addPair(210, 760 + (136 * bitsValue));
416 duration += 970 + (136 * bitsValue);