SignalProcessing Improvements in Maple 2023
The SignalProcessing package has been expanded with new and updated commands.
with( SignalProcessing ):
Quantize
SavitzkyGolayFilter
Convolution
FFT and InverseFFT
The new SignalProcessing:-Quantize command is used to replace real-valued data in a container with values from a codebook.
For example, suppose we want to quantize the values generated by sin⁡t over the interval 0,2⁢π so that the values are to the nearest quarter integer. With this new command, we can do either of the following:
X := GenerateSignal( sin(t), t = 0 .. 2 * Pi, 50 )^+;
X≔0.0.1278771616845060.2536545839095070.3752670048793740.4907175520039380.5981105304912160.6956825506034860.7818314824680300.8551427630053460.9144126230158120.9586678530366610.9871817834144500.9994862162006880.9953791129491980.9749279121818240.9384684220497600.8865993063730000.8201722545969560.7402779970753150.6482283953077880.5455349012105490.4338837391175580.3151082180236210.1911586287013720.0640702199807128−0.0640702199807130−0.191158628701372−0.315108218023621−0.433883739117558−0.545534901210548−0.648228395307789−0.740277997075316−0.820172254596956−0.886599306373000−0.938468422049761−0.974927912181824−0.995379112949198−0.999486216200688−0.987181783414450−0.958667853036660−0.914412623015812−0.855142763005346−0.781831482468030−0.695682550603486−0.598110530491216−0.490717552003938−0.375267004879374−0.253654583909508−0.127877161684506−2.44929359829471×10−16
Y := Quantize( X, -1 .. 1, 9 );
Y≔0.0.2500000000000000.2500000000000000.5000000000000000.5000000000000000.5000000000000000.7500000000000000.7500000000000000.7500000000000001.1.1.1.1.1.1.1.0.7500000000000000.7500000000000000.7500000000000000.5000000000000000.5000000000000000.2500000000000000.2500000000000000.0.−0.250000000000000−0.250000000000000−0.500000000000000−0.500000000000000−0.750000000000000−0.750000000000000−0.750000000000000−1.−1.−1.−1.−1.−1.−1.−1.−0.750000000000000−0.750000000000000−0.750000000000000−0.500000000000000−0.500000000000000−0.500000000000000−0.250000000000000−0.2500000000000000.
Z := Quantize( X, < seq( 0.25 * k, k = -4 .. 4 ) > );
Z≔0.0.2500000000000000.2500000000000000.5000000000000000.5000000000000000.5000000000000000.7500000000000000.7500000000000000.7500000000000001.1.1.1.1.1.1.1.0.7500000000000000.7500000000000000.7500000000000000.5000000000000000.5000000000000000.2500000000000000.2500000000000000.0.−0.250000000000000−0.250000000000000−0.500000000000000−0.500000000000000−0.750000000000000−0.750000000000000−0.750000000000000−1.−1.−1.−1.−1.−1.−1.−1.−0.750000000000000−0.750000000000000−0.750000000000000−0.500000000000000−0.500000000000000−0.500000000000000−0.250000000000000−0.2500000000000000.
The command can also display the original and quantized signals together:
Quantize( X, -1 .. 1, 9, 'output' = 'plot', 'emphasizecodes' );
The example above uses the default nearest for the option truncation, but below and above are also available. Moreover, classical quantization techniques midriser and midtread are provided:
Quantize( X, 'midriser', 1, 9, 'output' = 'plot', 'emphasizecodes' );
The SignalProcessing:-GenerateSignal command has also been updated to include the quantization option:
GenerateSignal( sin(t), t = 0 .. 2 * Pi, 50, 'quantization' = [-1..1,9], 'output' = 'signalplot' );
The new SignalProcessing:-SavitzkyGolayFilter command applies the Savitzky-Golay Filter to a signal, with applications including smoothing noisy data and estimating derivatives of data. The filter is also known as Polynomial Smoothing, Least-Squares Smoothing, and Locally Weighted Scatterplot Smoothing (LOWESS).
For example, consider the following:
f := sin(t) + 1/5 * cos(3*t); # original expression
f≔sin⁡t+cos⁡3⁢t5
t1 := 0; # start time
t1≔0
t2 := 2 * Pi; # finish time
t2≔2⁢π
m := 200; # number of points
m≔200
(T,X) := GenerateSignal( f, t = t1 .. t2, m, 'noisedeviation' = 0.2, 'includefinishtime' = 'false', 'output' = ['times','signal'] );
The smoothing, both unweighted and weighted, noticeably reduces the noise:
d := 2; # degree of polynomial interpolants
d≔2
r := 10; # radius of frame
r≔10
W := < seq( 1 .. r + 1 ), seq( r .. 1, -1 ) >: # weights
SavitzkyGolayFilter( X, d, r, 'timerange' = t1 .. t2, 'plotoptions' = [ 'title' = "Unweighted Savitzky-Golay Filtered Signal" ], 'output' = 'plot' );
SavitzkyGolayFilter( X, d, W, 'timerange' = t1 .. t2, 'plotoptions' = [ 'title' = "Weighted Savitzky-Golay Filtered Signal" ], 'output' = 'plot' );
The SignalProcessing:-DifferentiateData command now includes an option to estimate the derivative of a signal using the Savitzky-Golay Filter;
(dt,T,U) := GenerateSignal( t * (t^2-1)^3, t = -1 .. 1, 75, 'includefinishtime' = 'false', 'output' = ['timestep','times','signal'] );
V := DifferentiateData( U, 1, 'step' = dt, 'method' = 'savitzkygolay', 'extrapolation' = 'periodic', 'frameradius' = 2 );
dataplot( T, [U,V], 'style' = 'line', 'legend' = ["Signal","First derivative"], 'color' = ['red','blue'], 'view' = [-1..1,'DEFAULT'] );
The SignalProcessing:-Convolution command has been updated to include shape options full (the default), same, and valid. For example:
A := LinearAlgebra:-RandomVector( 5, 'generator' = -1.0 .. 1.0, 'datatype' = 'float[8]' );
A≔−0.8448859366818580.604222951371852−0.434653429149560−0.8092896618613750.964721117374344
B := LinearAlgebra:-RandomVector( 3, 'generator' = -1.0 .. 1.0, 'datatype' = 'float[8]' );
B≔0.360574124053584−0.9838121809817140.254768677798811
C1 := Convolution( A, B, 'shape' = 'full' );
C1≔−0.3046440065442531.04907623747173−0.9664171520500850.2897455095882241.03330641968989−1.155286043631840.245780723518053
C2 := Convolution( A, B, 'shape' = 'same' );
C2≔1.04907623747173−0.9664171520500850.2897455095882241.03330641968989−1.15528604363184
C3 := Convolution( A, B, 'shape' = 'valid' );
C3≔−0.9664171520500850.2897455095882241.03330641968989
Moreover, lists can now be passed for signals in addition to rtables.
The SignalProcessing:-FFT and SignalProcessing:-InverseFFT commands now support padding and truncation. Padding is helpful due to the increase in both the speed of computation and the frequency resolution. The size option accepts a positive integer for the new size and keywords same (the default, for not changing the length) and recommended (for changing the length to the next power of 2). For example:
X := LinearAlgebra:-RandomVector( 3, 'generator' = -1.0 .. 1.0, 'datatype' = 'complex[8]' );
X≔−0.235334127009411+0.524842563866319⁢I−0.600897572737194−0.723997348520040⁢I0.0678662154384722−0.122666338816995⁢I
Y1 := FFT( X, 'size' = 'same' );
Y1≔−0.443616019201318−0.185803512266725⁢I−0.282662827618448+0.881810643516785⁢I0.318668182084598+0.213046855341119⁢I
Y2 := FFT( X, 'size' = 'recommended' );
Y2≔−0.384182742154067−0.160910561735358⁢I−0.513598845483962+0.624203237710254⁢I0.216714830583127+0.563086786784682⁢I0.210398503036078+0.0233056649730601⁢I
Y3 := FFT( X, 'size' = 5 );
Y3≔−0.343623490895507−0.143922781735175⁢I−0.553020070919267+0.416781168114568⁢I−0.0165998120551015+0.666529860535818⁢I0.259681898881381+0.292888860328475⁢I0.127338369569880−0.0586934569533205⁢I
The size option also works with multi-dimensional input. Moreover, lists can now be passed for signals in addition to rtables.
Download Help Document