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 autolearn=ham autolearn_force=no version=3.4.4 X-Google-Thread: 103376,f822ae7b0f7433c1 X-Google-Attributes: gid103376,public X-Google-Language: ENGLISH,ASCII-7-bit Path: g2news2.google.com!news4.google.com!news.glorb.com!uio.no!fi.sn.net!newsfeed1.fi.sn.net!news.song.fi!not-for-mail Date: Thu, 18 Jan 2007 18:17:34 +0200 From: Niklas Holsti User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.7.8) Gecko/20060628 Debian/1.7.8-1sarge7.1 X-Accept-Language: en MIME-Version: 1.0 Newsgroups: comp.lang.ada Subject: Re: Translating an embedded C algorithm References: <1168871816.263502.212100@11g2000cwr.googlegroups.com> <45acc0c3$0$22514$39db0f71@news.song.fi> In-Reply-To: <45acc0c3$0$22514$39db0f71@news.song.fi> Content-Type: text/plain; charset=us-ascii; format=flowed Content-Transfer-Encoding: 7bit Message-ID: <45af9c60$0$22524$39db0f71@news.song.fi> Organization: TDC Song Internet Services NNTP-Posting-Host: laku61.adsl.netsonic.fi X-Trace: 1169136736 news.song.fi 22524 81.17.205.61:32851 X-Complaints-To: abuse@song.fi Xref: g2news2.google.com comp.lang.ada:8262 Date: 2007-01-18T18:17:34+02:00 List-Id: Niklas Holsti wrote: > ... > > I agree with other posters that simply translating this function into > Ada does not give a good comparison between C and Ada. The first steps > in an Ada design would be to consider encapsulation (packages) and data > representation (types), for which the C version gives no model. I'm replying to my own post because I've changed my opinion a bit: I think that an Ada translation of this C function can, with a bit of elaboration, illustrate several Ada features for "programming in the small", even if not for "programming in the large". So here is my try at an Ada version of the code and some of the context. This makes for a long post, for which I apologize in advance. Putting the sources somewhere for download seemed like overkill, and I prefer to expose my suggestion to the purifying flames of c.l.a instead of sending it privately to the OP. The Ada code below has been compiled and tested (gnat 3.15p, Debian). It is hereby put in the public domain. In this code, I tried to use as many relevant Ada features as possible, without trying to limit myself to some "8-bit subset". It would be interesting to compile this code with AVR-Ada; I don't happen to have that installed, but I may try it later, if no-one else does it. First, the context. The C function deals with two kinds of data: ADC readings ("counts") and temperatures expressed in deci-degrees Celsius (that is, units of 0.1 degrees C). The ADC count-type does not depend on temperatures, and the temperature type does not depend on the ADC, so I would define these types in different packages, so: package ADC is type Count_T is range 0 .. 1023; -- -- An ADC reading. -- More ADC stuff ... end ADC; and package Temperatures is type Celsius_T is delta 0.1 digits 5 range -40.0 .. 120.0; -- -- A temperature in degrees Celsius. -- More temperature stuff ... end Temperatures; The function to convert ADC counts to temperatures then seems suitable for a child of package ADC. Here is the package declaration, in which I have added a Saturation option and two Ada idioms of reporting out-of-range results (discriminated type; exception): with Temperatures; package ADC.Thermometer is type Reading_T (In_Range : Boolean := False) is record case In_Range is when False => null; when True => Temperature : Temperatures.Celsius_T; end case; end record; -- -- A temperature, measured through the ADC. -- The ADC reading can be In the expected Range, in which -- case we have a Temperature. If the reading is out of -- range, we have no temperature (it might be interesting -- to have here the ADC count, for diagnostic purposes). -- -- The default initial value of a Reading is "not In Range". Saturation : Boolean := True; -- -- Whether the temperature is saturated at its lower or -- upper limit when the ADC count is out of range. -- If this option is True, the Temperature functions below -- always return a temperature, never an out-of-range -- indication. If the ADC count is below the minimum -- expected value, the minimum temperature is returned; -- if the ADC count is above the maximum expected value, -- the maximum temperature is returned. If this option is -- False, the Temperature functions do not return a -- temperature when the ADC count is out of range but -- instead give an out-of-range indication. function Temperature (Count : Count_T) return Reading_T; -- -- The temperature corresponding to the given ADC Count, -- following the Saturation option defined above. When -- saturation is not used the in-range/out-of-range -- status is shown by the In_Range component. Out_Of_Range : exception; -- -- Signals an out-of-range ADC reading for temperature. -- See function Temperature, below. function Temperature (Count : Count_T) return Temperatures.Celsius_T; -- -- The temperature corresponding to the given ADC Count, -- following the Saturation option defined above. When -- saturation is not used and the Count is out of the -- range expected for the thermometer, the function -- propagates the Out_Of_Range exception. end ADC.Thermometer; To finish this posting, here is the package body. It follows the inerpolation logic of the C code but makes saturation optional. Also, it handles separately the case where the Index becomes the last index of the calibration table, where I think the C code uses Index+1 incorrectly as an index to the Table. package body ADC.Thermometer is use type Temperatures.Celsius_T; -- -- We will need to compute with values of this type, so we -- like to have easy access to the operators for this type. -- The conversion from ADC counts to Celsius degrees uses linear -- interpolation in a table of temperatures for a sequence of -- calibration points corresponding to equispaced ADC counts in -- increasing order. Points : constant := 32; -- -- The number of calibration points. type Point_Count_T is range 0 .. Points - 1; -- -- A count of calibration points. For example, the number of -- points (spacings) from the first calibration point to the -- calibration point that precedes a given ADC reading. subtype Index_T is Point_Count_T; -- -- The index of a calibration point. First_Count : constant Count_T := 220; -- -- The ADC count for the first calibration point. Spacing : constant := 8; -- -- The spacing of calibration points (difference in ADC counts -- between two adjacent calibration points). Last_Count : constant Count_T := First_Count + (Points - 1) * Spacing; -- -- The ADC count for the last calibration point. Table : constant array (Index_T) of Temperatures.Celsius_T := ( 0 => 117.1, 1 => 111.6, 2 => 106.1, 3 => 100.6, 4 => 95.2, 5 => 89.9, 6 => 84.6, 7 => 79.3, 8 => 74.1, 9 => 68.9, 10 => 63.8, 11 => 58.8, 12 => 53.7, 13 => 48.8, 14 => 43.8, 15 => 38.9, 16 => 34.1, 17 => 29.3, 18 => 24.6, 19 => 19.9, 20 => 15.2, 21 => 10.6, 22 => 6.1, 23 => 1.6, 24 => -2.9, 25 => -7.3, 26 => -11.6, 27 => -16.0, 28 => -20.2, 29 => -24.4, 30 => -28.6, 31 => -32.7); -- -- The calibration table. The first entry corresponds to First_Count -- and the last entry to Last_Count. subtype Interpolation_Distance_T is Natural range 0 .. Spacing - 1; -- -- The difference in ADC counts between an ADC reading and the -- preceding calibration point. function Temperature (Count : Count_T) return Reading_T is Index : Index_T; -- The index in the calibration Table such that the Count -- lies in the interval between the calibration points -- number Index (inclusive) and Index + 1 (exclusive). Interpolation_Distance : Interpolation_Distance_T; -- The difference in ADC counts between Count and the -- calibration point at Table(Index). Temp_Diff : Temperatures.Celsius_T; -- The difference in temperature between the calibration -- points at Index and Index + 1. -- -- We assume that calibration points are spaced closely -- enough that Temp_Diff * (Spacing - 1) stays in range -- for Celsius_T. begin if Count in First_Count .. Last_Count then -- The ADC reading is in range for this calibration table. Index := Point_Count_T ((Count - First_Count) / Spacing) + Table'First; if Index = Table'Last then -- Count is Last_Count, so Index + 1 is out of the Table. -- But in this case we do not need to interpolate. return (In_Range => True, Temperature => Table(Table'Last)); else -- Both Index and Index + 1 are valid Table indices. Interpolation_Distance := Interpolation_Distance_T ( (Count - First_Count) mod Spacing); Temp_Diff := Table(Index) - Table(Index + 1); return ( In_Range => True, Temperature => Table(Index) - (Temp_Diff * Interpolation_Distance) / Spacing); end if; elsif not Saturation then -- We report the out-of-range status. return (In_Range => False); elsif Count < First_Count then -- Saturate at minimum temperature. return (In_Range => True, Temperature => Table(Table'First)); else -- Saturate at maximum temperature. return (In_Range => True, Temperature => Table(Table'Last)); end if; end Temperature; function Temperature (Count : Count_T) return Temperatures.Celsius_T is Reading : constant Reading_T := Temperature (Count); -- The temperature, with an in-range/out-of-range indication. begin if Reading.In_Range then return Reading.Temperature; else raise Out_Of_Range; end if; end Temperature; end ADC.Thermometer; -- Niklas Holsti Tidorum Ltd niklas holsti tidorum fi . @ .