53c882a8e517b5b219aafcdde39167a34fbf743c
[pierogi] / protocols / rc6skyprotocol.cpp
1 #include "rc6skyprotocol.h"
2
3 #include "pirrx51hardware.h"
4
5 #include "pirexception.h"
6
7 #include <QMutex>
8 extern bool commandInFlight;
9 extern QMutex commandIFMutex;
10
11 // These defines might need to be turned into variables, for odd devices.
12 #define HEADER_PULSE 2666
13 #define HEADER_SPACE 888
14 #define TRAILER_BIPHASE 888
15
16 // This version of Mode 6 RC6 is used in Sky and Sky+ receivers.  It seems to
17 // be pretty close to vanilla RC6.
18 // The biphase unit of time is 444 usec.
19 // The RC6 header block starts with the normal 2666 usec pulse, 888 usec space.
20 // The next bit is fixed as a "1", as usual.
21 // The next three bits are 110, marking this as a mode 6 protocol.
22 // The trailer bit has an 888 usec biphase.  It is a toggle bit.
23 // Next comes 8 bits of address, 4 bits I don't know about (subdevice?),
24 // and finally 8 bits of command.
25 // A space of (at least) 2666 usec must follow any command.
26 // The carrier frequency is 36 kHZ, duty cycle between 25 and 50 %.
27
28 RC6SkyProtocol::RC6SkyProtocol(
29   QObject *guiObject,
30   unsigned int index)
31   : PIRProtocol(guiObject, index, 2666, false),
32     biphaseUnit(444),
33     buffer(0),
34     keypressCount(0)
35 {
36   setCarrierFrequency(36000);
37 }
38
39
40 void RC6SkyProtocol::startSendingCommand(
41   unsigned int threadableID,
42   PIRKeyName command)
43 {
44   try
45   {
46     // Is this command meant for us?
47     if (threadableID != id) return;
48
49     clearRepeatFlag();
50
51     KeycodeCollection::const_iterator i = keycodes.find(command);
52
53     // Sanity check:
54     if (i == keycodes.end())
55     {
56       std::string s = "Tried to send a non-existent command.\n";
57       throw PIRException(s);
58     }
59
60     PIRRX51Hardware rx51device(carrierFrequency, dutyCycle);
61
62     int repeatCount = 0;
63     int duration = 0;
64     while (repeatCount < MAX_REPEAT_COUNT)
65     {
66       bufferContainsSpace = false;
67       bufferContainsPulse = false;
68       // First, construct the "Header" segment of the pulse train.
69       //
70       // The header involves:
71       // a) a "lead" of 2666 us pulse, 888 us space;
72       // b) a "start bit", value 1 (so 444 us pulse, 444 us space)
73       // c) three control bits, set to "110" (i.e., mode "6")
74       // d) the double-sized "trailer" bit, set based on the keypress count:
75
76       rx51device.addSingle(HEADER_PULSE); // lead pulse
77       duration += HEADER_PULSE;
78       rx51device.addSingle(HEADER_SPACE); // lead space
79       duration += HEADER_SPACE;
80       rx51device.addSingle(biphaseUnit); // start bit pulse
81       duration += biphaseUnit;
82       rx51device.addSingle(biphaseUnit); // start bit space
83       duration += biphaseUnit;
84       rx51device.addSingle(biphaseUnit); // bit 1 pulse;
85       duration += biphaseUnit;
86       rx51device.addSingle(biphaseUnit); // bit 1 space;
87       duration += biphaseUnit;
88       rx51device.addSingle(biphaseUnit); // bit 2 pulse;
89       duration += biphaseUnit;
90       rx51device.addSingle(2 * biphaseUnit); // bit 2 space + bit 3 space;
91       duration += 2 * biphaseUnit;
92
93       if (keypressCount % 2)
94       {
95         rx51device.addSingle(biphaseUnit); // bit 3 pulse;
96         duration += biphaseUnit;
97         rx51device.addSingle(2 * biphaseUnit); // trailer space
98         duration += 2 * biphaseUnit;
99         buffer = 2 * biphaseUnit; // trailer pulse goes into the buffer
100         bufferContainsPulse = true;
101       }
102       else
103       {
104         rx51device.addSingle(3 * biphaseUnit); // bit 3 + trailer pulses
105         duration += 3 * biphaseUnit;
106         buffer = 2 * biphaseUnit; // trailer space goes into the buffer
107         bufferContainsSpace = true;
108       }
109
110       // Now, we can start the normal buffering process:
111
112       // push the address data:
113       duration += pushBits(preData, rx51device);
114
115       // push the command data:
116       duration += pushBits((*i).second.firstCode, rx51device);
117
118       // Flush out the buffer, if necessary:
119       if (buffer)
120       {
121         rx51device.addSingle(buffer);
122         duration += buffer;
123         buffer = 0;
124       }
125
126       // Actually send out the command:
127       rx51device.sendCommandToDevice();
128
129       // Sleep for an amount of time.  (RC6 demands an addtional 6 unit space
130       // at the end of any command...)
131       sleepUntilRepeat(duration + 6 * biphaseUnit);
132
133       // Have we been told to stop yet?
134       if (checkRepeatFlag())
135       {
136         // Yes, we can now quit repeating:
137         ++keypressCount;
138         QMutexLocker ciflocker(&commandIFMutex);
139         commandInFlight = false;
140         return;
141       }
142     }
143   }
144   catch (PIRException e)
145   {
146     emit commandFailed(e.getError().c_str());
147   }
148
149   ++keypressCount;
150   QMutexLocker cifLocker(&commandIFMutex);
151   commandInFlight = false;
152 }
153
154
155 int RC6SkyProtocol::pushBits(
156   const CommandSequence &bits,
157   PIRRX51Hardware &rx51device)
158 {
159   int duration = 0;
160
161   CommandSequence::const_iterator i = bits.begin();
162
163   while (i != bits.end())
164   {
165     if (*i)
166     {
167       duration += pushOne(rx51device);
168     }
169     else
170     {
171       duration += pushZero(rx51device);
172     }
173
174     ++i;
175   }
176
177   return duration;
178 }
179
180
181 // This should be part of a general RC6 parent maybe?
182 int RC6SkyProtocol::pushZero(
183   PIRRX51Hardware &rx51device)
184 {
185   // Need to add a space, then a pulse.
186   int duration = 0;
187
188   if (bufferContainsSpace)
189   {
190     // Merge this space and the previous one, and send to device:
191     rx51device.addSingle(buffer + biphaseUnit);
192     duration += (buffer + biphaseUnit);
193     buffer = 0;
194      bufferContainsSpace = false;
195   }
196   else
197   {
198     if (bufferContainsPulse)
199     {
200       // Flush out the buffer:
201       rx51device.addSingle(buffer);
202       duration += buffer;
203       buffer = 0;
204       bufferContainsPulse = false;
205     }
206
207     // push a space onto the device:
208     rx51device.addSingle(biphaseUnit);
209     duration += biphaseUnit;
210   }
211
212   // Put a pulse into the buffer to wait:
213   buffer = biphaseUnit;
214   bufferContainsPulse = true;
215
216   return duration;
217 }
218
219
220 int RC6SkyProtocol::pushOne(
221   PIRRX51Hardware &rx51device)
222 {
223   // Need to add a pulse, then a space.
224   int duration = 0;
225
226   // First, the pulse:
227   if (bufferContainsPulse)
228   {
229     rx51device.addSingle(buffer + biphaseUnit);
230     duration += (buffer + biphaseUnit);
231     buffer = 0;
232     bufferContainsPulse = false;
233   }
234   else
235   {
236     if (bufferContainsSpace)
237     {
238       // Flush the buffer:
239       rx51device.addSingle(buffer);
240       duration += buffer;
241       buffer = 0;
242       bufferContainsSpace = false;
243     }
244     // Now, add the pulse:
245     rx51device.addSingle(biphaseUnit);
246     duration += biphaseUnit;
247   }
248
249   // Next, push a space onto the buffer:
250   buffer = biphaseUnit;
251   bufferContainsSpace = true;
252
253   return duration;
254 }