]>
Commit | Line | Data |
---|---|---|
e3a2b9c9 | 1 | #ifndef ALIMUONTRACKERDDLDECODER_H |
2 | #define ALIMUONTRACKERDDLDECODER_H | |
3 | /************************************************************************** | |
4 | * This file is property of and copyright by the ALICE HLT Project * | |
5 | * All rights reserved. * | |
6 | * * | |
7 | * Primary Authors: * | |
8 | * Artur Szostak <artursz@iafrica.com> * | |
9 | * * | |
10 | * Permission to use, copy, modify and distribute this software and its * | |
11 | * documentation strictly for non-commercial purposes is hereby granted * | |
12 | * without fee, provided that the above copyright notice appears in all * | |
13 | * copies and that both the copyright notice and this permission notice * | |
14 | * appear in the supporting documentation. The authors make no claims * | |
15 | * about the suitability of this software for any purpose. It is * | |
16 | * provided "as is" without express or implied warranty. * | |
17 | **************************************************************************/ | |
18 | ||
19 | /* $Id$ */ | |
20 | ||
21 | /// | |
22 | /// \file AliMUONTrackerDDLDecoder.h | |
23 | /// \author Artur Szostak <artursz@iafrica.com> | |
24 | /// \date 28-11-2007 | |
25 | /// \brief Implementation of a high performance DDL decoder for the muon tracking stations. | |
26 | /// | |
27 | /// This file implementes the AliMUONTrackerDDLDecoder class, which contains | |
28 | /// the core logic for decoding the payload in DDL streams comming from the muon | |
29 | /// spectrometer's tracking chambers in a very efficient manner. | |
30 | /// | |
31 | /// This implementation is derived from work done by Christian Finck for the | |
32 | /// AliMUONPayloadTracker. | |
33 | /// | |
34 | /// Note to maintainers: Please remember that this file is used by the online | |
35 | /// dHLT system. As an online system, the dHLT requires the fastest code possible | |
36 | /// in the decoders to satisfy its timing constraints. The performance impact | |
37 | /// must be checked before any proposed modification is made to this file. | |
38 | /// | |
39 | ||
40 | #include "AliMUONTrackerDDLDecoderEventHandler.h" | |
41 | ||
e3a2b9c9 | 42 | /// \ingroup raw |
43 | /// \class AliMUONTrackerDDLDecoder | |
44 | /// \brief A high performance decoder class for MUON tracking DDL data. | |
45 | /// | |
46 | /// This class implements a high performance decoder for decoding DDL payload | |
47 | /// data coming from the muon spectrometers tracking chambers. | |
48 | /// It has been implemented using the event driven paradigm, which allows us | |
49 | /// to minimise the number of method calls made in the inner loops of the algorithm | |
50 | /// and minimise the memory footprint. | |
51 | /// The decoder class only contains the basic decoding and error checking logic. | |
52 | /// It calls methods such as OnNewBlock, OnNewBusPatch, OnData etc in | |
53 | /// the event handler during the decoding to return the decoded data. | |
54 | /// The event handler class is nothing more than a callback interface to deliver | |
55 | /// the next chunks of decoded data. | |
56 | /// To actually do something with the data one needs to implement a custom | |
57 | /// event handler (callback) class by inheriting from AliMUONTrackerDDLDecoderEventHandler | |
58 | /// and overriding the callback methods like so: | |
59 | /// \code | |
60 | /// class MyCustomHandler : public AliMUONTrackerDDLDecoderEventHandler | |
61 | /// { | |
62 | /// public: | |
63 | /// void OnData(UInt_t data) | |
64 | /// { | |
65 | /// // I can do something with 'data' here. | |
66 | /// } | |
67 | /// }; | |
68 | /// \endcode | |
69 | /// | |
70 | /// Once the custom handler is written then the decoder is in instantiated as | |
71 | /// shown below, to use your new custom handler. And to start decoding one needs | |
72 | /// to call the Decode() method of the decoder. | |
73 | /// \code | |
74 | /// AliMUONTrackerDDLDecoder<MyCustomHandler> myDecoder; | |
75 | /// muDecoder.Decoder(buffer, bufferSize); | |
76 | /// \endcode | |
77 | /// | |
78 | /// Note that this class was written as a template on purpose. To maximise the | |
79 | /// compilers chance to make optimisations and inlining code must use a template. | |
80 | /// Depending on exactly what you do inside your handler the decoder will be | |
81 | /// significantly slower if run time polymorphism was used i.e. making the class | |
82 | /// AliMUONTrackerDDLDecoderEventHandler abstract and using virtual methods. | |
83 | /// | |
84 | /// \author Artur Szostak <artursz@iafrica.com> | |
85 | ||
86 | template <class EventHandler> | |
87 | class AliMUONTrackerDDLDecoder | |
88 | { | |
89 | public: | |
90 | ||
91 | /// Default contructor. | |
92 | AliMUONTrackerDDLDecoder() : | |
93 | fExitOnError(true), fTryRecover(false), | |
94 | fSendDataOnParityError(false), fHadError(false), | |
95 | fMaxBlocks(2), fMaxDSPs(5), fMaxBusPatches(5), fHandler() | |
96 | {} | |
97 | ||
98 | /// Constant method to return the event handler instance. | |
99 | const EventHandler& GetHandler() const { return fHandler; } | |
100 | ||
101 | /// Returns the event handler instance. | |
102 | EventHandler& GetHandler() { return fHandler; } | |
103 | ||
104 | /// Returns the "exit on error" flag. | |
105 | /// i.e. should the decoder stop on the very first error found. | |
106 | bool ExitOnError() const { return fExitOnError; } | |
107 | ||
108 | /// Sets the "exit on error" flag. | |
109 | /// i.e. should the decoder stop on the very first error found. | |
110 | void ExitOnError(bool value) { fExitOnError = value; } | |
111 | ||
112 | /// Returns the "try to recover from errors" flag. | |
113 | /// i.e. should the decoder try to recover from errors found in the | |
114 | /// payload headers. | |
115 | bool TryRecover() const { return fTryRecover; } | |
116 | ||
117 | /// Sets the "try to recover from errors" flag. | |
118 | /// i.e. should the decoder try to recover from errors found in the | |
119 | /// payload headers. | |
120 | void TryRecover(bool value) { fTryRecover = value; } | |
121 | ||
122 | /// Returns the flag indicating if the raw data words in the bus patches | |
123 | /// that failed their parity tests (i.e. parity error / bit flip in the | |
124 | /// raw data word) will be sent to the event handler anyway through OnData. | |
125 | bool SendDataOnParityError() const { return fSendDataOnParityError; } | |
126 | ||
127 | /// Sets the flag indicating if the raw data words in the bus patches | |
128 | /// that failed their parity tests (i.e. parity error / bit flip in the | |
129 | /// raw data word) will be sent to the event handler anyway through OnData. | |
130 | void SendDataOnParityError(bool value) { fSendDataOnParityError = value; } | |
131 | ||
132 | /// Returns the maximum block count expected in the DDL payload. | |
133 | UInt_t MaxBlocks() const { return fMaxBlocks; } | |
134 | ||
135 | /// Sets the maximum block count expected in the DDL payload. | |
136 | void MaxBlocks(UInt_t n) { fMaxBlocks = n; } | |
137 | ||
138 | /// Returns the maximum DSP header count expected in any given block | |
139 | /// structure within the DDL payload. | |
140 | UInt_t MaxDSPs() const { return fMaxDSPs; } | |
141 | ||
142 | /// Sets the maximum DSP header count expected in any given block structure | |
143 | /// within the DDL payload. | |
144 | void MaxDSPs(UInt_t n) { fMaxDSPs = n; } | |
145 | ||
146 | /// Returns the maximum number of bus patches expected in any given DSP | |
147 | /// structure within the DDL payload. | |
148 | UInt_t MaxBusPatches() const { return fMaxBusPatches; } | |
149 | ||
150 | /// Sets the maximum number of bus patches expected in any given DSP | |
151 | /// structure within the DDL payload. | |
152 | void MaxBusPatches(UInt_t n) { fMaxBusPatches = n; } | |
153 | ||
154 | /// This method decodes the DDL payload contained in the buffer. | |
155 | bool Decode(const void* buffer, UInt_t bufferSize); | |
156 | ||
157 | private: | |
158 | ||
159 | bool fExitOnError; ///< Indicates if we should exit on the very first error. | |
160 | bool fTryRecover; ///< Indicates if we should try recover from a corrupt structure header. | |
161 | bool fSendDataOnParityError; ///< If set to true then we issue a OnData() event even if the data word had a parity error. | |
162 | bool fHadError; ///< Indicates if we had an error decoding the data. | |
163 | UInt_t fMaxBlocks; ///< Maximum number of block structures allowed in a DDL stream. | |
164 | UInt_t fMaxDSPs; ///< Maximum number of DSP structures allowed in a DDL stream. | |
165 | UInt_t fMaxBusPatches; ///< Maximum number of bus patch structures allowed in a DDL stream. | |
166 | EventHandler fHandler; ///< The event handler which deals with parsing events. | |
167 | ||
8a0dae7c | 168 | void DecodeBuffer(const UChar_t* start, const UChar_t* end); |
169 | ||
e3a2b9c9 | 170 | bool DecodeBlockData( |
171 | const AliMUONBlockHeaderStruct* blockHeader, | |
172 | const UChar_t* start, const UChar_t* end | |
173 | ); | |
174 | ||
175 | bool DecodeDSPData(const UChar_t* start, const UChar_t* end); | |
176 | ||
177 | bool DecodeBusPatchData(const UChar_t* start, const UChar_t* end); | |
178 | ||
179 | /// Possible results that can be returned by the TryRecoverStruct method. | |
180 | enum RecoverResult | |
181 | { | |
182 | kRecoverFailed, ///< The recovery failed. Cannot continue parsing. | |
183 | kStructRecovered, ///< Indicates that we recovered from a corrupt structure header and can continue processing the given structure. | |
184 | kContinueToNextStruct ///< Must continue parsing the next structure and ignore the current one. | |
185 | }; | |
186 | ||
187 | RecoverResult TryRecoverStruct( | |
188 | UInt_t expectedKey, | |
189 | UInt_t headerSize, | |
190 | UInt_t totalLength, | |
191 | UInt_t length, | |
192 | const UChar_t* structStart, | |
193 | const UChar_t* bufferEnd, | |
194 | const UChar_t*& dataEnd, | |
195 | const UChar_t*& structEnd, | |
196 | const UChar_t*& current | |
197 | ); | |
198 | ||
199 | const UChar_t* FindKey( | |
200 | UInt_t key, const UChar_t* start, const UChar_t* end | |
201 | ); | |
202 | ||
203 | bool ParityIsOk(UInt_t data); | |
204 | ||
205 | static const UInt_t fgkBlockDataKey; ///< The key word expected to identify block structure headers. | |
206 | static const UInt_t fgkDSPDataKey; ///< The key word expected to identify DSP structure headers. | |
207 | static const UInt_t fgkBusPatchDataKey; ///< The key word expected to identify bus patch headers. | |
208 | static const UInt_t fgkPaddingWord; ///< The expected format of the padding word in the DDL payload. | |
209 | }; | |
210 | ||
211 | //_____________________________________________________________________________ | |
212 | ||
213 | // The following are the structure header keys which are used to identify the kind | |
214 | // of structure header we are dealing with: block, DSP or bus patch header. | |
215 | template <class EventHandler> | |
216 | const UInt_t AliMUONTrackerDDLDecoder<EventHandler>::fgkBlockDataKey = 0xFC0000FC; | |
217 | template <class EventHandler> | |
218 | const UInt_t AliMUONTrackerDDLDecoder<EventHandler>::fgkDSPDataKey = 0xF000000F; | |
219 | template <class EventHandler> | |
220 | const UInt_t AliMUONTrackerDDLDecoder<EventHandler>::fgkBusPatchDataKey = 0xB000000B; | |
221 | template <class EventHandler> | |
222 | const UInt_t AliMUONTrackerDDLDecoder<EventHandler>::fgkPaddingWord = 0xBEEFFACE; | |
223 | ||
224 | ||
e3a2b9c9 | 225 | template <class EventHandler> |
226 | bool AliMUONTrackerDDLDecoder<EventHandler>::Decode(const void* buffer, UInt_t bufferSize) | |
227 | { | |
228 | /// This method should be called to actually decode the DDL payload | |
229 | /// contained in a memory buffer. The payload should be for a muon tracking | |
230 | /// chamber DDL stream. | |
231 | /// As the decoder progresses it will make method calls to the event handler | |
232 | /// instance (which can be accessed with the GetHandler() method) to indicate | |
233 | /// the start of the new block, DSP and bus patch headers. For every raw | |
234 | /// data word the OnData method of the event handler is called. | |
235 | /// | |
236 | /// If an error occurs during the parse because the data is corrupt then | |
237 | /// the OnError method is called indicating what the problem was. | |
238 | /// Decoding will stop at this point unless the fExitOnError flag is set | |
239 | /// to false. Also raw data words which contain a parity error are only | |
240 | /// sent to the event handler with OnData if the fSendDataOnParityError | |
241 | /// flag is set to true. There is also an optional flag fTryRecover which | |
242 | /// can enable logic which will attempt to recover the header structures found | |
243 | /// in the DDL payload if they are found to be inconsistent (assumed corrupt). | |
244 | /// | |
245 | /// \param buffer This is the pointer to the start of the memory buffer | |
246 | /// containing the DDL payload. Remember that this must be the start of | |
247 | /// the payload and not the DDL stream. That is, this pointer should be | |
248 | /// equal to: DDL start pointer + 8 * sizeof(UInt_t). | |
249 | /// \param bufferSize This is the pointer to the first byte just past the | |
250 | /// end of the block structure. | |
251 | /// \return Returns false if there was any problem with decoding the data, | |
252 | /// and true otherwise. Note: the data may have been partially decoded | |
253 | /// even if false was returned, which would be indicated by at least one | |
254 | /// call to the event handlers OnData method. | |
255 | ||
256 | assert( buffer != NULL ); | |
257 | ||
258 | fHadError = false; | |
259 | ||
260 | // We are basically implementing something like a recursive decent parser. | |
8a0dae7c | 261 | // So start by marking the start of buffer position and end of buffer. |
262 | const UChar_t* start = reinterpret_cast<const UChar_t*>(buffer); | |
263 | const UChar_t* end = start + bufferSize; | |
e3a2b9c9 | 264 | |
e3a2b9c9 | 265 | fHandler.OnNewBuffer(buffer, bufferSize); |
8a0dae7c | 266 | DecodeBuffer(start, end); |
267 | fHandler.OnEndOfBuffer(buffer, bufferSize); | |
268 | return not fHadError; | |
269 | } | |
270 | ||
271 | ||
272 | template <class EventHandler> | |
273 | void AliMUONTrackerDDLDecoder<EventHandler>::DecodeBuffer( | |
274 | const UChar_t* start, const UChar_t* end | |
275 | ) | |
276 | { | |
277 | /// This method decodes the buffer's payload data. It unpacks the block | |
278 | /// structures contained inside and then for each block it calls the | |
279 | /// OnNewBlock method for the event handler to signal the start of each new | |
280 | /// block structure. OnEndOfBlock is called once each block is processed. | |
281 | /// \param start This is the pointer to the start of the buffer. | |
282 | /// \param end This is the pointer to the first byte just past the | |
283 | /// end of the buffer. | |
284 | /// \return If the blocks in the buffer were decoded without errors | |
285 | /// or we could recover from the errors, then true is returned. | |
286 | /// False is returned otherwise. | |
287 | ||
288 | const UChar_t* current = start; | |
e3a2b9c9 | 289 | |
290 | UInt_t blockCount = 0; // Indicates the number of blocks decoded. | |
291 | while (current < end) | |
292 | { | |
293 | // Mark the start of the block structure. | |
294 | const UChar_t* blockStart = current; | |
295 | ||
296 | // Get the block header, move the current pointer just past the end | |
297 | // of the header and check that we have not overflowed the buffer. | |
298 | const AliMUONBlockHeaderStruct* blockHeader | |
299 | = reinterpret_cast<const AliMUONBlockHeaderStruct*>(blockStart); | |
300 | current += sizeof(AliMUONBlockHeaderStruct); | |
301 | if (current > end) | |
302 | { | |
303 | // So we only got part of a block header at the very end | |
304 | // of the buffer. Nothing to do but report the error and exit. | |
305 | if (blockCount == fMaxBlocks) | |
306 | // Special case where we got all the blocks we | |
307 | // expected, so the remaining data must be rubbish. | |
308 | fHandler.OnError(EventHandler::kBufferTooBig, blockHeader); | |
309 | else | |
310 | fHandler.OnError(EventHandler::kNoBlockHeader, blockHeader); | |
8a0dae7c | 311 | fHadError = true; |
312 | return; | |
e3a2b9c9 | 313 | } |
314 | ||
315 | // The header fits the buffer so we can mark the data start and | |
316 | // read from the header to find the end of data and block pointers. | |
317 | const UChar_t* dataStart = current; | |
318 | current += blockHeader->fLength * sizeof(UInt_t); | |
319 | const UChar_t* dataEnd = current; | |
320 | const UChar_t* blockEnd = blockStart | |
321 | + blockHeader->fTotalLength * sizeof(UInt_t); | |
322 | ||
323 | // Now we need to check for the following things: | |
324 | // 1) Is the end of block or end of data pointer outside the buffer | |
325 | // boundaries. | |
326 | // 2) Are the values for these pointers the same. | |
327 | // 3) Is the expected data key in the header present. | |
328 | // If any of the above fail then we know there is a problem with | |
329 | // the block header. It must be corrupted somehow. | |
330 | if (blockHeader->fDataKey != fgkBlockDataKey | |
331 | or dataEnd > end or blockEnd > end or dataEnd != blockEnd) | |
332 | { | |
333 | // So let us see what exactly is wrong and report this. | |
334 | if (blockCount == fMaxBlocks) | |
335 | { | |
336 | // Special case where we got all the blocks we | |
337 | // expected, so the remaining data must be rubbish. | |
338 | // Don't even bother trying to recover the data. | |
339 | fHandler.OnError(EventHandler::kBufferTooBig, blockHeader); | |
8a0dae7c | 340 | fHadError = true; |
341 | return; | |
e3a2b9c9 | 342 | } |
343 | if (blockHeader->fDataKey != fgkBlockDataKey) | |
344 | fHandler.OnError(EventHandler::kBadBlockKey, &blockHeader->fDataKey); | |
345 | if (blockEnd > end) | |
346 | fHandler.OnError(EventHandler::kBadBlockLength, &blockHeader->fLength); | |
347 | if (dataEnd > end) | |
348 | fHandler.OnError(EventHandler::kBadBlockTotalLength, &blockHeader->fTotalLength); | |
349 | if (dataEnd != blockEnd) | |
350 | fHandler.OnError(EventHandler::kBlockLengthMismatch, blockHeader); | |
351 | ||
352 | // Stop the decoding if so requested by the user, otherwise | |
8a0dae7c | 353 | // remember about the error so that we return false from the |
354 | // Decode() method and continue decoding. | |
355 | fHadError = true; | |
356 | if (fExitOnError) return; | |
e3a2b9c9 | 357 | |
358 | // Try to recover from the corrupt header. | |
359 | RecoverResult result = TryRecoverStruct( | |
360 | fgkBlockDataKey, sizeof(AliMUONBlockHeaderStruct), | |
361 | blockHeader->fTotalLength, blockHeader->fLength, | |
362 | blockStart, end, dataEnd, blockEnd, current | |
363 | ); | |
364 | if (result == kContinueToNextStruct) | |
365 | continue; // Try the next block at 'current'. | |
8a0dae7c | 366 | if (result == kRecoverFailed) return; |
e3a2b9c9 | 367 | } |
368 | ||
369 | // At this point we certainly have a valid block header, so we | |
370 | // need to check if we have more blocks than we expected. If not | |
371 | // then we can indicate we have another block and decode its data. | |
372 | if (++blockCount > fMaxBlocks) | |
373 | { | |
374 | fHandler.OnError(EventHandler::kTooManyBlocks, current); | |
375 | ||
376 | // In this case we stop the decoding because clearly | |
377 | // something is seriously wrong with the data if we are | |
378 | // getting more blocks than expected. | |
8a0dae7c | 379 | fHadError = true; |
380 | return; | |
e3a2b9c9 | 381 | } |
382 | ||
383 | fHandler.OnNewBlock(blockHeader, dataStart); | |
8a0dae7c | 384 | if (not DecodeBlockData(blockHeader, dataStart, dataEnd)) |
385 | { | |
386 | // At this point we had a problem decoding the block structure's | |
387 | // data. Thus we should stop further decoding if so requested by | |
388 | // the user. Note the fHadError flag is already marked inside | |
389 | // DecodeBlockData. | |
390 | if (fExitOnError) | |
391 | { | |
392 | fHandler.OnEndOfBlock(blockHeader, dataStart); | |
393 | return; | |
394 | } | |
395 | } | |
396 | fHandler.OnEndOfBlock(blockHeader, dataStart); | |
e3a2b9c9 | 397 | } |
e3a2b9c9 | 398 | } |
399 | ||
400 | ||
401 | template <class EventHandler> | |
402 | bool AliMUONTrackerDDLDecoder<EventHandler>::DecodeBlockData( | |
403 | const AliMUONBlockHeaderStruct* blockHeader, | |
404 | const UChar_t* start, const UChar_t* end | |
405 | ) | |
406 | { | |
407 | /// This method decodes a block structure's data payload. It unpacks the | |
408 | /// DSP structures contained inside and then for each DSP it calls the | |
409 | /// OnNewDSP method for the event handler to signal the start of each new | |
410 | /// DSP structure. | |
411 | /// \param blockHeader | |
412 | /// \param start This is the pointer to the start of the block | |
413 | /// structure's data. | |
414 | /// \param end This is the pointer to the first byte just past the | |
415 | /// end of the block structure. | |
416 | /// \return If the block structure's data was decoded without errors | |
417 | /// or we could recover from the errors, then true is returned. | |
418 | /// False is returned otherwise. | |
419 | ||
420 | const UChar_t* current = start; | |
421 | ||
422 | UInt_t dspCount = 0; // Indicates the number of DSPs decoded. | |
423 | while (current < end) | |
424 | { | |
425 | // Mark the start of the DSP structure. | |
426 | const UChar_t* dspStart = current; | |
427 | ||
428 | // Get the DSP header, move the current pointer just past the end | |
429 | // of the header and check that we have not overflowed the buffer. | |
430 | const AliMUONDSPHeaderStruct* dspHeader | |
431 | = reinterpret_cast<const AliMUONDSPHeaderStruct*>(dspStart); | |
432 | current += sizeof(AliMUONDSPHeaderStruct); | |
433 | if (current > end) | |
434 | { | |
435 | // So we only got part of a DSP header at the very end of | |
436 | // the block structure buffer. Nothing to do but report the | |
437 | // error and exit. Set fHadError in case of further decoding. | |
438 | fHandler.OnError(EventHandler::kNoDSPHeader, dspHeader); | |
439 | fHadError = true; | |
440 | return false; | |
441 | } | |
442 | ||
443 | // The header fits the buffer so we can mark the data start and | |
444 | // read from the header to find the end of data and DSP pointers. | |
445 | const UChar_t* dataStart = current; | |
446 | current += dspHeader->fLength * sizeof(UInt_t); | |
447 | const UChar_t* dataEnd = current; | |
448 | const UChar_t* dspEnd = dspStart + dspHeader->fTotalLength * sizeof(UInt_t); | |
449 | ||
450 | // Now we need to check for the following things: | |
451 | // 1) Is the end of DSP or end of data pointer outside the buffer | |
452 | // boundaries. | |
453 | // 2) Are the values for these pointers the same. | |
454 | // 3) Is the expected data key in the header present. | |
455 | // If any of the above fail then we know there is a problem with | |
456 | // the DSP header. It must be corrupted somehow. | |
457 | if (dspHeader->fDataKey != fgkDSPDataKey | |
458 | or dataEnd > end or dspEnd > end or dataEnd != dspEnd) | |
459 | { | |
460 | // So let us see what exactly is wrong and report this. | |
461 | if (dspHeader->fDataKey != fgkDSPDataKey) | |
462 | fHandler.OnError(EventHandler::kBadDSPKey, &dspHeader->fDataKey); | |
463 | if (dspEnd > end) | |
464 | fHandler.OnError(EventHandler::kBadDSPLength, &dspHeader->fLength); | |
465 | if (dataEnd > end) | |
466 | fHandler.OnError(EventHandler::kBadDSPTotalLength, &dspHeader->fTotalLength); | |
467 | if (dataEnd != dspEnd) | |
468 | fHandler.OnError(EventHandler::kDSPLengthMismatch, dspHeader); | |
469 | ||
470 | // Indicate we had and error and stop the decoding if so | |
471 | // requested by the user. | |
472 | fHadError = true; | |
473 | if (fExitOnError) return false; | |
474 | ||
475 | // Try to recover from the corrupt header. | |
476 | RecoverResult result = TryRecoverStruct( | |
477 | fgkDSPDataKey, sizeof(AliMUONDSPHeaderStruct), | |
478 | dspHeader->fTotalLength, dspHeader->fLength, | |
479 | dspStart, end, dataEnd, dspEnd, current | |
480 | ); | |
481 | if (result == kContinueToNextStruct) | |
482 | continue; // Try the next DSP at 'current'. | |
483 | if (result == kRecoverFailed) return false; | |
484 | } | |
485 | ||
486 | // At this point we certainly have a valid DSP header, so we | |
487 | // need to check if we have more DSPs than we expected. If not | |
488 | // then we can indicate we have another DSP and decode its data. | |
489 | if (++dspCount > fMaxDSPs) | |
490 | { | |
491 | fHandler.OnError(EventHandler::kTooManyDSPs, current); | |
492 | ||
493 | // In this case we stop further decoding of the block | |
494 | // structure data because clearly something is seriously | |
495 | // wrong if we are getting more DSPs than expected. | |
496 | // Indicate that we had an error so the Decode() method | |
497 | // returns false. | |
498 | fHadError = true; | |
499 | return false; | |
500 | } | |
501 | ||
502 | fHandler.OnNewDSP(dspHeader, dataStart); | |
503 | ||
504 | // Check the error word in the header. | |
505 | if (dspHeader->fErrorWord == (0x000000B1 | blockHeader->fDSPId) | |
506 | or dspHeader->fErrorWord == (0x00000091 | blockHeader->fDSPId) | |
507 | ) | |
508 | { | |
509 | // An event with a glitch in the readout has been detected. | |
510 | // It means that somewhere a 1 byte word has been randomly | |
511 | // inserted and all the readout sequence is shifted until | |
512 | // the next event. | |
513 | fHandler.OnError(EventHandler::kGlitchFound, &dspHeader->fErrorWord); | |
514 | fHadError = true; | |
8a0dae7c | 515 | if (fExitOnError) |
516 | { | |
517 | fHandler.OnEndOfDSP(dspHeader, dataStart); | |
518 | return false; | |
519 | } | |
e3a2b9c9 | 520 | |
521 | // Try recover by finding the very next DSP and continue | |
522 | // decoding from there. Note: to achieve all we have to do | |
523 | // is continue to the next iteration, because the logic | |
524 | // will land up calling the FindKey method within the | |
525 | // TryRecoverStruct method above. | |
526 | if (fTryRecover) continue; | |
527 | } | |
528 | ||
529 | // Check if we are padding. If we are, then the bus patch data is | |
530 | // actually 4 bytes smaller and the last word is a padding word. | |
531 | if (dspHeader->fPaddingWord == 1) | |
532 | { | |
533 | dataEnd -= sizeof(UInt_t); | |
534 | ||
535 | // Check the pad word is correct. | |
536 | const UInt_t* padWord = reinterpret_cast<const UInt_t*>(dataEnd); | |
537 | if (*padWord != fgkPaddingWord) | |
538 | { | |
539 | fHandler.OnError(EventHandler::kBadPaddingWord, padWord); | |
540 | fHadError = true; | |
8a0dae7c | 541 | if (fExitOnError) |
542 | { | |
543 | fHandler.OnEndOfDSP(dspHeader, dataStart); | |
544 | return false; | |
545 | } | |
e3a2b9c9 | 546 | } |
547 | } | |
548 | ||
8a0dae7c | 549 | if (not DecodeDSPData(dataStart, dataEnd)) |
550 | { | |
551 | // At this point we had a problem decoding the DSP structure's | |
552 | // data, thus we should stop further decoding if so requested by | |
553 | // the user. Note the fHadError flag is already marked inside | |
554 | // DecodeDSPData. | |
555 | if (fExitOnError) | |
556 | { | |
557 | fHandler.OnEndOfDSP(dspHeader, dataStart); | |
558 | return false; | |
559 | } | |
560 | } | |
561 | fHandler.OnEndOfDSP(dspHeader, dataStart); | |
e3a2b9c9 | 562 | } |
563 | ||
564 | return true; | |
565 | } | |
566 | ||
567 | ||
568 | template <class EventHandler> | |
569 | bool AliMUONTrackerDDLDecoder<EventHandler>::DecodeDSPData( | |
570 | const UChar_t* start, const UChar_t* end | |
571 | ) | |
572 | { | |
573 | /// This method decodes a DSP structure's data payload. It finds all the | |
574 | /// bus patches found inside and for each it calls the OnNewBusPatch method | |
575 | /// for the event handler to signal the start of each new bus patch. | |
576 | /// \param start This is the pointer to the start of the DSP structure's data. | |
577 | /// \param end This is the pointer to the first byte just past the | |
578 | /// end of the DSP structure. | |
579 | /// \return If the DSP structure's data was decoded without errors | |
580 | /// or we could recover from the errors, then true is returned. | |
581 | /// False is returned otherwise. | |
582 | ||
583 | const UChar_t* current = start; | |
584 | ||
585 | UInt_t busPatchCount = 0; // Indicates the number of bus patches decoded. | |
586 | while (current < end) | |
587 | { | |
588 | // Mark the start of the bus patch structure. | |
589 | const UChar_t* busPatchStart = current; | |
590 | ||
591 | // Get the bus patch header, move the current pointer just past | |
592 | // the end of the header and check that we have not overflowed | |
593 | // the buffer. | |
594 | const AliMUONBusPatchHeaderStruct* busPatchHeader | |
595 | = reinterpret_cast<const AliMUONBusPatchHeaderStruct*>(busPatchStart); | |
596 | current += sizeof(AliMUONBusPatchHeaderStruct); | |
597 | if (current > end) | |
598 | { | |
599 | // So we only got part of a bus patch header at the very | |
600 | // end of the DSP structure buffer. Nothing to do but | |
601 | // report the error and exit. Set fHadError in case of | |
602 | // further decoding. | |
603 | fHandler.OnError(EventHandler::kNoBusPatchHeader, busPatchHeader); | |
604 | fHadError = true; | |
605 | return false; | |
606 | } | |
607 | ||
608 | // The header fits the buffer so we can mark the data start and | |
609 | // read from the header to find the end of data and bus patch | |
610 | // structure pointers. | |
611 | const UChar_t* dataStart = current; | |
612 | current += busPatchHeader->fLength * sizeof(UInt_t); | |
613 | const UChar_t* dataEnd = current; | |
614 | const UChar_t* busPatchEnd = busPatchStart | |
615 | + busPatchHeader->fTotalLength * sizeof(UInt_t); | |
616 | ||
617 | // Now we need to check for the following things: | |
618 | // 1) Is the end of bus patch structure or end of data pointer | |
619 | // outside the buffer boundaries. | |
620 | // 2) Are the values for these pointers the same. | |
621 | // 3) Is the expected data key in the header present. | |
622 | // If any of the above fail then we know there is a problem with | |
623 | // the bus patch header. It must be corrupted somehow. | |
624 | if (busPatchHeader->fDataKey != fgkBusPatchDataKey | |
625 | or dataEnd > end or busPatchEnd > end or dataEnd != busPatchEnd) | |
626 | { | |
627 | // So let us see what exactly is wrong and report this. | |
628 | if (busPatchHeader->fDataKey != fgkBusPatchDataKey) | |
629 | fHandler.OnError(EventHandler::kBadBusPatchKey, &busPatchHeader->fDataKey); | |
630 | if (busPatchEnd > end) | |
631 | fHandler.OnError(EventHandler::kBadBusPatchLength, &busPatchHeader->fLength); | |
632 | if (dataEnd > end) | |
633 | fHandler.OnError(EventHandler::kBadBusPatchTotalLength, &busPatchHeader->fTotalLength); | |
634 | if (dataEnd != busPatchEnd) | |
635 | fHandler.OnError(EventHandler::kBusPatchLengthMismatch, busPatchHeader); | |
636 | ||
637 | // Indicate we had and error and stop the decoding if so | |
638 | // requested by the user. | |
639 | fHadError = true; | |
640 | if (fExitOnError) return false; | |
641 | ||
642 | // Try to recover from the corrupt header. | |
643 | RecoverResult result = TryRecoverStruct( | |
644 | fgkBusPatchDataKey, sizeof(AliMUONBusPatchHeaderStruct), | |
645 | busPatchHeader->fTotalLength, busPatchHeader->fLength, | |
646 | busPatchStart, end, dataEnd, busPatchEnd, current | |
647 | ); | |
648 | if (result == kContinueToNextStruct) | |
649 | continue; // Try the next bus patch at 'current'. | |
650 | if (result == kRecoverFailed) return false; | |
651 | } | |
652 | ||
653 | // At this point we certainly have a valid bus patch header, so | |
654 | // we need to check if we have more bus patches than we expected. | |
655 | // If not then we can indicate we have another bus patch and | |
656 | // decode its data. | |
657 | if (++busPatchCount > fMaxBusPatches) | |
658 | { | |
659 | fHandler.OnError(EventHandler::kTooManyBusPatches, current); | |
660 | ||
661 | // In this case we stop further decoding of the DSP | |
662 | // structure's data because clearly something is seriously | |
663 | // wrong if we are getting more bus patches than expected. | |
664 | // Indicate that we had an error so the Decode() method | |
665 | // returns false. | |
666 | fHadError = true; | |
667 | return false; | |
668 | } | |
669 | ||
670 | fHandler.OnNewBusPatch(busPatchHeader, dataStart); | |
8a0dae7c | 671 | if (not DecodeBusPatchData(dataStart, dataEnd)) |
672 | { | |
673 | // At this point we had a problem decoding the bus patch data, | |
674 | // thus we should stop further decoding if so requested by the | |
675 | // user. Note the fHadError flag is already marked inside | |
676 | // DecodeBusPatchData. | |
677 | if (fExitOnError) | |
678 | { | |
679 | fHandler.OnEndOfBusPatch(busPatchHeader, dataStart); | |
680 | return false; | |
681 | } | |
682 | } | |
683 | fHandler.OnEndOfBusPatch(busPatchHeader, dataStart); | |
e3a2b9c9 | 684 | } |
685 | ||
686 | return true; | |
687 | } | |
688 | ||
689 | ||
690 | template <class EventHandler> | |
691 | bool AliMUONTrackerDDLDecoder<EventHandler>::DecodeBusPatchData( | |
692 | const UChar_t* start, const UChar_t* end | |
693 | ) | |
694 | { | |
695 | /// This method decodes a single bus patch's data payload. | |
696 | /// It will check the parity of the raw data words and send them | |
697 | /// to the event handler instance with calls to OnData. | |
698 | /// \param start This is the pointer to the start of the bus patch | |
699 | /// structure's data. | |
700 | /// \param end This is the pointer to the first byte just past the | |
701 | /// end of the bus patch structure. | |
702 | /// \return If the bus patch's data was decoded without errors | |
703 | /// or we could recover from the errors, then true is returned. | |
704 | /// False is returned otherwise. | |
705 | ||
706 | // Assert that 'end' is always larger than start by n*sizeof(UInt_t) | |
707 | // where n is a positive integer. This should be the case because we | |
708 | // always add multiples of sizeof(UInt_t) to the 'current' pointer in | |
709 | // all the DecodeXYZ methods. | |
710 | assert( UInt_t(end - start) % 4 == 0 ); | |
711 | ||
712 | // Now step through all the data words and issue OnData events. | |
713 | // We also need to check parity and signal OnError if it is not valid | |
714 | // for any of the data words. | |
715 | const UInt_t* data = reinterpret_cast<const UInt_t*>(start); | |
716 | const UInt_t* dataEnd = reinterpret_cast<const UInt_t*>(end); | |
717 | for (; data < dataEnd; data++) | |
718 | { | |
719 | if (ParityIsOk(*data)) | |
720 | { | |
8a0dae7c | 721 | fHandler.OnData(*data, false); |
e3a2b9c9 | 722 | } |
723 | else | |
724 | { | |
725 | // Indicate we had a parity error and exit immediately | |
726 | // if the user so requested. | |
727 | fHandler.OnError(EventHandler::kParityError, data); | |
728 | fHadError = true; | |
729 | if (fExitOnError) return false; | |
730 | ||
731 | if (fSendDataOnParityError) | |
8a0dae7c | 732 | fHandler.OnData(*data, true); |
e3a2b9c9 | 733 | } |
734 | } | |
735 | ||
736 | return true; | |
737 | } | |
738 | ||
739 | ||
740 | template <class EventHandler> | |
741 | typename AliMUONTrackerDDLDecoder<EventHandler>::RecoverResult | |
742 | AliMUONTrackerDDLDecoder<EventHandler>::TryRecoverStruct( | |
743 | UInt_t expectedKey, | |
744 | UInt_t headerSize, | |
745 | UInt_t totalLength, | |
746 | UInt_t length, | |
747 | const UChar_t* structStart, | |
748 | const UChar_t* bufferEnd, | |
749 | const UChar_t*& dataEnd, | |
750 | const UChar_t*& structEnd, | |
751 | const UChar_t*& current | |
752 | ) | |
753 | { | |
754 | /// This method attempts to recover from a corrupt structure header by | |
755 | /// figuring out which of the structure size indicators is correct. | |
756 | /// This is possible because each header has some redundant information. | |
757 | /// The recovery procedure is only attempted if fTryRecover was set to | |
758 | /// true. If the recovery procedure is successful then this method will | |
759 | /// also update the pointers indicating the start of data, end of structure | |
760 | /// and current parsing position with the correct values. | |
761 | /// | |
762 | /// [in] \param expectedKey This is the expected block key for the header | |
763 | /// currently being processed. | |
764 | /// [in] \param headerSize The expected header size as given by the sizeof | |
765 | /// operator for example. | |
766 | /// [in] \param totalLength The total length as given by the fTotalLength | |
767 | /// field in the current header being handled. | |
768 | /// [in] \param length The data length as given by the fLength field | |
769 | /// in the current header being handled. | |
770 | /// [in] \param structStart A pointer to the start of the structure header. | |
771 | /// [in] \param bufferEnd A pointer to the first byte just past the end | |
772 | /// of the buffer. This could be the pointer to the first byte | |
773 | /// just past the end of the parent structure if we are dealing | |
774 | /// with a DSP structure or bus patch. The parent structure for | |
775 | /// the DSP is a block structure and for a bus patch it is a DSP. | |
776 | /// [out] \param dataEnd This is the pointer to the first byte just past | |
777 | /// the end of the structure being processed. It should be equal to | |
778 | /// structStart + sizeof(structure header) + fLength, where fLength | |
779 | /// is the field found in the structure's header itself. This value | |
780 | /// will be corrected and updated if we could recover from the | |
781 | /// corruption in the header. | |
782 | /// [out] \param structEnd A pointer to the first byte just past the end of | |
783 | /// the structure. This value should be set equal to | |
784 | /// structStart + fTotalLength * sizeof(UInt_t), where fTotalLength | |
785 | /// is the field found in the structure's header itself. This value | |
786 | /// will be corrected and updated if we could recover from the | |
787 | /// corruption in the header. | |
788 | /// [out] \param current This is the pointer to the current location in | |
789 | /// the DDL payload being parsed. It should in principle point | |
790 | /// to the start of the structures data. This value will be | |
791 | /// corrected and updated if we could recover from the corruption | |
792 | /// in the header. | |
793 | /// | |
794 | /// \return Returns the result of the recovery attempt, which can be one | |
795 | /// of the following: | |
796 | /// kRecoverFailed - The recovery failed completely so the caller | |
797 | /// cannot continue parsing any more structures. If the failure | |
798 | /// is within a DSP then one could still continue parsing | |
799 | /// from the next block. Similarly for bus patches, parsing could | |
800 | /// continue from the next DSP structure. | |
801 | /// kStructRecovered - Indicates that we recovered from a corrupt | |
802 | /// structure header and can continue processing the data of the | |
803 | /// structure in question. | |
804 | /// kContinueToNextStruct - Either fTryRecover was set to false or we | |
805 | /// could not recover from the corrupt header but we did find the | |
806 | /// start of another header matching the expected key so parsing | |
807 | /// can continue from the updated current position. | |
808 | ||
809 | // Check if the user wants us to try and recover from a corrupt header. | |
810 | if (not fTryRecover) return kContinueToNextStruct; | |
811 | ||
812 | // If the user wants us to try recover, then try to recover what the | |
813 | // correct values for dataEnd, structEnd and current were supposed to be. | |
814 | // The recovery procedure is as follows: We have 4 conditions for a correct | |
815 | // header: | |
816 | // 1) The header key is what we expect. | |
817 | // 2) The totalLength equals length + headerSize. | |
818 | // 3) The word at dataEnd contains a valid key. (implies length is | |
819 | // correct.) | |
820 | // 4) The word at structEnd contains a valid key. (implies totalLength | |
821 | // is correct.) | |
822 | // If any 2 of these conditions hold then we know that only one of the | |
823 | // header fields is corrupt and we have enough information to reconstruct | |
824 | // the third field. Note that if conditions 3 and 4 are true then this | |
825 | // implies 2 is also true. (not necessarily the other way around though.) | |
826 | // The valid key mentioned above at dataEnd and structEnd should be: | |
827 | // a) A bus patch key, DSP key or end of buffer if expectedKey indicates | |
828 | // a buspatch. | |
829 | // b) A DSP key, block structure key or end of buffer if expectedKey | |
830 | // indicates a DSP. | |
831 | // c) A block structure key or end of buffer if expectedKey indicates | |
832 | // a DSP. | |
833 | const UInt_t* headerKey = reinterpret_cast<const UInt_t*>(structStart); | |
834 | bool headerKeyOk = (expectedKey == *headerKey); | |
835 | ||
836 | bool lengthsMatch = (totalLength == length + headerSize); | |
837 | ||
838 | bool lengthIsCorrect = false; | |
839 | bool totalLengthIsCorrect = false; | |
840 | const UInt_t* keyAtDataEnd = reinterpret_cast<const UInt_t*>(dataEnd); | |
841 | const UInt_t* keyAtStructEnd = reinterpret_cast<const UInt_t*>(structEnd); | |
842 | ||
a0fea27d | 843 | |
844 | if ( expectedKey == fgkBlockDataKey ) | |
845 | { | |
e3a2b9c9 | 846 | if (dataEnd == bufferEnd) |
847 | { | |
848 | // Are we at the end of the buffer? | |
849 | lengthIsCorrect = true; | |
850 | } | |
851 | else | |
852 | { | |
853 | // Must check that we can read another 4 bytes before | |
854 | // checking the key at dataEnd. | |
855 | if (dataEnd + sizeof(UInt_t) <= bufferEnd) | |
856 | { | |
857 | if (*keyAtDataEnd == fgkBlockDataKey) | |
858 | lengthIsCorrect = true; | |
859 | } | |
860 | } | |
861 | ||
862 | if (structEnd == bufferEnd) | |
863 | { | |
864 | // Are we at the end of the buffer? | |
865 | totalLengthIsCorrect = true; | |
866 | } | |
867 | else | |
868 | { | |
869 | // Must check that we can read another 4 bytes before | |
870 | // checking the key at structEnd. | |
871 | if (structEnd + sizeof(UInt_t) <= bufferEnd) | |
872 | { | |
873 | if (*keyAtStructEnd == fgkBlockDataKey) | |
874 | totalLengthIsCorrect = true; | |
875 | } | |
876 | } | |
a0fea27d | 877 | } |
e3a2b9c9 | 878 | |
a0fea27d | 879 | else if ( expectedKey == fgkDSPDataKey ) |
880 | { | |
e3a2b9c9 | 881 | if (dataEnd == bufferEnd) |
882 | { | |
883 | // Are we at the end of the buffer? | |
884 | lengthIsCorrect = true; | |
885 | } | |
886 | else | |
887 | { | |
888 | // Must check that we can read another 4 bytes before | |
889 | // checking the key at dataEnd. | |
890 | if (dataEnd + sizeof(UInt_t) <= bufferEnd) | |
891 | { | |
892 | if (*keyAtDataEnd == fgkBlockDataKey | |
893 | or *keyAtDataEnd == fgkDSPDataKey) | |
894 | lengthIsCorrect = true; | |
895 | } | |
896 | } | |
897 | ||
898 | if (structEnd == bufferEnd) | |
899 | { | |
900 | // Are we at the end of the buffer? | |
901 | totalLengthIsCorrect = true; | |
902 | } | |
903 | else | |
904 | { | |
905 | // Must check that we can read another 4 bytes before | |
906 | // checking the key at structEnd. | |
907 | if (structEnd + sizeof(UInt_t) <= bufferEnd) | |
908 | { | |
909 | if (*keyAtStructEnd == fgkBlockDataKey | |
910 | or *keyAtStructEnd == fgkDSPDataKey) | |
911 | totalLengthIsCorrect = true; | |
912 | } | |
913 | } | |
a0fea27d | 914 | } |
915 | else if ( expectedKey == fgkBusPatchDataKey ) | |
916 | { | |
e3a2b9c9 | 917 | if (dataEnd == bufferEnd) |
918 | { | |
919 | // Are we at the end of the buffer? | |
920 | lengthIsCorrect = true; | |
921 | } | |
922 | else | |
923 | { | |
924 | // Must check that we can read another 4 bytes before | |
925 | // checking the key at dataEnd. | |
926 | if (dataEnd + sizeof(UInt_t) <= bufferEnd) | |
927 | { | |
928 | if (*keyAtDataEnd == fgkDSPDataKey | |
929 | or *keyAtDataEnd == fgkBusPatchDataKey) | |
930 | lengthIsCorrect = true; | |
931 | } | |
932 | } | |
933 | ||
934 | if (structEnd == bufferEnd) | |
935 | { | |
936 | // Are we at the end of the buffer? | |
937 | totalLengthIsCorrect = true; | |
938 | } | |
939 | else | |
940 | { | |
941 | // Must check that we can read another 4 bytes before | |
942 | // checking the key at structEnd. | |
943 | if (structEnd + sizeof(UInt_t) <= bufferEnd) | |
944 | { | |
945 | if (*keyAtStructEnd == fgkDSPDataKey | |
946 | or *keyAtStructEnd == fgkBusPatchDataKey) | |
947 | totalLengthIsCorrect = true; | |
948 | } | |
949 | } | |
a0fea27d | 950 | } |
e3a2b9c9 | 951 | |
952 | if (headerKeyOk and lengthIsCorrect) | |
953 | { | |
954 | // totalLength was wrong, dataEnd is correct. | |
955 | structEnd = dataEnd; | |
956 | current = dataEnd; | |
957 | return kStructRecovered; | |
958 | } | |
959 | if (headerKeyOk and totalLengthIsCorrect) | |
960 | { | |
961 | // Length was wrong, structEnd is correct. | |
962 | dataEnd = structEnd; | |
963 | current = structEnd; | |
964 | return kStructRecovered; | |
965 | } | |
966 | if (lengthsMatch and lengthIsCorrect and totalLengthIsCorrect) | |
967 | { | |
968 | // The header's key was wrong but the lengths and pointers are OK. | |
969 | return kStructRecovered; | |
970 | } | |
971 | ||
972 | // Could not recover the header from the available information, so find | |
973 | // the next key in the stream that is the same as the currently expected | |
974 | // one and continue decoding from there. | |
975 | const UChar_t* location = FindKey( | |
976 | expectedKey, structStart + sizeof(UInt_t), bufferEnd | |
977 | ); | |
978 | if (location != NULL) | |
979 | { | |
980 | current = location; | |
981 | return kContinueToNextStruct; | |
982 | } | |
983 | ||
984 | return kRecoverFailed; | |
985 | } | |
986 | ||
987 | ||
988 | template <class EventHandler> | |
989 | const UChar_t* AliMUONTrackerDDLDecoder<EventHandler>::FindKey( | |
990 | UInt_t key, const UChar_t* start, const UChar_t* end | |
991 | ) | |
992 | { | |
993 | /// Searches for the first occurrence of the key value in the buffer marked by | |
994 | /// 'start' and 'end'. 'start' should point to the start of the buffer and 'end' | |
995 | /// should point to 'start + bufferSize', i.e. just past the last byte of the | |
996 | /// buffer. If the key was found then the pointer to that location is returned | |
997 | /// otherwise NULL is returned. | |
998 | ||
999 | const UChar_t* current = start; | |
1000 | while (current + sizeof(UInt_t) <= end) | |
1001 | { | |
1002 | UInt_t data = * reinterpret_cast<const UInt_t*>(current); | |
1003 | if (data == key) return current; | |
1004 | current++; | |
1005 | } | |
1006 | return NULL; | |
1007 | } | |
1008 | ||
1009 | ||
1010 | template <class EventHandler> | |
1011 | bool AliMUONTrackerDDLDecoder<EventHandler>::ParityIsOk(UInt_t data) | |
1012 | { | |
1013 | /// Optimised parity check addapted from: | |
1014 | /// http://graphics.stanford.edu/~seander/bithacks.html#ParityParallel | |
1015 | ||
1016 | // parity of the 32 bits must be zero if the last bit is equal | |
1017 | // to the parity of the first 31 bits. | |
1018 | // Reason: the parity bit xor the parity of the first 31 bits must give | |
1019 | // zero, unless there was a bit error. | |
1020 | data ^= data >> 16; | |
1021 | data ^= data >> 8; | |
1022 | data ^= data >> 4; | |
1023 | data &= 0xf; | |
1024 | data = ((0x6996 >> data) & 1); | |
1025 | return data == 0; | |
1026 | } | |
1027 | ||
1028 | #endif // ALIMUONTRACKERDDLDECODER_H |