001/* Chop an input sequence and construct from it a new output sequence. 002 003 Copyright (c) 1997-2014 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.domains.sdf.lib; 029 030import ptolemy.data.BooleanToken; 031import ptolemy.data.IntToken; 032import ptolemy.data.Token; 033import ptolemy.data.expr.Parameter; 034import ptolemy.data.type.BaseType; 035import ptolemy.kernel.CompositeEntity; 036import ptolemy.kernel.util.Attribute; 037import ptolemy.kernel.util.IllegalActionException; 038import ptolemy.kernel.util.NameDuplicationException; 039 040/////////////////////////////////////////////////////////////////// 041//// Chop 042 043/** 044 This actor reads a sequence of input tokens of any type, and writes a 045 sequence of tokens constructed from the input sequence (possibly 046 supplemented with zeros). The number of input tokens consumed 047 is given by <i>numberToRead</i>, and the number of output tokens 048 produced is given by <i>numberToWrite</i>. 049 The <i>offset</i> parameter (default 0) specifies where in the output 050 block the first (oldest) input that is read should go. 051 If <i>offset</i> is positive and <i>usePastInputs</i> is true, 052 then the first few outputs will come from values read in previous iterations. 053 <p> 054 A simple use of this actor is to pad a block of inputs with zeros. 055 Set <i>offset</i> to zero and use <i>numberToWrite > numberToRead</i>. 056 <a name="zeroPadding"></a> 057 <a name="padding"></a></p> 058 <p> 059 Another simple use is to obtain overlapping windows from 060 an input stream. 061 Set <i>usePastInputs</i> to true, use <i>numberToWrite > numberToRead</i>, 062 and set <i>offset</i> equal to <i>numberToWrite - numberToRead</i>. 063 <a name="overlappingWindows"></a> 064 <a name="windowing"></a></p> 065 <p> 066 The general operation is illustrated with the following examples. 067 If <i>offset</i> is positive, 068 there two possible scenarios, illustrated by the following examples:</p> 069 <pre> 070 iiiiii numberToRead = 6 071 \ \ offset = 2 072 ppiiiiii00 numberToWrite = 10 073 074 iiiiii numberToRead = 6 075 \ \ \ offset = 2 076 ppiii numberToWrite = 5 077 </pre> 078 <p> 079 The symbol "i" refers to any input token. The leftmost symbol 080 refers to the oldest input token of the ones consumed in a given 081 firing. The symbol "p" refers to a token that is either zero 082 (if <i>usePastInputs</i> is false) or is equal to a previously 083 consumed input token (if <i>usePastInputs</i> is true). 084 The symbol "0" refers to a zero-valued token. 085 In the first of the above examples, the entire input block is 086 copied to the output, and then filled out with zeros. 087 In the second example, only a portion of the input block fits. 088 The remaining input tokens are discarded, although they might 089 be used in subsequent firings if <i>usePastInputs</i> is true.</p> 090 <p> 091 When the <i>offset</i> is negative, this indicates that the 092 first <i>offset</i> input tokens that are read should be 093 discarded. The corresponding scenarios are shown below:</p> 094 <pre> 095 iiiiii numberToRead = 6 096 / / / offset = -2 097 iiii000000 numberToWrite = 10 098 099 iiiiii numberToRead = 6 100 / / // offset = -2 101 iii numberToWrite = 3 102 </pre> 103 <p> 104 In the first of these examples, the first two input tokens are 105 discarded. In the second example, the first two and the last input 106 token are discarded.</p> 107 <p> 108 The zero-valued tokens are constructed using the zero() method of 109 the first input token that is read in the firing. This returns 110 a zero-valued token with the same type as the input.</p> 111 112 @author Edward A. Lee 113 @version $Id$ 114 @since Ptolemy II 1.0 115 @Pt.ProposedRating Green (eal) 116 @Pt.AcceptedRating Yellow (neuendor) 117 */ 118public class Chop extends SDFTransformer { 119 /** Construct an actor in the specified container with the specified 120 * name. 121 * @param container The container. 122 * @param name The name. 123 * @exception IllegalActionException If the actor cannot be contained 124 * by the proposed container. 125 * @exception NameDuplicationException If the name coincides with 126 * an actor already in the container. 127 */ 128 public Chop(CompositeEntity container, String name) 129 throws IllegalActionException, NameDuplicationException { 130 super(container, name); 131 132 numberToRead = new Parameter(this, "numberToRead"); 133 numberToRead.setExpression("128"); 134 numberToRead.setTypeEquals(BaseType.INT); 135 136 numberToWrite = new Parameter(this, "numberToWrite"); 137 numberToWrite.setExpression("64"); 138 numberToWrite.setTypeEquals(BaseType.INT); 139 140 offset = new Parameter(this, "offset"); 141 offset.setExpression("0"); 142 offset.setTypeEquals(BaseType.INT); 143 144 usePastInputs = new Parameter(this, "usePastInputs"); 145 usePastInputs.setExpression("true"); 146 usePastInputs.setTypeEquals(BaseType.BOOLEAN); 147 148 input_tokenConsumptionRate.setExpression("numberToRead"); 149 output_tokenProductionRate.setExpression("numberToWrite"); 150 } 151 152 /////////////////////////////////////////////////////////////////// 153 //// ports and parameters //// 154 155 /** The number of input tokens to read. 156 * This is an integer, with default 128. 157 */ 158 public Parameter numberToRead; 159 160 /** The number of tokens to write to the output. 161 * This is an integer, with default 64. 162 */ 163 public Parameter numberToWrite; 164 165 /** Start of output block relative to start of input block. 166 * This is an integer, with default 0. 167 */ 168 public Parameter offset; 169 170 /** If offset is greater than 0, specify whether to use previously 171 * read inputs (otherwise use zeros). 172 * This is a boolean, with default true. 173 */ 174 public Parameter usePastInputs; 175 176 /////////////////////////////////////////////////////////////////// 177 //// public methods //// 178 179 /** Check the validity of parameter values and using the new 180 * values, recompute the size of the internal buffers. 181 * @param attribute The attribute that has changed. 182 * @exception IllegalActionException If the parameters are out of range. 183 */ 184 @Override 185 public void attributeChanged(Attribute attribute) 186 throws IllegalActionException { 187 // Note: it is important that none of these sections depend on 188 // each other. 189 if (attribute == numberToRead) { 190 _numberToRead = ((IntToken) numberToRead.getToken()).intValue(); 191 192 if (_numberToRead <= 0) { 193 throw new IllegalActionException(this, 194 "Invalid numberToRead: " + _numberToRead); 195 } 196 } else if (attribute == numberToWrite) { 197 _numberToWrite = ((IntToken) numberToWrite.getToken()).intValue(); 198 199 if (_numberToWrite <= 0) { 200 throw new IllegalActionException(this, 201 "Invalid numberToWrite: " + _numberToRead); 202 } 203 204 _buffer = new Token[_numberToWrite]; 205 } else if (attribute == offset) { 206 _offsetValue = ((IntToken) offset.getToken()).intValue(); 207 } else if (attribute == usePastInputs) { 208 _usePast = ((BooleanToken) usePastInputs.getToken()).booleanValue(); 209 } 210 211 if (attribute == offset || attribute == usePastInputs) { 212 if (_offsetValue > 0) { 213 _pastBuffer = new Token[_offsetValue]; 214 _pastNeedsInitializing = true; 215 } 216 } 217 218 if (attribute == numberToRead || attribute == numberToWrite 219 || attribute == offset || attribute == usePastInputs) { 220 // NOTE: The following computation gets repeated when each of 221 // these gets set, but it's a simple calculation, so we live 222 // with it. 223 // The variables _highLimit and _lowLimit indicate the range of 224 // output indexes that come directly from the input block 225 // that is read. 226 _highLimit = _offsetValue + _numberToRead - 1; 227 228 if (_highLimit >= _numberToWrite) { 229 _highLimit = _numberToWrite - 1; 230 } 231 232 if (_offsetValue >= 0) { 233 _lowLimit = _offsetValue; 234 _inputIndex = 0; 235 } else { 236 _lowLimit = 0; 237 _inputIndex = -_offsetValue; 238 } 239 } else { 240 super.attributeChanged(attribute); 241 } 242 } 243 244 /** Consume the specified number of input tokens, and produce 245 * the specified number of output tokens. 246 * @exception IllegalActionException If there is no director. 247 */ 248 @Override 249 public void fire() throws IllegalActionException { 250 super.fire(); 251 252 int inputIndex = _inputIndex; 253 int pastBufferIndex = 0; 254 Token[] inBuffer = input.get(0, _numberToRead); 255 Token zero = inBuffer[0].zero(); 256 257 for (int i = 0; i < _numberToWrite; i++) { 258 if (i > _highLimit) { 259 _buffer[i] = zero; 260 } else if (i < _lowLimit) { 261 if (_usePast) { 262 if (_pastNeedsInitializing) { 263 // Fill past buffer with zeros. 264 for (int j = 0; j < _pastBuffer.length; j++) { 265 _pastBuffer[j] = zero; 266 } 267 268 _pastNeedsInitializing = false; 269 } 270 271 _buffer[i] = _pastBuffer[pastBufferIndex++]; 272 } else { 273 _buffer[i] = zero; 274 } 275 } else { 276 // FIXME: This will access past samples... 277 _buffer[i] = inBuffer[inputIndex]; 278 inputIndex++; 279 } 280 } 281 282 if (_usePast && _offsetValue > 0) { 283 // Copy input buffer into past buffer. Have to be careful 284 // here because the buffer might be longer than the 285 // input window. 286 int startCopy = _numberToRead - _offsetValue; 287 int length = _pastBuffer.length; 288 int destination = 0; 289 290 if (startCopy < 0) { 291 // Shift older data. 292 destination = _pastBuffer.length - _numberToRead; 293 System.arraycopy(_pastBuffer, _numberToRead, _pastBuffer, 0, 294 destination); 295 startCopy = 0; 296 length = _numberToRead; 297 } 298 299 System.arraycopy(inBuffer, startCopy, _pastBuffer, destination, 300 length); 301 } 302 303 output.send(0, _buffer, _numberToWrite); 304 } 305 306 /** Override the base class to ensure that the past buffer 307 * gets initialized. 308 * @exception IllegalActionException If the superclass throws it. 309 */ 310 @Override 311 public void initialize() throws IllegalActionException { 312 super.initialize(); 313 _pastNeedsInitializing = true; 314 } 315 316 /////////////////////////////////////////////////////////////////// 317 //// private members //// 318 private int _highLimit; 319 320 /////////////////////////////////////////////////////////////////// 321 //// private members //// 322 private int _inputIndex; 323 324 /////////////////////////////////////////////////////////////////// 325 //// private members //// 326 private int _lowLimit; 327 328 private int _numberToRead; 329 330 private int _numberToWrite; 331 332 private int _offsetValue; 333 334 private Token[] _buffer; 335 336 private Token[] _pastBuffer; 337 338 private boolean _usePast; 339 340 private boolean _pastNeedsInitializing; 341}