Improved Keyset Selection Window
[pierogi] / rc5protocol.cpp
1 #include "rc5protocol.h"
2
3 #include "pirexception.h"
4
5 RC5Protocol::RC5Protocol(
6   QObject *guiObject,
7   unsigned int index,
8   unsigned int bPulse,
9   unsigned int bSpace,
10   unsigned int lPulse,
11   unsigned int gSpace,
12   bool iclflag)
13   : PIRProtocol(guiObject, index, gSpace, iclflag),
14     biphasePulse(bPulse),
15     biphaseSpace(bSpace),
16     leadPulse(lPulse),
17     buffer(0),
18     keypressCount(0)
19 {
20 }
21
22
23 void RC5Protocol::setHeaderPair(
24   unsigned int pulse,
25   unsigned int space)
26 {
27   headerPulse = pulse;
28   headerSpace = space;
29   hasHeaderPair = true;
30 }
31
32
33 void RC5Protocol::setPreData(
34   unsigned long data,
35   unsigned int bits)
36 {
37   appendToBitSeq(preData, data, bits);
38 }
39
40
41 void RC5Protocol::setToggleBit(
42   unsigned int bit)
43 {
44   toggleBit = bit;
45 }
46
47
48 void RC5Protocol::startSendingCommand(
49   unsigned int threadableID,
50   PIRKeyName command)
51 {
52   // Dumping entire method inside of try/catch, to deal with cross-thread
53   // exception handling:
54   try
55   {
56     clearRepeatFlag();
57
58     if (threadableID != id) return;
59
60     KeycodeCollection::const_iterator i = keycodes.find(command);
61
62     // Sanity check, make sure command exists first:
63     if (i == keycodes.end())
64     {
65       std::string s = "Tried to send a non-existent command.\n";
66       throw PIRException(s);
67     }
68
69     // Construct the object that communicates with the device driver:
70     PIRRX51Hardware rx51device(carrierFrequency, dutyCycle);
71
72     int repeatCount = 0;
73     while (repeatCount < MAX_REPEAT_COUNT)
74     {
75       int commandDuration = 0;
76
77       // Now, throw together an RC5 protocol command string:
78       buffer = 0;
79       bufferContainsPulse = false;
80       bufferContainsSpace = false;
81   
82       // Start off the first pulse with the lead, if any:
83       if (leadPulse)
84       {
85         buffer = leadPulse;
86         bufferContainsPulse = true;
87       }
88
89       // Encode the bits:
90       commandDuration += pushBits((*i).second, rx51device);
91
92       // Clear out the buffer, if necessary:
93       if (buffer)
94       {
95         rx51device.addSingle(buffer);
96         commandDuration += buffer;
97         buffer = 0;
98         bufferContainsSpace = false;
99         bufferContainsPulse = false;
100       }
101
102       // Now, tell the device to send the whole command:
103       rx51device.sendCommandToDevice();
104
105       // Sleep for an amount of time.  (Need to make this interruptable!)
106       sleepUntilRepeat(commandDuration);
107
108       // Have we satisfied the minimum number of repetitions?
109       if (repeatCount >= minimumRepetitions)
110       {
111         // Have we been told to stop yet?
112         if (checkRepeatFlag())
113         {
114           // Ok, then we can quit now:
115           ++keypressCount;
116           return;
117         }
118       }
119
120       ++repeatCount;
121     }
122   }
123   catch (PIRException e)
124   {
125     emit commandFailed(e.getError().c_str());
126   }
127
128   ++keypressCount;
129 }
130
131
132 int RC5Protocol::pushBits(
133   const CommandSequence &bits,
134   PIRRX51Hardware &rx51device)
135 {
136   int bitsDuration = 0;
137
138   // Rather than encoding a 0 or 1 through the timing of a pulse, RC5 encodes
139   // a bit by swapping the order of pulses and spaces.  (This is called
140   // "biphase".)
141
142   CommandSequence::const_iterator i = bits.begin();
143   int bitCount = 1;
144   bool bitValue;
145
146   while (i != bits.end())
147   {
148     bitValue = *i;
149
150     if (bitCount == toggleBit)  // are we on a toggled bit?
151     {
152       if (keypressCount % 2)  // is it time to toggle?
153       {
154         bitValue = !bitValue;  // then flip the bit
155       }
156     }
157
158     if (bitValue)
159     {
160       // We've got a "1".  First add a space, then a pulse.
161       if (bufferContainsSpace)
162       {
163         // Merge our space with the previous space, and send them to
164         // the device.
165         rx51device.addSingle(buffer + biphaseSpace);
166         bitsDuration += (buffer + biphaseSpace);
167         buffer = 0;
168         bufferContainsSpace = false;
169       }
170       else
171       {
172         if (bufferContainsPulse)
173         {
174           // Flush the buffer:
175           rx51device.addSingle(buffer);
176           bitsDuration += buffer;
177           buffer = 0;
178           bufferContainsPulse = false;
179         }
180         // Add a space:
181         rx51device.addSingle(biphaseSpace);
182         bitsDuration += biphaseSpace;
183       }
184
185       // Put a pulse into the buffer to wait.
186       buffer = biphasePulse;
187       bufferContainsPulse = true;
188     }
189     else
190     {
191       // We've got a "0".  First add a pulse, then a space.
192       if (bufferContainsPulse)
193       {
194         // Merge our pulse with the previous one, and send them to the device:
195         rx51device.addSingle(buffer + biphasePulse);
196         bitsDuration += (buffer + biphasePulse);
197         buffer = 0;
198         bufferContainsPulse = false;
199       }
200       else
201       {
202         if (bufferContainsSpace)
203         {
204           // Flush out the buffer:
205           rx51device.addSingle(buffer);
206           bitsDuration += buffer;
207           buffer = 0;
208           bufferContainsSpace = false;
209         }
210
211         // Add a pulse:
212         rx51device.addSingle(biphasePulse);
213         bitsDuration += biphasePulse;
214       }
215
216       // Put a space into the buffer to wait:
217       buffer = biphaseSpace;
218       bufferContainsSpace = true;
219     }
220
221     ++i;
222     ++bitCount;
223   }
224
225   return bitsDuration;
226 }