001/* Encode an input sequence with a convolutional code. 002 003 Copyright (c) 2003-2015 The Regents of the University of California. 004 All rights reserved. 005 Permission is hereby granted, without written agreement and without 006 license or royalty fees, to use, copy, modify, and distribute this 007 software and its documentation for any purpose, provided that the above 008 copyright notice and the following two paragraphs appear in all copies 009 of this software. 010 011 IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY 012 FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES 013 ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF 014 THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF 015 SUCH DAMAGE. 016 017 THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, 018 INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 019 MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE 020 PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF 021 CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, 022 ENHANCEMENTS, OR MODIFICATIONS. 023 024 PT_COPYRIGHT_VERSION_2 025 COPYRIGHTENDKEY 026 027 */ 028package ptolemy.actor.lib.comm; 029 030import ptolemy.actor.lib.Transformer; 031import ptolemy.data.ArrayToken; 032import ptolemy.data.BooleanToken; 033import ptolemy.data.IntToken; 034import ptolemy.data.Token; 035import ptolemy.data.expr.Parameter; 036import ptolemy.data.type.ArrayType; 037import ptolemy.data.type.BaseType; 038import ptolemy.kernel.CompositeEntity; 039import ptolemy.kernel.util.Attribute; 040import ptolemy.kernel.util.IllegalActionException; 041import ptolemy.kernel.util.NameDuplicationException; 042import ptolemy.kernel.util.Workspace; 043 044/////////////////////////////////////////////////////////////////// 045//// ConvolutionalCoder 046 047/** 048 Encode an input sequence with a convolutional code. The inputs and 049 outputs are booleans. 050 051 <p>The input sequence 052 enters a shift register, and the contents of the shift register are 053 combined using boolean functions given by the <i>polynomialArray</i> 054 parameter. The initial state of the shift register is given by the 055 <i>initialState</i> parameter, which should be a non-negative integer. 056 The <i>uncodedRate</i> parameter, often denoted by <i>k</i> in the 057 coding literature, is the number of bits per firing that are shifted 058 into the shift register. The <i>polynomialArray</i> parameter is an 059 array of positive integers. Each integer indicates one polynomial 060 used for computing output bits. To get a <i>k</i>/<i>n</i> 061 convolutional code, set <i>uncodedRate</i> to <i>k</i> and provide 062 <i>n</i> integers in <i>polynomialArray</i>.</p> 063 064 <p> The integers in <i>polynomialArray</i> are usually most conveniently 065 given as octal numbers. A leading zero indicates an octal 066 number. The <i>i</i>-th bit of the integer indicates whether the 067 <i>i</i>-th tap of the delay line should be used. All bits that 068 are used are exclusive-ored, thus yielding the parity of the selected 069 bits. See more details in Scrambler actor on using an integer to 070 define a polynomial. The <i>n</i> parity results are produced on 071 the output in a sequence.</p> 072 073 <p> A good convolutional code should have large Hamming distance between 074 any two of its codewords. This is not easily checked, but there are some 075 simple rules that all "good" codes should satisfy:</p> 076 <ol> 077 <li> <i>k</i> should be strictly smaller than <i>n</i>, otherwise 078 the code is not uniquely decodable. Thus, <i>uncodedRate</i> 079 should be less than the length of <i>polynomialArray</i>.</li> 080 <li> <i>k</i> should not be higher than the highest order of 081 all polynomials, otherwise, some input bits never get 082 involved in computing parities.</li> 083 </ol> 084 085 <p>If these rules are violated, the actor will throw an exception. 086 However, these rules do not guarantee the codeword can be decoded 087 successfully, and it is not always true that larger polynomials 088 yield better codes. Users should check tables for convolutional 089 codes from professional references. For convenience, we list here 090 some convolutional codes that have large distance property.</p> 091 <pre> 092 Rate = 1/2 093 polynomialArray 094 {05, 07} 095 {013, 017} 096 {031, 027} 097 {065, 057} 098 {0155, 0117} 099 100 Rate = 1/3 101 polynomialArray 102 {05, 07, 07} 103 {015, 013, 017} 104 {025, 033, 037} 105 {071, 065, 057} 106 {0155, 0123, 0137} 107 108 Rate = 1/4 109 polynomialArray 110 {05, 07, 07, 07} 111 {015, 013, 013, 017} 112 {025, 035, 033, 037} 113 {065, 073, 047, 057} 114 {0135, 0135, 0163, 0147} 115 116 Rate = 1/5 117 polynomialArray 118 {07, 07, 07, 05, 05} 119 {017, 017, 015, 013, 013} 120 {037, 035, 033, 025, 027} 121 {057, 047, 067, 053, 075} 122 123 Rate = 1/6 124 polynomialArray 125 {07, 07, 07, 07, 05, 05} 126 {017, 017, 015, 015, 013, 013} 127 {037, 027, 035, 033, 025, 027} 128 {067, 057, 055, 053, 071, 075} 129 130 Rate = 2/3 131 polynomialArray 132 {017, 06, 013} 133 {072, 057, 027} 134 {0171, 0166, 0273} 135 136 Rate = k/5 137 k polynomialArray 138 2 {017, 016, 011, 05, 02} 139 2 {072, 047, 025, 053, 075} 140 3 {056, 062, 057, 043, 071} 141 142 Rate = k/7 143 k polynomialArray 144 2 {012, 06, 05, 013, 013, 015, 017} 145 2 {066, 055, 027, 071, 052, 056, 057} 146 3 {051, 042, 036, 023, 075, 061, 047} 147 148 Rate polynomialArray 149 3/4 {064, 052, 043, 071} 150 3/8 {054, 021, 062, 043, 045, 036, 057, 071} 151 </pre> 152 153 <p> Note that this implementation is limited to a shift register 154 length of 32 because of the specification of the polynomials and 155 initial shift register state as 32-bit integers.</p> 156 157 <p>For more information on convolutional codes, see Proakis, Digital 158 Communications, Fourth Edition, McGraw-Hill, 2001, pp. 471-477, 159 or Barry, Lee and Messerschmitt, <i>Digital Communication</i>, Third Edition, 160 Kluwer, 2004.</p> 161 162 @author Ye Zhou, contributor: Edward A. Lee 163 @version $Id$ 164 @since Ptolemy II 3.0 165 @Pt.ProposedRating Yellow (eal) 166 @Pt.AcceptedRating Red (cxh) 167 @see Scrambler 168 @see ViterbiDecoder 169 */ 170public class ConvolutionalCoder extends Transformer { 171 /** Construct an actor with the given container and name. 172 * The output and trigger ports are also constructed. 173 * @param container The container. 174 * @param name The name of this actor. 175 * @exception IllegalActionException If the entity cannot be contained 176 * by the proposed container. 177 * @exception NameDuplicationException If the container already has an 178 * actor with this name. 179 */ 180 public ConvolutionalCoder(CompositeEntity container, String name) 181 throws NameDuplicationException, IllegalActionException { 182 super(container, name); 183 184 uncodedRate = new Parameter(this, "uncodedRate"); 185 uncodedRate.setTypeEquals(BaseType.INT); 186 uncodedRate.setExpression("1"); 187 188 polynomialArray = new Parameter(this, "polynomialArray"); 189 polynomialArray.setTypeEquals(new ArrayType(BaseType.INT)); 190 polynomialArray.setExpression("{05, 07}"); 191 192 initialState = new Parameter(this, "initialState"); 193 initialState.setTypeEquals(BaseType.INT); 194 initialState.setExpression("0"); 195 196 // Declare data types, consumption rate and production rate. 197 input.setTypeEquals(BaseType.BOOLEAN); 198 _inputRate = new Parameter(input, "tokenConsumptionRate"); 199 _inputRate.setExpression("1"); 200 output.setTypeEquals(BaseType.BOOLEAN); 201 _outputRate = new Parameter(output, "tokenProductionRate"); 202 _outputRate.setExpression("1"); 203 } 204 205 /////////////////////////////////////////////////////////////////// 206 //// ports and parameters //// 207 208 /** An array of integers defining an array of polynomials with 209 * binary coefficients. The coefficients indicate the presence (1) 210 * or absence (0) of a tap in the shift register. Each element 211 * of this array parameter should be a positive integer. 212 * The default value is {05, 07}. 213 */ 214 public Parameter polynomialArray; 215 216 /** Integer defining the initial state of the shift register. 217 * The i-th bit of the integer indicates the value of the 218 * i-th register. This parameter should be a non-negative 219 * integer. Its default value is the integer 0. 220 */ 221 public Parameter initialState; 222 223 /** Integer defining the number of bits that the shift register 224 * takes in each firing. It should be a positive integer. Its 225 * default value is the integer 1. 226 */ 227 public Parameter uncodedRate; 228 229 /////////////////////////////////////////////////////////////////// 230 //// public methods //// 231 232 /** If the attribute being changed is <i>uncodedRate</i>, 233 * then verify that it is a positive integer; if it is 234 * <i>polynomialArray</i>, then verify that each of its elements is 235 * a positive integer and find the maximum value among them, which 236 * is used to compute the highest order among all polynomials. 237 * @param attribute The attribute that changed. 238 * @exception IllegalActionException If <i>uncodedRate</i> is 239 * non-positive or any element of <i>polynomialArray</i> is non-positive. 240 */ 241 @Override 242 public void attributeChanged(Attribute attribute) 243 throws IllegalActionException { 244 if (attribute == uncodedRate) { 245 _inputNumber = ((IntToken) uncodedRate.getToken()).intValue(); 246 247 if (_inputNumber < 1) { 248 throw new IllegalActionException(this, 249 "inputLength must be non-negative."); 250 } 251 252 // Set a flag indicating the private variable 253 // _inputNumber is invalid, but do not compute 254 // the value until all parameters have been set. 255 _inputNumberInvalid = true; 256 257 // Set the input consumption rate. 258 _inputRate.setToken(new IntToken(_inputNumber)); 259 } else if (attribute == polynomialArray) { 260 ArrayToken maskToken = (ArrayToken) polynomialArray.getToken(); 261 _maskNumber = maskToken.length(); 262 _mask = new int[_maskNumber]; 263 _maxPolyValue = 0; 264 265 for (int i = 0; i < _maskNumber; i++) { 266 _mask[i] = ((IntToken) maskToken.getElement(i)).intValue(); 267 268 if (_mask[i] <= 0) { 269 throw new IllegalActionException(this, 270 "Polynomial is required to be strictly positive."); 271 } 272 273 if (_mask[i] > _maxPolyValue) { 274 _maxPolyValue = _mask[i]; 275 } 276 } 277 278 _inputNumberInvalid = true; 279 280 // Set the output production rate. 281 _outputRate.setToken(new IntToken(_maskNumber)); 282 } else { 283 super.attributeChanged(attribute); 284 } 285 } 286 287 /** Clone the actor into the specified workspace. 288 * @param workspace The workspace for the new object. 289 * @return A new actor. 290 * @exception CloneNotSupportedException If a derived class contains 291 * an attribute that cannot be cloned. 292 */ 293 @Override 294 public Object clone(Workspace workspace) throws CloneNotSupportedException { 295 ConvolutionalCoder newObject = (ConvolutionalCoder) super.clone( 296 workspace); 297 298 newObject._inputRate = (Parameter) newObject.input 299 .getAttribute("tokenConsumptionRate"); 300 newObject._mask = new int[newObject._maskNumber]; 301 newObject._outputRate = (Parameter) newObject.output 302 .getAttribute("tokenProductionRate"); 303 304 return newObject; 305 } 306 307 /** Read <i>uncodedRate</i> bits from the input port and shift 308 * them into the shift register. Compute the parity for each 309 * polynomial specified in <i>polynomialArray</i>. Send the results 310 * in sequence to the output. The i-th bit in the output 311 * corresponds to the parity computed using the i-th polynomial. 312 */ 313 @Override 314 public void fire() throws IllegalActionException { 315 super.fire(); 316 if (_inputNumberInvalid) { 317 if (_inputNumber >= _maskNumber) { 318 throw new IllegalActionException(this, 319 "Output rate should be larger than input rate."); 320 } 321 322 if (1 << _inputNumber > _maxPolyValue) { 323 throw new IllegalActionException(this, 324 "The highest order of all polynomials is too low."); 325 } 326 327 _inputNumberInvalid = false; 328 } 329 330 _latestShiftReg = _shiftReg; 331 332 // Read from the input port and shift the bits into 333 // the shift register. 334 Token[] inputToken = input.get(0, _inputNumber); 335 int reg = _latestShiftReg; 336 337 for (int i = 0; i < _inputNumber; i++) { 338 reg = reg << 1; 339 340 BooleanToken input = (BooleanToken) inputToken[i]; 341 reg = reg | (input.booleanValue() ? 1 : 0); 342 } 343 344 _latestShiftReg = reg; 345 346 // Compute the parities for all polynomials respectively. 347 BooleanToken[] result = new BooleanToken[_maskNumber]; 348 int[] parity; /*= new int[_maskNumber];*/ 349 parity = _calculateParity(_mask, _maskNumber, reg); 350 351 // Send the parity results to the output. 352 for (int i = 0; i < _maskNumber; i++) { 353 if (parity[i] == 1) { 354 result[i] = BooleanToken.TRUE; 355 } else { 356 result[i] = BooleanToken.FALSE; 357 } 358 } 359 360 output.broadcast(result, result.length); 361 } 362 363 /** Initialize the actor by resetting the shift register state 364 * equal to the value of <i>initialState</i>. 365 * @exception IllegalActionException If the parent class throws it. 366 */ 367 @Override 368 public void initialize() throws IllegalActionException { 369 super.initialize(); 370 _latestShiftReg = _shiftReg = ((IntToken) initialState.getToken()) 371 .intValue(); 372 } 373 374 /** Record the most recent shift register state as the new 375 * state for the next iteration. 376 * @exception IllegalActionException If the base class throws it 377 */ 378 @Override 379 public boolean postfire() throws IllegalActionException { 380 _shiftReg = _latestShiftReg; 381 return super.postfire(); 382 } 383 384 /////////////////////////////////////////////////////////////////// 385 //// private methods //// 386 387 /** Calculate the parities given by the polynomial array 388 * and the state of shift register. 389 * @param mask The polynomial array. 390 * @param reg State of shift register. 391 * @return Parities stored in an array. 392 */ 393 private int[] _calculateParity(int[] mask, int maskNumber, int reg) { 394 int[] parity = new int[maskNumber]; 395 396 for (int i = 0; i < maskNumber; i++) { 397 int masked = mask[i] & reg; 398 399 // Find the parity of the "masked". 400 parity[i] = 0; 401 402 // Calculate the parity of the masked word. 403 while (masked > 0) { 404 parity[i] = parity[i] ^ masked & 1; 405 masked = masked >> 1; 406 } 407 } 408 409 return parity; 410 } 411 412 /////////////////////////////////////////////////////////////////// 413 //// private variables //// 414 // Consumption rate of the input port. 415 private Parameter _inputRate; 416 417 // Production rate of the output port. 418 private Parameter _outputRate; 419 420 // Record the state of the shift register. 421 private int _shiftReg; 422 423 // Updated state of the shift register. 424 private int _latestShiftReg; 425 426 // Number of bits the actor consumes per firing. 427 private int _inputNumber; 428 429 // Polynomial array. 430 private int[] _mask; 431 432 // Number of polynomials. 433 private int _maskNumber; 434 435 // The maximum value in integer among all polynomials. 436 private int _maxPolyValue; 437 438 // A flag indicating that the private variable 439 // _inputNumber is invalid. 440 private transient boolean _inputNumberInvalid = true; 441}