Manipulate[ (*by Nasser M. Abbasi, 080615 *) ( ok = True; If [Not[normalized], ( samplingUsed = sampling; If[filterOrderType == filterOrderMinumum, If[stop >= sampling/2, stop = sampling/2 - 0.1]; If[pass >= stop, pass = stop - 0.01] , If[pass >= sampling/2, pass = sampling/2 - 0.1] ]; wpass = N[pass /sampling*2*Pi]; wstop = N[stop /sampling*2*Pi] ), ( samplingUsed = 2; (*Nyquist=1*) If[filterOrderType == filterOrderMinumum, If[passNormalized >= stopNormalized, passNormalized = stopNormalized - 0.01 ] ]; wpass = N[passNormalized*Pi]; wstop = N[stopNormalized*Pi] ) ]; If[passdb >= stopdb, passdb = stopdb - 0.01]; Which[ filterOrderType == filterOrderMinumum, {filterOrderFoundByDesign, \[CapitalOmega]c} = butterd[samplingUsed, wpass, wstop , -passdb, -stopdb, method]; If[filterOrderFoundByDesign > maxFilterOrderAllowed , ok = False; statusMsg = Text@Style[ Row[{"Error: calculated filter order is ", filterOrderFoundByDesign, ". Limit is 10"}]] ] , filterOrderType == filterOrderSpecified, filterOrderFoundByDesign = filterOrder; \[CapitalOmega]c = If[method == bilinearMappingMethod, 2*samplingUsed*Tan[wpass /2], wpass *samplingUsed]; ]; If[ok, hspoles = N@getHsStablePoles[filterOrderFoundByDesign, \[CapitalOmega]c]; hsGain = Re@Chop@getHsGain[hspoles, method, samplingUsed]; If[hsGain < minumumGainAllowed , ok = False; statusMsg = "Error: Gain found to be too small" ] ]; If[ok, { coeff = getPartialFractionsCoeff[hspoles, hsGain, s]; hzpoles = Chop@getHzPoles[hspoles, method, samplingUsed]; hsSecondOrderForm = Chop@getHsFromPolesSecondOrder[hspoles, s, coeff]; fullFormHS = Chop@FullSimplify@Total@hsSecondOrderForm; fullFormHS = Chop@Expand[Numerator@fullFormHS]/ Chop@Expand@Denominator@fullFormHS; Which[ method == bilinearMappingMethod,(*bilinear*){ hz = getHzBilinear[hsSecondOrderForm, samplingUsed, s, z];}, method == impulseInvarianceMappingMethod, { hz = getHzImpulseInvariance[hspoles, coeff, samplingUsed, z];} ]; fullFormHz = FullSimplify@Total@hz; fullFormHz = Expand[Numerator@fullFormHz]/Expand@Denominator@fullFormHz; Which[ plotType == plotTypeResponseSpectrumDBScale, plots = magPhasePlot[hz, samplingUsed/2, useDBScale, z, normalized], plotType == plotTypeResponseSpectrumLinearScale, plots = magPhasePlot[hz, samplingUsed/2, useLinearScale, z, normalized], plotType == plotTypeButterworth, plots = doplotTypeButterworth[ filterOrderFoundByDesign, \[CapitalOmega]c], plotType == plotTypeShowPoles, plots = polesPlot[hzpoles, hspoles, \[CapitalOmega]c, filterOrderFoundByDesign, method], plotType == plotTypeHSfirstOrder, plots = doHsPlot[hspoles, coeff, 1./samplingUsed, s], plotType == plotTypeHSSecondOrder, If[filterOrderFoundByDesign == 1, plots = doHsPlot[hspoles, coeff, 1./samplingUsed, s], plots = doHs2Plot[hsSecondOrderForm, s] ], plotType == plotTypeHSPolynomial, plots = doHsFullPlot[fullFormHS], plotType == plotTypeHzfirstOrder, plots = doHzPlot[hspoles, coeff, hzpoles, 1./samplingUsed, z], plotType == plotTypeHzSecondOrder, If[filterOrderFoundByDesign == 1, plots = doHzPlot[coeff, hzpoles, 1./samplingUsed, z], plots = doHz2Plot[hz, z]], plotType == plotTypeHzPolynomial, plots = doHzFullPlot[fullFormHz], plotType == plotTypeImpluseResponse, plots = doImpulseResponsePlot[fullFormHS, s, hz, z, 1/samplingUsed, nSamplesForPlotting], plotType == plotTypeStepResponse, plots = doStepResponsePlot[fullFormHS, s, hz, z, 1/samplingUsed, nSamplesForPlotting], plotType == plotTypeRampResponse, plots = doRampResponsePlot[fullFormHS, s, hz, z, 1/samplingUsed, nSamplesForPlotting] ] } ]; If[Not[ok], statusMsg = Text@Style[statusMsg, Red, 14], statusMsg = Text@Style[ Row[{"Filter order ", Style["N", Italic], " = ", filterOrderFoundByDesign, ". Cutoff frequency ", "\[Omega]", " = ", \[CapitalOmega]c, " (rad/sec)"}], 12]]; Column[{Framed[plots, ImageSize -> {390, 350}, FrameMargins -> 4], Framed[statusMsg, ImageSize -> {390, 22}, ImageMargins -> 0, Alignment -> {Center}, FrameMargins -> 0]}] ), (*-------------------------------------------*) (* C O N T R O L L A Y O U T LEFT SIDE *) (*-------------------------------------------*) Item[ Grid[{ { Panel[ Labeled[ RadioButtonBar[ Dynamic[method], {bilinearMappingMethod -> Style["bilinear method", 10], impulseInvarianceMappingMethod -> Style["impulse invariance", 10]}, Appearance -> "Vertical"], Style["mapping method", "Label", Bold], {{Top, Center}}], FrameMargins -> 3 ], Panel[ Labeled[ RadioButtonBar[ Dynamic[normalized], {True -> Style["normalized", 10], False -> Style["Hz", 10]}, Appearance -> "Vertical"], Style["frequency units", "Label", Bold], {{Top, Center}}], FrameMargins -> 3] }, { Panel[Labeled[Column[{ Control[{{passNormalized, .2, Style["\!\(\*SubscriptBox[\(F\), \(pass\)]\) ", 10]}, .01, .98, .01, Appearance -> "Labeled", ImageSize -> Small, Enabled -> Dynamic[normalized === True]}], Control[{{stopNormalized, .3, Style["\!\(\*SubscriptBox[\(F\), \(stop\)]\) ", 10]}, .02, .99, .01, Appearance -> "Labeled", ImageSize -> Small, Enabled -> Dynamic[ normalized === True && filterOrderType == filterOrderMinumum]}] }], Style["normalized frequency input", "Label", Bold], {{Top, Center}}] , FrameMargins -> 3], SpanFromLeft }, { Panel[Labeled[Column[{ Control[{{pass, 2, Style["\!\(\*SubscriptBox[\(F\), \(pass\)]\) ", 10]}, 0.1, 49.8, 0.1, Appearance -> "Labeled", ImageSize -> Small, Enabled -> Dynamic[normalized === False]}], Control[{{stop, 3, Style["\!\(\*SubscriptBox[\(F\), \(stop\)]\) ", 10]}, 0.2, 49.9, 0.1, Appearance -> "Labeled", ImageSize -> Small, Enabled -> Dynamic[ normalized === False && filterOrderType == filterOrderMinumum]}], Control[{{sampling, 10, Style["\!\(\*SubscriptBox[\(F\), \(samp\)]\)", 10]}, 1, 100, 0.1, Appearance -> "Labeled", ImageSize -> Small, Enabled -> Dynamic[normalized === False]}] }], Style["Hz frequency input", "Label", Bold], {{Top, Center}}] , FrameMargins -> 3], SpanFromLeft }, { Panel[Labeled[Column[{ Control[{{passdb, 1, Style["\!\(\*SubscriptBox[\(A\), \(pass\)]\) ", 10]}, 0.1, 99.0, 0.1, Appearance -> "Labeled", ImageSize -> Small, Enabled -> Dynamic[filterOrderType == filterOrderMinumum]}], Control[{{stopdb, 15, Style["\!\(\*SubscriptBox[\(A\), \(stop\)]\) ", 10]}, 0.2, 100, 0.1, Appearance -> "Labeled", ImageSize -> Small, Enabled -> Dynamic[filterOrderType == filterOrderMinumum]}] }], Style["attenuation in db", "Label", Bold], {{Top, Center}}] , FrameMargins -> 3], SpanFromLeft } }], ControlPlacement -> Left ], (*-------------------------------------------*) (* C O N T R O L L A Y O U T TOP SIDE *) (*-------------------------------------------*) Item[ Grid[{ {Panel[ doPlotButterworthSpecs[2.3, 5.3, -7, -26, 2, 3]], Panel[Column[{Labeled[ RadioButtonBar[ Dynamic[filterOrderType], {filterOrderSpecified -> Style["specific order", 10], filterOrderMinumum -> Style["minumum", 10]}, Appearance -> "Horizontal" ], Style["method to determine filter order", "Label", Bold], {{Top, Center}} ], Framed@Labeled[ Control[{{filterOrder, 3, ""}, 1, maxFilterOrderAllowed , 1, Appearance -> "Labeled", ImageSize -> Small, Enabled -> Dynamic[filterOrderType == filterOrderSpecified]} ], Style["filter order", "Label", Bold], {{Top, Center}} ] }, Alignment -> Center, Spacings -> 1 ], FrameMargins -> 4, ImageMargins -> 1],(*Panel*) Panel[Column[{ Labeled[Control[{ {plotType, 1, ""}, { plotTypeResponseSpectrumDBScale -> Style["digital filter spectrum db", 11], plotTypeResponseSpectrumLinearScale -> Style["digital filter spectrum linear", 11], plotTypeButterworth -> Style["Butterworth gain plot", 11], plotTypeShowPoles -> Style["locations of poles and zeros", 11], plotTypeHSfirstOrder -> Style["H(s) first\[Hyphen]order sections", 11], plotTypeHSSecondOrder -> Style["H(s) second\[Hyphen]order sections", 11], plotTypeHSPolynomial -> Style["H(s) single section", 11], plotTypeHzfirstOrder -> Style["H(z) first\[Hyphen]order sections", 11], plotTypeHzSecondOrder -> Style["H(z) second\[Hyphen]order sections", 11], plotTypeHzPolynomial -> Style["H(z) single section", 11], plotTypeImpluseResponse -> Style["filter impulse response", 11], plotTypeStepResponse -> Style["filter step response", 11], plotTypeRampResponse -> Style["filter ramp response", 11], }, ControlType -> PopupMenu, ImageSize -> Medium, FrameMargins -> 2} ], Style["select result to display", "Label", Bold], {{Top, Center}}], Framed@Labeled[ Control[{{nSamplesForPlotting, 100, Style["", 10]}, 10, 500, 1, Appearance -> "Labeled", ImageSize -> Small}] , Style["number of samples to plot", "Label", Bold], {{Top, Center}} ] }, Alignment -> Center, Spacings -> 1], FrameMargins -> 4 ](*Panel*) }}], ControlPlacement -> Top], {{filterOrderType, 2}, ControlType -> None}, {{method, 1}, ControlType -> None}, {{samplingUsed, 0}, ControlType -> None}, {{normalized, True}, ControlType -> None}, {{plots, 0}, ControlType -> None}, {{ok, True}, ControlType -> None}, {{wpass, 1}, ControlType -> None}, {{wstop, 1}, ControlType -> None}, {{hsSecondOrderForm, 2}, ControlType -> None}, {{hsGain, 0}, ControlType -> None}, {{fullFormHS, 0}, ControlType -> None}, {{fullFormHz, 0}, ControlType -> None}, {{statusMsg, ""}, ControlType -> None}, {{filterOrderFoundByDesign, 0}, ControlType -> None}, {{coeff, 0}, ControlType -> None}, {{\[CapitalOmega]c, 0}, ControlType -> None}, {{hz, 0}, ControlType -> None}, {{hspoles, 0}, ControlType -> None}, {{hzpoles, 0}, ControlType -> None}, {{scale, 0}, ControlType -> None}, ContinuousAction -> False, SynchronousUpdating -> False, AutorunSequencing -> {1, 2}, FrameMargins -> 0, ImageMargins -> 0, TrackedSymbols :> {filterOrderType, filterOrder, method, normalized, stop, pass, passNormalized, stopNormalized, sampling, passdb, stopdb, plotType, nSamplesForPlotting}, Initialization :> { filterOrderMinumum = 2; filterOrderSpecified = 1; impulseInvarianceMappingMethod = 2; bilinearMappingMethod = 1; plotTypeResponseSpectrumDBScale = 1; plotTypeResponseSpectrumLinearScale = 2; plotTypeShowPoles = 4; plotTypeHSfirstOrder = 5; plotTypeHSSecondOrder = 6; plotTypeHSPolynomial = 7; plotTypeHzfirstOrder = 8; plotTypeHzSecondOrder = 9; plotTypeHzPolynomial = 10; plotTypeImpluseResponse = 11; plotTypeStepResponse = 12; plotTypeRampResponse = 13; plotTypeButterworth = 14; useDBScale = 1; useLinearScale = 2; useSquaredScale = 3; maxFilterOrderAllowed = 10; minumumGainAllowed = 0.000001; orderLimitForDisplay = 7; (*---------------------------------------------*) makehs[ hspoles_ /; VectorQ[hspoles, NumberQ], hsgain_ /; NumberQ[hsgain] && Im[hsgain] == 0, s_] := Module[{i}, hsgain/Product[s - hspoles[[i]], {i, 1, Length[hspoles]}] ]; (*---------------------------------------------*) getPartialFractionsCoeff[ hspoles_List /; VectorQ[hspoles, NumberQ], hsgain_ /; NumberQ[hsgain] && Im[hsgain] == 0, s_] := Module[{i}, Chop@ Table[ Limit[makehs[ Delete[hspoles, i], hsgain , s ], s -> hspoles[[i]]], {i, 1, Length[hspoles]}] ]; (*---------------------------------------------*) ticksForMag[] := Module[{}, { {.2 Pi, ".2"}, {.4 Pi, ".4"}, {.6 Pi, ".6"}, {.8 Pi, ".8"}, {Pi, "1"}} ]; (*---------------------------------------------*) ticksForMag[ nyquist_ /; NumberQ[nyquist] && Positive[nyquist] ] := Module[{}, {{.2 Pi , Style[ .2*nyquist ] }, {.4 Pi , Style[ .4*nyquist ]}, {.6 Pi , Style[ .6*nyquist ] }, {.8 Pi , Style[ .8*nyquist ] }, {Pi , Style[ N[nyquist] ]}} ]; (*---------------------------------------------*) formatPolynomialFormHZ[hz_, z_] := Module[{num, den, cnum, cden, formNumerator, formDen, len, hzz, i}, hzz = Together[hz]; num = Numerator[hzz]; den = Denominator[hzz]; cnum = CoefficientList[num, z]; cden = CoefficientList[den, z]; len = Length[cden]; formDen = Table[If[i == 1, 1, Superscript[z, -(i - 1)]], {i, 1, len}]; len = Length[cnum]; formNumerator = Table[If[i == 1, 1, Superscript[z, -(i - 1)]], {i, 1, len}]; formDen = Reverse[formDen]; formNumerator = Reverse[formNumerator]; Dot[formNumerator, cnum]/Dot[formDen, cden] ]; (*---------------------------------------------*) polesPlot[ hzpoles_ /; VectorQ[hzpoles, NumberQ], hspoles_ /; VectorQ[hspoles, NumberQ], \[CapitalOmega]c_ /; NumberQ[\[CapitalOmega]c] && Positive[\[CapitalOmega]c], filterOrder_ /; NumberQ[filterOrder] && Positive[filterOrder], method_ /; NumberQ[method] && Positive[method] ] := Module[{i, hsPolesLocations, hzPolesLocations, hsPolesPlot, hzPolesPlot, t, commonPlotOptions, maxPolesToShow = 10}, hsPolesLocations = Table[{ComplexExpand[Re[hspoles[[i]]]], ComplexExpand[Im[hspoles[[ i]]]]}, {i, 1, Length[hspoles]}]; hzPolesLocations = Table[{ComplexExpand[Re[hzpoles[[i]]]], ComplexExpand[Im[hzpoles[[ i]]]]}, {i, 1, Length[hzpoles]}]; commonPlotOptions = {ImageMargins -> 1, ImagePadding -> All, ImageSize -> Full, Frame -> True, AspectRatio -> .7, Ticks -> None, PlotStyle -> {Thin, Black}, TicksStyle -> Directive[10], Exclusions -> None, GridLines -> Automatic}; hsPolesPlot = Plot[0, {t, -\[CapitalOmega]c, \[CapitalOmega]c}, PlotRange -> {{-1.1 \[CapitalOmega]c, 1.1 \[CapitalOmega]c}, {-1.1 \[CapitalOmega]c, 1.1 \[CapitalOmega]c}}, Evaluate[commonPlotOptions], PlotLabel -> Text@Row[{Style[ Row[{Style["H", Italic], "(", Style["s", Italic], ") poles, ", Subscript["\[CapitalOmega]", Style["c", Italic]], " = ", numIt[N[\[CapitalOmega]c], 6, 4]}], 10]}], Epilog -> {Circle[{0, 0}, \[CapitalOmega]c], Table[ Text[Style["\[Cross]", Thick, Red, 18], hsPolesLocations[[i]]], {i, 1, Length[hsPolesLocations]}]}]; hzPolesPlot = Plot[0, {t, -1, 1.1}, Evaluate[commonPlotOptions], PlotRange -> {{-1.1, 1.1 }, {-1.1, 1.1}}, PlotLabel -> Style[Row[{Style["H", Italic], "(", Style["z", Italic], ") poles/zeros, ", Style["N", Italic], " = ", Style[filterOrder, 10]}], 10], Epilog -> { Circle[{0, 0}, 1], Table[ Text[Style["\[Cross]", Thick, Red, 18], hzPolesLocations[[i]]], {i, 1, Length[hzPolesLocations]}], If[method == bilinearMappingMethod, { Text[Style["o", Thick, Black, 20], {-1, 0}], Text[Style[Length[hzPolesLocations], Black, 12], {-.9, 0}, {0, -1}]}, Text["", {0, 0}]]}]; Grid[{ {GraphicsRow[{hsPolesPlot, hzPolesPlot}, ImageSize -> Full]}, {GraphicsRow[{TableForm[ Table[{numIt[hsPolesLocations[[i, 1]], 6, 4], numIt[hsPolesLocations[[ i, 2] ], 6, 4] }, {i, 1, If[filterOrder > maxPolesToShow, maxPolesToShow, filterOrder]}], TableAlignments -> Center, TableHeadings -> {None, {Style["Re", 11], Style["Im", 11]}}] , TableForm[ Table[{numIt[hzPolesLocations[[i, 1]], 6, 4], numIt[hzPolesLocations[[ i, 2] ], 6, 4] , numIt[EuclideanDistance[{0, 0}, {hzPolesLocations[[i, 1]], hzPolesLocations[[i, 2]]}], 6, 4]}, {i, 1, If[filterOrder > maxPolesToShow, maxPolesToShow, filterOrder]}], TableAlignments -> Center, TableHeadings -> {None, {Style["Re", 11], Style["Im", 11], Style[Row[{, "|", Style["z", Italic], "|"}], 11]}}] }, ImageSize -> Full]}}] ]; (*---------------------------------------------*) magPhasePlot[hz_,(* H(z) *) (nyquist_ /; NumberQ[nyquist] && Positive[nyquist]), (vscale_ /; IntegerQ[vscale]), (* for vertical scale, 1 or 2 or 3 *) z_ ,(* H(z) variable*) isNormalized_ ] := Module[{dtft, \[Omega], mag, phase, absMag, yTitle, data, i, commonPlotOptions}, dtft = hz /. z -> Exp[I \[Omega]] ; dtft = Total@dtft; absMag = Abs[ComplexExpand@dtft]; Which[ vscale == useLinearScale, yTitle = Style["magnitude", 12], vscale == useDBScale, {absMag = 10 Log[10, absMag], yTitle = Style["gain (db)", 12]} ]; commonPlotOptions = {ImagePadding -> {{65, 10}, {40, 5}}, ImageSize -> Full, ImageMargins -> 1, AxesOrigin -> {0, 0}, Frame -> True, AspectRatio -> .40, Frame -> True, PlotStyle -> {Thick, Red}, TicksStyle -> Directive[10], Joined -> True, GridLines -> {Range[0, Pi, .1 Pi], Automatic}}; data = Table[{i, absMag /. \[Omega] -> i}, {i, 0, Pi, Pi/100}]; mag = ListPlot[ data, Evaluate[commonPlotOptions], GridLines -> {Range[0, Pi, .1 Pi], Automatic}, FrameTicks -> {{Automatic, None}, {If[isNormalized, ticksForMag[], ticksForMag[nyquist]], None}}, PlotRange -> {{0, Pi}, Automatic}, FrameLabel -> {{yTitle, None}, {If[isNormalized, Style["normalized frequency", 12], Style["frequency (hz)", 12]], None}} ]; data = Table[{i, 180/Pi Arg[ComplexExpand[dtft /. \[Omega] -> i]]}, {i, 0, Pi, Pi/100}]; phase = ListPlot[data, Evaluate[commonPlotOptions], FrameTicks -> {{Range[-200, 200, 40], None}, {If[isNormalized, ticksForMag[], ticksForMag[nyquist]], None}}, PlotRange -> {{0, Pi}, {-200, 200}}, FrameLabel -> {{Style["phase (degree)", 12], None}, {If[isNormalized, Style["normalized frequency", 12], Style["frequency (hz)", 12]], None}} ]; Labeled[GraphicsColumn[{mag, phase}, ImageSize -> Full], Text@Style["digital filter frequency response", 12, Bold], {{Top, Center}}] ]; (*---------------------------------------------*) numIt[v_?(NumberQ[#] &), s1_?(NumberQ[#] && Positive[#] &), s2_?(NumberQ[#] && Positive[#] &)] := Module[{}, Style[ ToString[ AccountingForm[Chop[v], {s1, s2}, NumberPadding -> {" ", "0"}, NumberSigns -> {"-", ""}]], 11] ]; (*---------------------------------------------*) str[expr_] := Module[{}, StringReplace[ToString[expr, FormatType -> TraditionalForm], c : LetterCharacter ~~ "$" ~~ DigitCharacter .. :> c] ]; (*---------------------------------------------*) butterd[ fs_?(NumberQ[#] && Positive[#] &), (* sampling frequency*) wpass_?(NumberQ[#] && Positive[#] &), (* passband corner frequency*) wstop_?(NumberQ[#] && Positive[#] &), (* stopband corner frequency*) \[Delta]p_?(NumberQ[#] && Negative[#] &), (* attenuation at passband in db*) \[Delta]s_?(NumberQ[#] && Negative[#] &), (* attenuation at stopband in db*) method_?(NumberQ[#] && Positive[#] &) (* bilinear or impulse invariance*) ] := Module[{T = 1/fs, \[Alpha]s, \[Alpha]p, filterOrder, \[CapitalOmega]c, \[CapitalOmega]pass, \ \[CapitalOmega]stop}, If[method == bilinearMappingMethod, \[CapitalOmega]pass = N[2/T Tan[wpass/2]]; \[CapitalOmega]stop = N[2/T Tan[wstop/2]] , \[CapitalOmega]pass = N[ wpass/T]; \[CapitalOmega]stop = N[wstop/T] ]; \[Alpha]s = N[10^(-\[Delta]s/10)]; \[Alpha]p = N[10^(-\[Delta]p/10)]; filterOrder = 1/2 ((Log[10, \[Alpha]p - 1] - Log[10, \[Alpha]s - 1])/(Log[10, \[CapitalOmega]pass] - Log[10, \[CapitalOmega]stop])); filterOrder = Ceiling[filterOrder]; \[CapitalOmega]c = 0; If[filterOrder <= 10, \[CapitalOmega]c = N@If[method == bilinearMappingMethod, \[CapitalOmega]stop/ 10^(1/(2 filterOrder) Log[10, \[Alpha]s - 1]), \[CapitalOmega]pass/ 10^(1/(2 filterOrder) Log[10, \[Alpha]p - 1])] ]; {filterOrder, \[CapitalOmega]c} ]; (*---------------------------------------------*) getHsStablePoles[filterOrder_?(NumberQ[#] && Positive[#] &), \[CapitalOmega]c_?(NumberQ[#] && Positive[#] &)] := Module[{i, poles}, poles = Table[0, {i, filterOrder}]; Do[{ poles[[ i + 1]] = \[CapitalOmega]c (Cos[(Pi (1 + 2 i + filterOrder))/(2 filterOrder)] + I Sin[(Pi (1 + 2 i + filterOrder))/(2 filterOrder)]);} , {i, 0, Length[poles] - 1} ]; poles ]; (*---------------------------------------------*) getHsGain[ hspoles_ /; VectorQ[hspoles, NumberQ], method_?(NumberQ[#] && Positive[#] &), fs_?(NumberQ[#] && Positive[#] &)] := Module[{k, T = 1/fs, i}, k = Chop[ Product[-hspoles[[i]], {i, 1, Length[hspoles]}]]; If[method == bilinearMappingMethod, k, T*k ] ]; (*---------------------------------------------*) getHsFromPolesSecondOrder[ hspoles_ /; VectorQ[hspoles, NumberQ], s_, coeff_ /; VectorQ[coeff, NumberQ] ] := Module[{i, hs, expr}, If[EvenQ[Length[hspoles]], { hs = Table[0, {i, Length[hspoles]/2}]; Do[{ expr = coeff[[i]]/(s - hspoles[[i]]) + Conjugate@coeff[[i]]/( s - Conjugate@hspoles[[i]]); expr = Normal@Chop@FullSimplify[ComplexExpand[expr]]; expr = ComplexExpand[Numerator[expr]]/ ComplexExpand[Denominator[expr]]; hs[[i]] = expr; }, {i, 1, Length[hs]} ];}, { hs = Table[0, {i, 1 + ((Length[hspoles] - 1)/2)}]; Do[{ expr = coeff[[i]]/(s - hspoles[[i]]) + Conjugate@coeff[[i]]/( s - Conjugate@hspoles[[i]]); expr = Normal@Chop@FullSimplify[ComplexExpand[expr]]; expr = ComplexExpand[Numerator[expr]]/ ComplexExpand[Denominator[expr]]; hs[[i]] = expr; }, {i, 1, Length[hs] - 1} ]; hs[[-1]] = coeff[[(Length[hspoles] - 1)/2 + 1]]/(s - hspoles[[(Length[hspoles] - 1)/2 + 1]]); } ]; hs ]; (*---------------------------------------------*) getHzPoles[ hspoles_ /; VectorQ[hspoles, NumberQ], method_?(NumberQ[#] && Positive[#] &), fs_?(NumberQ[#] && Positive[#] &)] := Module[{ T = 1/fs, i}, If[method == bilinearMappingMethod, Table[(1 + (T/2) hspoles[[i]])/(1 - (T/2) hspoles[[i]]), {i, 1, Length[hspoles]}], Table[Exp[T*hspoles[[i]]], {i, 1, Length[hspoles]}] ] ]; (*---------------------------------------------*) getHzBilinear[hsSecondOrderForm_List, sampling_?(NumberQ[#] && Positive[#] &), s_, z_] := Module[{T = 1/sampling, i}, Table[ FullSimplify[ hsSecondOrderForm[[i]] /. s -> (2/T (1 - z^-1)/(1 + z^-1))], {i, 1, Length[hsSecondOrderForm]}] ]; (*---------------------------------------------*) getHzImpulseInvariance[hspoles_ /; VectorQ[hspoles, NumberQ], coeff_ /; VectorQ[coeff, NumberQ], sampling_?(NumberQ[#] && Positive[#] &), z_] := Module[{T = 1/sampling, i, hz, expr}, If[EvenQ[Length[hspoles]], { hz = Table[0, {i, Length[hspoles]/2}]; Do[{ expr = (z coeff[[i]])/( z - Exp[ hspoles[[i]] T]) + (z Conjugate@coeff[[i]])/(z - Exp[Conjugate@hspoles[[i]] T]); expr = Normal@Chop@FullSimplify[ComplexExpand[expr]]; expr = ComplexExpand[Numerator[expr]]/ ComplexExpand[Denominator[expr]]; hz[[i]] = expr; }, {i, 1, Length[hz]} ]; }, { hz = Table[0, {i, 1 + ((Length[hspoles] - 1)/2)}]; Do[{ expr = (z coeff[[i]])/( z - Exp[ hspoles[[i]] T]) + (z Conjugate@coeff[[i]])/(z - Exp[Conjugate@hspoles[[i]] T]); expr = Normal@Chop@FullSimplify[ComplexExpand[expr]]; expr = ComplexExpand[Numerator[expr]]/ ComplexExpand[Denominator[expr]]; hz[[i]] = expr; }, {i, 1, Length[hz] - 1} ]; hz[[-1]] = (z coeff[[(Length[hspoles] - 1)/2 + 1]])/(z - Exp[T hspoles[[(Length[hspoles] - 1)/2 + 1]]]); } ]; hz ]; (*---------------------------------------------*) doHsPlot[hspoles_ /; VectorQ[hspoles, NumberQ], coeff_ /; VectorQ[coeff, NumberQ], T_?(NumberQ[#] && Positive[#] &), s_] := Module[{i, hs, fontSize}, hs = Total@Table[ coeff[[i]]/(s - hspoles[[i]]), {i, 1, Length[hspoles]}]; If[Length[hs] < orderLimitForDisplay, fontSize = 24, fontSize = 16]; Grid[{ {Text@TraditionalForm[Style[hs, fontSize]]} }, Alignment -> Center, Spacings -> 0] ]; (*---------------------------------------------*) doHzPlot[hspoles_ /; VectorQ[hspoles, NumberQ], coeff_ /; VectorQ[coeff, NumberQ], hzpoles_ /; VectorQ[hzpoles, NumberQ], T_?(NumberQ[#] && Positive[#] &), z_] := Module[{i, hz, fontSize}, hz = Total@Table[ (coeff[[i]])/(1 - z^-1*Exp[hspoles[[i]] T]) , {i, 1, Length[hspoles]}]; If[Length[hz] < orderLimitForDisplay, fontSize = 24, fontSize = 16]; Grid[{ {Text@TraditionalForm[Style[hz, fontSize]]} }, Alignment -> Center, Spacings -> 0] ]; (*---------------------------------------------*) doHsFullPlot[hs_] := Module[{fontSize}, fontSize = 16; Grid[{ {Text@TraditionalForm[Style[hs, fontSize]]}}, Alignment -> Center, Spacings -> 0] ]; (*---------------------------------------------*) doHzFullPlot[hz_] := Module[{fontSize}, fontSize = 16; Grid[{{Text@TraditionalForm[Style[hz, fontSize]]}}, Alignment -> Center, Spacings -> 0] ]; (*---------------------------------------------*) doHs2Plot[hs_, s_] := Module[{i, myhs, c, fontSize}, myhs = Table[0, {i, Length[hs]}]; Do[ { myhs[[i]] = Expand@FullSimplify[Numerator[hs[[i]]]]/ Expand@FullSimplify[Denominator[hs[[i]]]]; c = CoefficientList[Denominator[hs[[i]]], s]; If[Length[c] == 3, { c = c[[3]]; myhs[[i]] = Expand@FullSimplify[Numerator[myhs[[i]]]/c] / Expand@FullSimplify[Denominator[myhs[[i]]]/c] ;} ];}, {i, 1, Length[hs]}]; If[Length[hs] < orderLimitForDisplay, fontSize = 24, fontSize = 18]; Grid[{{Text@TraditionalForm[Style[Total@myhs, fontSize]]}}, Alignment -> Center, Spacings -> 0] ]; (*---------------------------------------------*) doHz2Plot[hz_, z_] := Module[{i, myhz, c, fontSize}, myhz = Table[0, {i, Length[hz]}]; Do[{myhz[[i]] = formatPolynomialFormHZ[hz[[i]], z];} , {i, 1, Length[hz]}]; If[Length[myhz] < orderLimitForDisplay, fontSize = 24, fontSize = 18]; Grid[{ {Text@TraditionalForm[Style[Total@myhz, fontSize]]}}, Alignment -> Center, Spacings -> 0] ]; (*---------------------------------------------*) inverseZtransform[hz_, z_, n_?(IntegerQ[#] && Positive[#] &)] := Module[{num, den, h}, num = Chop[Numerator[hz], 10^-5]; If[num == 0, num = 1]; den = Denominator[hz]; h = Normal[Series[num/den, {z, Infinity, n}]]; CoefficientList[h, z^(-1) ] ]; (*---------------------------------------------*) doStepResponsePlot[hs_, s_, hz_, z_, sampleTime_, nSamplesForPlotting_] := Module[{contResponse, discreteResponse = 0, t, i, p1, p2, commonPlotOptions, maxTime}, maxTime = nSamplesForPlotting*sampleTime; Do[ {discreteResponse = discreteResponse + inverseZtransform[(1/(1 - z^(-1))) hz[[i]], z, nSamplesForPlotting]; }, {i, 1, Length[hz]} ]; contResponse = Chop@InverseLaplaceTransform[hs/s, s, t]; commonPlotOptions = {ImagePadding -> {{70, 10}, {40, 30}}, Frame -> True, ImageSize -> Full, AxesOrigin -> {0, 0}, ImageMargins -> 1, AspectRatio -> .35, Frame -> True, TicksStyle -> Directive[10]}; p1 = Plot[Chop@contResponse, {t, 0, maxTime}, Evaluate[commonPlotOptions], PlotRange -> {{0, maxTime}, {0, 1.3}}, FrameLabel -> {{Text@Style["amplitude", 12], None}, {Text@Style["time (sec)", 12], Text@Style[ Row[{Style["h", Italic], "(", Style["t", Italic], ") analog step response"}], 12]}} ]; p2 = ListPlot[discreteResponse, Evaluate[commonPlotOptions], PlotRange -> {All, {0, 1.3}}, FrameLabel -> {{Style["amplitude", 12], None}, {Text@Style["sample number", 12], Text@Style[ Row[{Style["h", Italic], "(", Style["n", Italic], ") discrete unit step response"}], 12]}}, Filling -> Axis]; GraphicsColumn[{p1, p2}, ImageSize -> Full] ]; (*---------------------------------------------*) doImpulseResponsePlot[hs_, s_, hz_, z_, sampleTime_, nSamplesForPlotting_] := Module[{contResponse, discreteResponse = 0, t, i, p1, p2, commonPlotOptions, maxTime}, maxTime = nSamplesForPlotting*sampleTime; Do[ {discreteResponse = discreteResponse + inverseZtransform[hz[[i]], z, nSamplesForPlotting];}, {i, 1, Length[hz]} ]; contResponse = Chop@InverseLaplaceTransform[hs, s, t]; commonPlotOptions = {ImagePadding -> {{70, 10}, {40, 30}}, ImageSize -> Full, ImageMargins -> 1, PlotRange -> All, AxesOrigin -> {0, 0}, Frame -> True, AspectRatio -> .35, Frame -> True, TicksStyle -> Directive[10]}; p1 = Plot[Chop@contResponse, {t, 0, maxTime}, Evaluate[commonPlotOptions], FrameLabel -> {{Text@Style["amplitude", 12], None}, {Text@Style["time (sec)", 12], Text@Style[ Row[{Style["h", Italic], "(", Style["t", Italic], ") analog impulse response"}], 12]}} ]; p2 = ListPlot[discreteResponse, Evaluate[commonPlotOptions], FrameLabel -> {{Text@Style["amplitude", 12], None}, {Text@Style["sample number", 12], Text@Style[ Row[{Style["h", Italic], "(", Style["n", Italic], ") discrete unit impulse response"}], 12]}}, Filling -> Axis]; GraphicsColumn[{p1, p2}, ImageSize -> Full] ]; (*---------------------------------------------*) doRampResponsePlot[hs_, s_, hz_, z_, sampleTime_, nSamplesForPlotting_] := Module[{contResponse, discreteResponse = 0, t, i, p1, p2, maxTime}, maxTime = nSamplesForPlotting*sampleTime; Do[ { discreteResponse = discreteResponse + inverseZtransform[hz[[i]]*(sampleTime*z/(z - 1)^2), z, nSamplesForPlotting]; }, {i, 1, Length[hz]}]; contResponse = Chop@InverseLaplaceTransform[hs*1/s^2, s, t]; p1 = Plot[Chop@contResponse, {t, 0, maxTime}, ImagePadding -> {{65, 10}, {40, 30}}, ImageSize -> Full, PlotRange -> All, Frame -> True, FrameLabel -> {{Text@Style["amplitude", 12], None}, {Text@Style["time (sec)", 12], Text@Style[ Row[{Style["h", Italic], "(", Style["t", Italic], ") analog ramp response"}], 12]}}, AxesOrigin -> {0, 0}, AspectRatio -> .35, TicksStyle -> Directive[10]]; p2 = ListPlot[discreteResponse, Frame -> True, ImagePadding -> All, ImageSize -> Full, FrameLabel -> {{Text@Style["amplitude", 12], None}, {Text@Style["sample number", 12], Text@Style[ Row[{Style["h", Italic], "(", Style["t", Italic], ") discrete unit ramp response"}], 12]}}, Filling -> Axis, PlotRange -> All, AxesOrigin -> {0, 0}, AspectRatio -> .45, TicksStyle -> Directive[10]]; GraphicsColumn[{p1, p2}, ImageSize -> Full] ]; (*---------------------------------------------*) doplotTypeButterworth[ filterOrderFoundByDesign_, \[CapitalOmega]c_] := Module[{data, w, commonPlotOptions, maxX = 5*\[CapitalOmega]c, title}, commonPlotOptions = {ImagePadding -> {{35, 10}, {40, 90}}, ImageSize -> Full, ImageMargins -> 1, AxesOrigin -> {0, 0}, Frame -> True, AspectRatio -> .40, Frame -> True, PlotStyle -> {Thick, Red}, TicksStyle -> Directive[10], Joined -> True, GridLines -> Automatic}; title = Column[{Text@ Style[Row[{Style["H", Italic], "(\[Omega]) = ", "\!\(\*FractionBox[\(1\), SqrtBox[\(1 + \*SuperscriptBox[\ \((\*FractionBox[\(\[Omega]\), SubscriptBox[\(\[Omega]\), \(c\)]])\), \ \(2 N\)]\)]]\)"}], 16], Spacer[3], Style["Butterworth gain plot (normalized)", Bold, 14]}, Alignment -> Center]; data = Table[{w/\[CapitalOmega]c, 1/Sqrt[1 + (w/\[CapitalOmega]c)^(2 \ filterOrderFoundByDesign)]}, {w, 0, maxX, .1}]; ListPlot[data, AxesOrigin -> {0, 0}, Evaluate@commonPlotOptions, FrameLabel -> {{Text@ Style[Row[{"|", Style["H", Italic], "(\[Omega])|"}], 12], None}, {Text@Style["\[Omega]/\[Omega]", 12], title}}, PlotRange -> All, Frame -> True, Joined -> True] ]; (*---------------------------------------------*) doPlotButterworthSpecs[fpass_, fstop_, apass_, astop_, \[CapitalOmega]c_, filterOrderFoundByDesign_] := Module[ {data, w, commonPlotOptions, maxX = 5*\[CapitalOmega]c, title}, commonPlotOptions = {ImagePadding -> All, ImageMargins -> 1, ImageSize -> 180, AspectRatio -> .4, PlotStyle -> {Thick, Red}, TicksStyle -> Directive[10], Ticks -> None, Joined -> True}; title = Style["design specification parameters", 12]; data = Table[{w, 20 Log[10, 1/Sqrt[1 + (w/\[CapitalOmega]c)^(2 \ filterOrderFoundByDesign)]]}, {w, 0, maxX, .1}]; Show[ ListPlot[data, AxesOrigin -> {0, -50}, Evaluate@commonPlotOptions, PlotRange -> {{-1.5, 6}, {0, -66}}, AxesLabel -> {Text@Style["\[Omega]", 12], Text@Style[Row[{"|", Style["H", Italic], "(\[Omega])| db"}], 12]}, PlotLabel -> title, PlotRange -> All, Joined -> True, Epilog -> { {Dashed, Line[{{0, apass}, {fpass, apass}}]}, {Dashed, Line[{{fpass, -50}, {fpass, apass}}]}, {Dashed, Line[{{0, astop}, {fstop, astop}}]}, {Dashed, Line[{{fstop, -50}, {fstop, astop}}]} }], Graphics@ Text[ Style["\!\(\*SubscriptBox[\(A\), \(pass\)]\)", 12], {-.65, apass}], Graphics@ Text[Style["\!\(\*SubscriptBox[\(A\), \(stop\)]\)", 12], {-.65, astop}], Graphics@ Text[Style["\!\(\*SubscriptBox[\(F\), \(pass\)]\)", 12], {fpass, -60}], Graphics@ Text[Style["\!\(\*SubscriptBox[\(F\), \(stop\)]\)", 12], {fstop, -60}]]]; } ]