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