001/* An actor that outputs a quantized version of the input. 002 003 Copyright (c) 1998-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; 029 030import ptolemy.data.ArrayToken; 031import ptolemy.data.DoubleToken; 032import ptolemy.data.expr.Parameter; 033import ptolemy.data.type.ArrayType; 034import ptolemy.data.type.BaseType; 035import ptolemy.kernel.CompositeEntity; 036import ptolemy.kernel.util.Attribute; 037import ptolemy.kernel.util.IllegalActionException; 038import ptolemy.kernel.util.NameDuplicationException; 039import ptolemy.kernel.util.Workspace; 040 041/////////////////////////////////////////////////////////////////// 042//// Quantizer 043 044/** 045 <p>Produce an output token on each firing with a value that is 046 a quantized version of the input. The input and output types 047 are both double. 048 </p><p> 049 There are two ways to specify the quantization. If the <i>levels</i> 050 parameter is given, then the output will be the element of the array specified 051 by <i>levels</i> that is closest to the input. Otherwise, if <i>levels</i> is 052 empty, then the output will be quantized according to the <i>delta</i> parameter, 053 which specifies the spacing between quantization levels. Specifically, the 054 output will be 055 </p><p> 056 <i>delta</i> * Math.floor( <i>input</i>/<i>delta</i>) . 057 </p><p> 058 With the default value of <i>delta</i>, which is 1.0, the output is simply 059 the integer part of the input. 060 </p><p> 061 The <i>levels</i> parameter contains an array of doubles 062 specifying the quantization levels. The elements must be in 063 an increasing order, or an exception will be thrown. 064 The default value of <i>levels</i> is {-1.0, 1.0}.</p> 065 <p> 066 Suppose <i>u</i> is the input, and 067 <i>levels</i> = {<i>a</i>, <i>b</i>, <i>c</i>}, where 068 <i>a</i> < <i>b</i> < <i>c</i>, then the output of the actor will be: 069 </p> 070 <p> 071 <i>y</i> = <i>a</i>, for <i>u</i> <= (<i>b</i>+<i>a</i>)/2; 072 <br><i>y</i> = <i>b</i>, for (<i>b</i>+<i>a</i>)/2 <<i>u</i> <= (<i>c</i>+<i>b</i>)/2;<br> 073 <br><i>y</i> = <i>c</i>, for <i>u</i> > (<i>c</i>+<i>b</i>)/2;<br> 074 </p><p> 075 Thus, for the default <i>levels</i>, the output is (almost) 076 the signum function of the input, or +1.0 if the input is positive, 077 and -1.0 otherwise. This is almost the signum function because it 078 outputs -1.0 if the input is zero. 079 </p><p> 080 This actor does not require that the quantization intervals be equal, 081 i.e. we allow that (<i>c</i>-<i>b</i>) != (<i>b</i>-<i>a</i>).</p> 082 083 @author Jie Liu 084 @version $Id$ 085 @since Ptolemy II 0.3 086 @Pt.ProposedRating Yellow (liuj) 087 @Pt.AcceptedRating Yellow (yuhong) 088 */ 089public class Quantizer extends Transformer { 090 /** Construct an actor with the given container and name. 091 * @param container The container. 092 * @param name The name of this actor. 093 * @exception IllegalActionException If the actor cannot be contained 094 * by the proposed container. 095 * @exception NameDuplicationException If the container already has an 096 * actor with this name. 097 */ 098 public Quantizer(CompositeEntity container, String name) 099 throws NameDuplicationException, IllegalActionException { 100 super(container, name); 101 levels = new Parameter(this, "levels"); 102 levels.setExpression("{-1.0, 1.0}"); 103 levels.setTypeEquals(new ArrayType(BaseType.DOUBLE)); 104 105 delta = new Parameter(this, "delta"); 106 delta.setExpression("1.0"); 107 delta.setTypeEquals(BaseType.DOUBLE); 108 109 // Call this so that we don't have to copy its code here... 110 attributeChanged(levels); 111 112 // Set the type constraints. 113 input.setTypeEquals(BaseType.DOUBLE); 114 output.setTypeEquals(BaseType.DOUBLE); 115 } 116 117 /////////////////////////////////////////////////////////////////// 118 //// ports and parameters //// 119 120 /** The spacing between quantization levels to use if the <i>levels</i> 121 * parameter is not given. This is a double that defaults to 1.0. 122 */ 123 public Parameter delta; 124 125 /** The quantization levels. 126 * This parameter contains an array of doubles with default value 127 * {-1.0, 1.0}. 128 */ 129 public Parameter levels; 130 131 /////////////////////////////////////////////////////////////////// 132 //// public methods //// 133 134 /** Clone the actor into the specified workspace. 135 * @param workspace The workspace for the new object. 136 * @return A new actor. 137 * @exception CloneNotSupportedException If a derived class contains 138 * an attribute that cannot be cloned. 139 */ 140 @Override 141 public Object clone(Workspace workspace) throws CloneNotSupportedException { 142 Quantizer newObject = (Quantizer) super.clone(workspace); 143 144 newObject._thresholds = new double[_thresholds.length]; 145 System.arraycopy(_thresholds, 0, newObject._thresholds, 0, 146 _thresholds.length); 147 148 return newObject; 149 } 150 151 /** If the argument is the levels parameter, check that the array 152 * is increasing and has the right dimension. Recompute the 153 * quantization thresholds. 154 * @param attribute The attribute that changed. 155 * @exception IllegalActionException If the levels array is not 156 * increasing. 157 */ 158 @Override 159 public void attributeChanged(Attribute attribute) 160 throws IllegalActionException { 161 if (attribute == levels) { 162 ArrayToken levelsValue = (ArrayToken) levels.getToken(); 163 if (levelsValue == null || levelsValue.length() == 0) { 164 _thresholds = null; 165 return; 166 } 167 double[] _levels = new double[levelsValue.length()]; 168 double previous = Double.NEGATIVE_INFINITY; 169 170 for (int i = 0; i < levelsValue.length(); i++) { 171 _levels[i] = ((DoubleToken) levelsValue.getElement(i)) 172 .doubleValue(); 173 174 // Check nondecreasing property. 175 if (_levels[i] < previous) { 176 throw new IllegalActionException(this, 177 "Value of levels is not nondecreasing."); 178 } 179 180 previous = _levels[i]; 181 } 182 183 // Compute the quantization thresholds. 184 _thresholds = new double[_levels.length - 1]; 185 186 for (int j = 0; j < _levels.length - 1; j++) { 187 _thresholds[j] = (_levels[j + 1] + _levels[j]) / 2.0; 188 } 189 } else { 190 super.attributeChanged(attribute); 191 } 192 } 193 194 /** Output the quantization of the input. 195 * If there is no input, then produce no output. 196 * @exception IllegalActionException If there is no director. 197 */ 198 @Override 199 public void fire() throws IllegalActionException { 200 super.fire(); 201 if (input.hasToken(0)) { 202 double in = ((DoubleToken) input.get(0)).doubleValue(); 203 if (_thresholds != null) { 204 int index = _getQuantizationIndex(in); 205 output.send(0, 206 ((ArrayToken) levels.getToken()).getElement(index)); 207 } else { 208 // Using delta parameter. 209 double deltaValue = ((DoubleToken) delta.getToken()) 210 .doubleValue(); 211 double result = deltaValue * Math.floor(in / deltaValue); 212 output.send(0, new DoubleToken(result)); 213 } 214 } 215 } 216 217 /////////////////////////////////////////////////////////////////// 218 //// private methods //// 219 220 /* Compute the quantization index for the given input value. 221 * @parameter in The input value 222 * @return The quantization index. 223 */ 224 private int _getQuantizationIndex(double in) { 225 int index = _thresholds.length; 226 227 for (int i = 0; i < _thresholds.length; i++) { 228 if (in <= _thresholds[i]) { 229 index = i; 230 break; 231 } 232 } 233 234 return index; 235 } 236 237 /////////////////////////////////////////////////////////////////// 238 //// private variables //// 239 // The thresholds for quantization. 240 private double[] _thresholds; 241}