001/* An FIR filter with a raised cosine frequency response. 002 003 Copyright (c) 1998-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.ArrayToken; 031import ptolemy.data.BooleanToken; 032import ptolemy.data.DoubleToken; 033import ptolemy.data.IntToken; 034import ptolemy.data.expr.Parameter; 035import ptolemy.data.type.BaseType; 036import ptolemy.kernel.CompositeEntity; 037import ptolemy.kernel.util.Attribute; 038import ptolemy.kernel.util.IllegalActionException; 039import ptolemy.kernel.util.NameDuplicationException; 040import ptolemy.kernel.util.Settable; 041import ptolemy.math.DoubleUnaryOperation; 042import ptolemy.math.SignalProcessing; 043 044/////////////////////////////////////////////////////////////////// 045//// RaisedCosine 046 047/** 048 This actor implements an FIR filter with 049 a raised cosine or square-root raised cosine frequency response. 050 The excess bandwidth is given 051 by <i>excessBW</i> and the symbol interval (in number of samples) 052 by <i>interpolation</i> (which by default is 16). 053 The length of the filter (the number of taps) is given by <i>length</i>. 054 <p> 055 For the ordinary raised cosine response, 056 the impulse response of the filter would ideally be 057 <pre> 058 sin(pi n/T) cos(alpha pi n/T) 059 h(n) = ----------- * ----------------- 060 pi n/T 1-(2 alpha n/T)<sup>2</sup> 061 </pre> 062 where <i>alpha</i> is <i>excessBW</i> and <i>T</i> is the 063 <i>interpolation</i> factor. 064 However, this pulse is centered at zero, and we can only implement causal 065 filters in the SDF domain in Ptolemy. Hence, the impulse response is 066 actually 067 <pre> 068 g(n) = h(n - M) 069 </pre> 070 where <i>M</i> = <i>length/</i>2 if <i>length</i> is even, and <i>M 071 </i>= (<i>length+</i>1)<i>/</i>2 if <i>length</i> is odd. 072 The impulse response is simply truncated outside this range, so 073 the impulse response will generally not be symmetric if <i>length</i> is even 074 because it will have one more sample to the left than to the right of center. 075 Unless this extra sample is zero, the filter will not have linear phase 076 if <i>length</i> is even. 077 <p> 078 For the ordinary raised cosine response, the 079 distance (in number of samples) from the center 080 to the first zero crossing is given by <i>symbolInterval</i>. 081 For the square-root raised cosine response, a cascade of two identical 082 square-root raised cosine filters would be equivalent to a single 083 ordinary raised cosine filter. 084 <p> 085 The impulse response of the square-root raised cosine pulse is given by 086 <pre> 087 4 alpha(cos((1+alpha)pi n/T)+Tsin((1-alpha)pi n/T)/(4n alpha/T)) 088 h(n) = ----------------------------------------------------------------- 089 pi sqrt(T)(1-(4 alpha n/T)<sup>2</sup>) 090 </pre> 091 This impulse response convolved with itself will, in principle, be equal 092 to a raised cosine pulse. However, because of the abrupt rectangular 093 windowing of the pulse, with low excess bandwidth, this ideal is not 094 closely approximated except for very long filters. 095 <p> 096 The output sample rate is <i>interpolation</i> times the input. 097 This is set by default to 16 because in digital communication systems 098 this pulse is used for the line coding of symbols, and upsampling is necessary. 099 Typically, the value of <i>interpolation</i> is the same as that of 100 <i>symbolInterval</i>, at least when the filter is being used 101 as a transmit pulse shaper. 102 <h3>References</h3> 103 <p>[1] 104 E. A. Lee and D. G. Messerschmitt, 105 <i>Digital Communication,</i> Kluwer Academic Publishers, Boston, 1988. 106 <p>[2] 107 I. Korn, <i>Digital Communications</i>, Van Nostrand Reinhold, New York, 1985. 108 109 @author Edward A. Lee 110 @version $Id$ 111 @since Ptolemy II 0.2 112 @Pt.ProposedRating Yellow (neuendor) 113 @Pt.AcceptedRating Yellow (neuendor) 114 */ 115public class RaisedCosine extends FIR { 116 /** Construct an actor with the given container and name. 117 * @param container The container. 118 * @param name The name of this actor. 119 * @exception IllegalActionException If the actor cannot be contained 120 * by the proposed container. 121 * @exception NameDuplicationException If the container already has an 122 * actor with this name. 123 */ 124 public RaisedCosine(CompositeEntity container, String name) 125 throws NameDuplicationException, IllegalActionException { 126 super(container, name); 127 128 length = new Parameter(this, "length", new IntToken(64)); 129 interpolation.setToken(new IntToken(16)); 130 excessBW = new Parameter(this, "excessBW", new DoubleToken(1.0)); 131 root = new Parameter(this, "root", new BooleanToken(false)); 132 symbolInterval = new Parameter(this, "symbolInterval", 133 new IntToken(16)); 134 135 // Hide taps from UI. 136 taps.setVisibility(Settable.NONE); 137 _initialize(); 138 } 139 140 /////////////////////////////////////////////////////////////////// 141 //// public variables //// 142 143 /** The excess bandwidth. This contains a 144 * DoubleToken, and by default it has value 1.0. 145 */ 146 public Parameter excessBW; 147 148 /** The length of the pulse. This contains an 149 * IntToken, and by default it has value 64. 150 */ 151 public Parameter length; 152 153 /** If true, use the square root of the raised cosine instead of the 154 * raised cosine. This contains a 155 * BooleanToken, and by default it has value false. 156 */ 157 public Parameter root; 158 159 /** The symbol interval, which is the number of samples to the first 160 * zero crossing on each side of the main lobe. Its value is an 161 * IntToken, and by default it has value 16. 162 */ 163 public Parameter symbolInterval; 164 165 /////////////////////////////////////////////////////////////////// 166 //// public methods //// 167 168 /** Reevaluate the filter taps if the attribute is any of the ones 169 * defined locally, and otherwise call the superclass. 170 * @param attribute The attribute that changed. 171 * @exception IllegalActionException If the parameters are out of range. 172 */ 173 @Override 174 public void attributeChanged(Attribute attribute) 175 throws IllegalActionException { 176 if (attribute == excessBW || attribute == length || attribute == root 177 || attribute == symbolInterval) { 178 _initialize(); 179 } else { 180 super.attributeChanged(attribute); 181 } 182 } 183 184 // Initialize the state of the actor based on the current state of the 185 // parameters. 186 private void _initialize() throws IllegalActionException { 187 double excessBWValue = ((DoubleToken) excessBW.getToken()) 188 .doubleValue(); 189 int symbolIntervalValue = ((IntToken) symbolInterval.getToken()) 190 .intValue(); 191 int lengthValue = ((IntToken) length.getToken()).intValue(); 192 boolean sqrt = ((BooleanToken) root.getToken()).booleanValue(); 193 194 if (excessBWValue < 0.0) { 195 throw new IllegalActionException(this, 196 "Excess bandwidth was " + excessBWValue 197 + " which is not greater than or equal to zero."); 198 } 199 200 if (lengthValue <= 0) { 201 throw new IllegalActionException(this, "Length was " + lengthValue 202 + " which is not greater than zero."); 203 } 204 205 double center = lengthValue * 0.5; 206 207 DoubleUnaryOperation raisedCosineSampleGenerator = sqrt 208 ? (DoubleUnaryOperation) new SignalProcessing.SqrtRaisedCosineSampleGenerator( 209 symbolIntervalValue, excessBWValue) 210 : (DoubleUnaryOperation) new SignalProcessing.RaisedCosineSampleGenerator( 211 symbolIntervalValue, excessBWValue); 212 213 double[] tapsArray = SignalProcessing.sampleWave(lengthValue, -center, 214 1.0, raisedCosineSampleGenerator); 215 DoubleToken[] tapsArrayToken = new DoubleToken[tapsArray.length]; 216 217 for (int i = 0; i < tapsArray.length; i++) { 218 tapsArrayToken[i] = new DoubleToken(tapsArray[i]); 219 } 220 221 taps.setToken(new ArrayToken(BaseType.DOUBLE, tapsArrayToken)); 222 } 223}