From mboxrd@z Thu Jan 1 00:00:00 1970 X-Spam-Checker-Version: SpamAssassin 3.4.4 (2020-01-24) on polar.synack.me X-Spam-Level: X-Spam-Status: No, score=-1.9 required=5.0 tests=BAYES_00,FREEMAIL_FROM autolearn=unavailable autolearn_force=no version=3.4.4 X-Received: by 10.236.104.134 with SMTP id i6mr16775165yhg.48.1427387125922; Thu, 26 Mar 2015 09:25:25 -0700 (PDT) X-Received: by 10.182.43.229 with SMTP id z5mr159011obl.22.1427387125889; Thu, 26 Mar 2015 09:25:25 -0700 (PDT) Path: eternal-september.org!reader01.eternal-september.org!reader02.eternal-september.org!news.eternal-september.org!mx02.eternal-september.org!feeder.eternal-september.org!feeds.phibee-telecom.net!newsfeed.xs4all.nl!newsfeed2a.news.xs4all.nl!xs4all!newspeer1.nac.net!border2.nntp.dca1.giganews.com!nntp.giganews.com!z107no4982562qgd.0!news-out.google.com!qk8ni265igc.0!nntp.google.com!z20no527603igj.0!postnews.google.com!glegroupsg2000goo.googlegroups.com!not-for-mail Newsgroups: comp.lang.ada Date: Thu, 26 Mar 2015 09:25:25 -0700 (PDT) Complaints-To: groups-abuse@google.com Injection-Info: glegroupsg2000goo.googlegroups.com; posting-host=74.203.194.21; posting-account=bXcJoAoAAAAWI5APBG37o4XwnD4kTuQQ NNTP-Posting-Host: 74.203.194.21 User-Agent: G2/1.0 MIME-Version: 1.0 Message-ID: Subject: RFC: Generic Fixed-Point IIR Filter From: Patrick Noffke Injection-Date: Thu, 26 Mar 2015 16:25:25 +0000 Content-Type: text/plain; charset=ISO-8859-1 Content-Transfer-Encoding: quoted-printable Xref: news.eternal-september.org comp.lang.ada:25264 Date: 2015-03-26T09:25:25-07:00 List-Id: I would greatly appreciate some feedback/help on implementing a generic fix= ed-point IIR filter package. I am attempting to define a fixed-point type (Pressure_Type) that has a lim= ited range and precision. When doing things (like filtering) to values of = this type, intermediate calculations require a larger range and possibly hi= gher precision. But I don't want to expand the range/precision of the base= type -- rather define new types as appropriate for the required intermedia= te calculations. Below is my first attempt. I noticed the following issues: - I cannot define Filter_Value_Type within the IIR_Filter package (i.e. it= has to be a generic parameter). If I try to put it in the package and bas= e it on Fixed_Type, I get an error that a non-static expression is used for= the delta and range. For example, this gives the error I just mentioned: type Filter_Value_Type is delta Fixed_Type'Delta / 10 range N_A * Fixed_Type'First .. N_A * Fixed_Type'Last; I realize (from http://en.wikibooks.org/wiki/Ada_Programming/Generics) that= generic formal objects are non-static, but it's not clear to me why this w= on't work for a generic formal type (how can a type be non-static?). Is th= ere another way to parameterize the delta and range for intermediate calcul= ations? - If I try to use Fixed_Type for X_Array and Y_Array, then I do in fact ge= t a constraint error (due to range overflow for an intermediate calculation= ). Is there a better way to do a calculation for a fixed-point type when y= ou expect the result to be within the specified range of Fixed_Type, but in= termediate calculations may not? - I get warnings that the filter constants are not a multiple of small. I= understand why this is (small is the largest (most positive) negative powe= r of two not greater than delta). I can explicitly modify the constants to= be a multiple of small, but is there another way to define the constants t= hat will "convert" them to a multiple of small? - I realize my range and precision for Filter_Value_Type are not necessari= ly ideal (the range depends on the filter coefficients, the filter architec= ture, as well as the filter order). Do you know of any references or have = suggestions for how to analyze a filter to determine the ideal range and pr= ecision? iir_filter.ads: generic -- The fixed-point numeric type for the filter input and output. type Fixed_Type is delta <>; -- The fixed-point numeric type for intermediate filter calculations. -- Should have enough range to avoid overflow and enough precision so -- rounding errors are insignificant. type Filter_Value_Type is delta <>; -- Number of "a" coefficients. N_A : Positive; -- Number of "b" coefficients. N_B : Positive; package IIR_Filter is type A_Coefficient_Array_Type is array (1 .. N_A) of Filter_Value_Type; type B_Coefficient_Array_Type is array (1 .. N_B) of Filter_Value_Type; type IIR_Filter_Type is private; procedure Init_Filter (Filter : in out IIR_Filter_Type; A_Coefficients : A_Coefficient_Array_Type; B_Coefficients : B_Coefficient_Array_Type; Initial_Value : Fixed_Type); function Step_Filter (Filter : in out IIR_Filter_Type; X : Fixed_Type) return Fixed_Type; private type Y_Array_Type is array (1 .. N_A) of Filter_Value_Type; type X_Array_Type is array (1 .. N_B) of Filter_Value_Type; type IIR_Filter_Type is record A : A_Coefficient_Array_Type; B : B_Coefficient_Array_Type; X_Array : X_Array_Type; Y_Array : Y_Array_Type; end record; end IIR_Filter; iif_filter.adb: package body IIR_Filter is procedure Init_Filter (Filter : in out IIR_Filter_Type; A_Coefficients : A_Coefficient_Array_Type; B_Coefficients : B_Coefficient_Array_Type; Initial_Value : Fixed_Type) is begin Filter.A :=3D A_Coefficients; Filter.B :=3D B_Coefficients; for X_Index in Filter.X_Array'Range loop Filter.X_Array (X_Index) :=3D Filter_Value_Type (Initial_Value); end loop; for Y_Index in Filter.Y_Array'Range loop Filter.Y_Array (Y_Index) :=3D Filter_Value_Type (Initial_Value); end loop; end Init_Filter; function Step_Filter (Filter : in out IIR_Filter_Type; X : Fixed_Type) return Fixed_Type is begin -- Compute IIR filter using Direct Form I. -- Move the elements to next oldest position. -- TODO: Use a circular buffer for this. if Filter.X_Array'Length > 1 then for X_Index in reverse 2 .. Filter.X_Array'Last loop Filter.X_Array (X_Index) :=3D Filter.X_Array (X_Index - 1); end loop; end if; if Filter.Y_Array'Length > 1 then for Y_Index in reverse 2 .. Filter.Y_Array'Last loop Filter.Y_Array (Y_Index) :=3D Filter.Y_Array (Y_Index - 1); end loop; end if; -- Store the new X value. Filter.X_Array (1) :=3D Filter_Value_Type (X); -- Compute new filter output. Initialize with b(1) * x(n). Filter.Y_Array (1) :=3D Filter.B (1) * X; -- Compute b(2) * x(n - 1) + b(3) * x(n - 2) + ... if Filter.X_Array'Length > 1 then for X_Index in 2 .. Filter.X_Array'Last loop Filter.Y_Array (1) :=3D Filter.Y_Array (1) + Filter.B (X_Index) * Filter.X_Array (X_Index); end loop; end if; -- Compute -a(2) * y(n - 1) - a(3) * y(n - 2) + ... if Filter.Y_Array'Length > 1 then for Y_Index in 2 .. Filter.Y_Array'Last loop Filter.Y_Array (1) :=3D Filter.Y_Array (1) - Filter.A (Y_Index) * Filter.Y_Array (Y_Index); end loop; end if; -- Divide by a(1). Filter.Y_Array (1) :=3D Filter.Y_Array (1) / Filter.A (1); return Fixed_Type (Filter.Y_Array (1)); end Step_Filter; end IIR_Filter; pressure.ads: package Pressure is type Pressure_Type is delta 0.001 range -20.0 .. 400.0; end Pressure; Then in my main procedure: procedure Filter_Test is N_A : constant Positive :=3D 3; N_B : constant Positive :=3D 3; type Filter_Value_Type is delta Pressure.Pressure_Type'Delta / 10 range N_A * Pressure.Pressure_Type'First .. N_A * Pressure.Pressure_Ty= pe'Last; package Pressure_IIR_Filter is new IIR_Filter (Fixed_Type =3D> Pressure.Pressure_Type, Filter_Value_Type = =3D> Filter_Value_Type, N_A =3D> N_A, N_B =3D> N_B); Pressure_Filter : Pressure_IIR_Filter.IIR_Filter_Type; A_Coeffs : constant Pressure_IIR_Filter.A_Coefficient_Array_Type :=3D (1.0, -1.84036, 0.85219); B_Coeffs : constant Pressure_IIR_Filter.B_Coefficient_Array_Type :=3D (0.0029583, 0.0059165, 0.0029583); Filtered_Pressure : Pressure.Pressure_Type :=3D 0.0; begin Pressure_IIR_Filter.Init_Filter (Pressure_Filter, A_Coeffs, B_Coeffs, 0.= 0); for Step_Index in 1 .. 200 loop Filtered_Pressure :=3D Pressure_IIR_Filter.Step_Filter (Pressure_Filter, 300.0); Put_Line ("Step: " & Step_Index'Img & ", Filtered_Pressure =3D " & Filtered_Pressure'Img); end loop; end Filter_Test; Regards, Patrick