Standard library: Numerics
Decibel Factor
Goal: implement functions to convert from Decibel values to factors and vice-versa.
Steps:
Implement the
Decibels
package.
Implement the
To_Decibel
function.Implement the
To_Factor
function.
Requirements:
The subtypes
Decibel
andFactor
are based on a floating-point type.Function
To_Decibel
converts a multiplication factor (or ratio) to decibels.
For the implementation, use \(20 * log_{10}(F)\), where F is the factor/ratio.
Function
To_Factor
converts a value in decibels to a multiplication factor (or ratio).
For the implementation, use \(10^{D/20}\), where D is the value in Decibel.
Remarks:
The Decibel is used to express the ratio of two values on a logarithmic scale.
For example, an increase of 6 dB corresponds roughly to a multiplication by two (or an increase by 100 % of the original value).
You can find the functions that you'll need for the calculation in the
Ada.Numerics.Elementary_Functions
package.
package Decibels is subtype Decibel is Float; subtype Factor is Float; function To_Decibel (F : Factor) return Decibel; function To_Factor (D : Decibel) return Factor; end Decibels;package body Decibels is function To_Decibel (F : Factor) return Decibel is begin return 0.0; end To_Decibel; function To_Factor (D : Decibel) return Factor is begin return 0.0; end To_Factor; end Decibels;with Ada.Command_Line; use Ada.Command_Line; with Ada.Text_IO; use Ada.Text_IO; with Decibels; use Decibels; procedure Main is type Test_Case_Index is (Db_Chk, Factor_Chk); procedure Check (TC : Test_Case_Index; V : Float) is package F_IO is new Ada.Text_IO.Float_IO (Factor); package D_IO is new Ada.Text_IO.Float_IO (Decibel); procedure Put_Decibel_Cnvt (D : Decibel) is F : constant Factor := To_Factor (D); begin D_IO.Put (D, 0, 2, 0); Put (" dB => Factor of "); F_IO.Put (F, 0, 2, 0); New_Line; end; procedure Put_Factor_Cnvt (F : Factor) is D : constant Decibel := To_Decibel (F); begin Put ("Factor of "); F_IO.Put (F, 0, 2, 0); Put (" => "); D_IO.Put (D, 0, 2, 0); Put_Line (" dB"); end; begin case TC is when Db_Chk => Put_Decibel_Cnvt (Decibel (V)); when Factor_Chk => Put_Factor_Cnvt (Factor (V)); end case; end Check; begin if Argument_Count < 2 then Put_Line ("ERROR: missing arguments! Exiting..."); return; elsif Argument_Count > 2 then Put_Line ("Ignoring additional arguments..."); end if; Check (Test_Case_Index'Value (Argument (1)), Float'Value (Argument (2))); end Main;
Root-Mean-Square
Goal: implement a function to calculate the root-mean-square of a sequence of values.
Steps:
Implement the
Signals
package.
Implement the
Rms
function.
Requirements:
Subtype
Sig_Value
is based on a floating-point type.Type
Signal
is an unconstrained array ofSig_Value
elements.Function
Rms
calculates the RMS of a sequence of values stored in an array of typeSignal
.
See the remarks below for a description of the RMS calculation.
Remarks:
The root-mean-square (RMS) value is an important information associated with sequences of values.
It's used, for example, as a measurement for signal processing.
It is calculated by:
Creating a sequence \(S\) with the square of each value of an input sequence \(S_{in}\).
Calculating the mean value \(M\) of the sequence \(S\).
Calculating the square-root \(R\) of \(M\).
You can optimize the algorithm above by combining steps #1 and #2 into a single step.
package Signals is subtype Sig_Value is Float; type Signal is array (Natural range <>) of Sig_Value; function Rms (S : Signal) return Sig_Value; end Signals;with Ada.Numerics.Elementary_Functions; use Ada.Numerics.Elementary_Functions; package body Signals is function Rms (S : Signal) return Sig_Value is begin return 0.0; end; end Signals;package Signals.Std is Sample_Rate : Float := 8000.0; function Generate_Sine (N : Positive; Freq : Float) return Signal; function Generate_Square (N : Positive) return Signal; function Generate_Triangular (N : Positive) return Signal; end Signals.Std;with Ada.Numerics; use Ada.Numerics; with Ada.Numerics.Elementary_Functions; use Ada.Numerics.Elementary_Functions; package body Signals.Std is function Generate_Sine (N : Positive; Freq : Float) return Signal is S : Signal (0 .. N - 1); begin for I in S'First .. S'Last loop S (I) := 1.0 * Sin (2.0 * Pi * (Freq * Float (I) / Sample_Rate)); end loop; return S; end; function Generate_Square (N : Positive) return Signal is S : constant Signal (0 .. N - 1) := (others => 1.0); begin return S; end; function Generate_Triangular (N : Positive) return Signal is S : Signal (0 .. N - 1); S_Half : constant Natural := S'Last / 2; begin for I in S'First .. S_Half loop S (I) := 1.0 * (Float (I) / Float (S_Half)); end loop; for I in S_Half .. S'Last loop S (I) := 1.0 - (1.0 * (Float (I - S_Half) / Float (S_Half))); end loop; return S; end; end Signals.Std;with Ada.Command_Line; use Ada.Command_Line; with Ada.Text_IO; use Ada.Text_IO; with Signals; use Signals; with Signals.Std; use Signals.Std; procedure Main is type Test_Case_Index is (Sine_Signal_Chk, Square_Signal_Chk, Triangular_Signal_Chk); procedure Check (TC : Test_Case_Index) is package Sig_IO is new Ada.Text_IO.Float_IO (Sig_Value); N : constant Positive := 1024; S_Si : constant Signal := Generate_Sine (N, 440.0); S_Sq : constant Signal := Generate_Square (N); S_Tr : constant Signal := Generate_Triangular (N + 1); begin case TC is when Sine_Signal_Chk => Put ("RMS of Sine Signal: "); Sig_IO.Put (Rms (S_Si), 0, 2, 0); New_Line; when Square_Signal_Chk => Put ("RMS of Square Signal: "); Sig_IO.Put (Rms (S_Sq), 0, 2, 0); New_Line; when Triangular_Signal_Chk => Put ("RMS of Triangular Signal: "); Sig_IO.Put (Rms (S_Tr), 0, 2, 0); New_Line; end case; end Check; begin if Argument_Count < 1 then Put_Line ("ERROR: missing arguments! Exiting..."); return; elsif Argument_Count > 1 then Put_Line ("Ignoring additional arguments..."); end if; Check (Test_Case_Index'Value (Argument (1))); end Main;
Rotation
Goal: use complex numbers to calculate the positions of an object in a circle after rotation.
Steps:
Implement the
Rotation
package.
Implement the
Rotation
function.
Requirements:
Type
Complex_Points
is an unconstrained array of complex values.Function
Rotation
returns a list of positions (represented by theComplex_Points
type) when dividing a circle inN
equal slices.
See the remarks below for a more detailed explanation.
You must use functions from
Ada.Numerics.Complex_Types
to implementRotation
.Subtype
Angle
is based on a floating-point type.Type
Angles
is an unconstrained array of angles.Function
To_Angles
returns a list of angles based on an input list of positions.
Remarks:
Complex numbers are particularly useful in computer graphics to simplify the calculation of rotations.
For example, let's assume you've drawn an object on your screen on position (1.0, 0.0).
Now, you want to move this object in a circular path — i.e. make it rotate around position (0.0, 0.0) on your screen.
You could use sine and cosine functions to calculate each position of the path.
However, you could also calculate the positions using complex numbers.
In this exercise, you'll use complex numbers to calculate the positions of an object that starts on zero degrees — on position (1.0, 0.0) — and rotates around (0.0, 0.0) for N slices of a circle.
For example, if we divide the circle in four slices, the object's path will consist of following points / positions:
Point #1: ( 1.0, 0.0) Point #2: ( 0.0, 1.0) Point #3: (-1.0, 0.0) Point #4: ( 0.0, -1.0) Point #5: ( 1.0, 0.0)Or graphically:
As expected, point #5 is equal to the starting point (point #1), since the object rotates around (0.0, 0.0) and returns to the starting point.
We can also describe this path in terms of angles. The following list presents the angles for the path on a four-sliced circle:
Point #1: 0.00 degrees Point #2: 90.00 degrees Point #3: 180.00 degrees Point #4: -90.00 degrees (= 270 degrees) Point #5: 0.00 degrees
To rotate a complex number simply multiply it by a unit vector whose arg is the radian angle to be rotated: \(Z = e^\frac{2 \pi}{N}\)
with Ada.Numerics.Complex_Types; use Ada.Numerics.Complex_Types; package Rotation is type Complex_Points is array (Positive range <>) of Complex; function Rotation (N : Positive) return Complex_Points; end Rotation;with Ada.Numerics; use Ada.Numerics; package body Rotation is function Rotation (N : Positive) return Complex_Points is C : Complex_Points (1 .. 1) := (others => (0.0, 0.0)); begin return C; end; end Rotation;with Rotation; use Rotation; package Angles is subtype Angle is Float; type Angles is array (Positive range <>) of Angle; function To_Angles (C : Complex_Points) return Angles; end Angles;with Ada.Numerics; use Ada.Numerics; with Ada.Numerics.Complex_Types; use Ada.Numerics.Complex_Types; package body Angles is function To_Angles (C : Complex_Points) return Angles is begin return A : Angles (C'Range) do for I in A'Range loop A (I) := Argument (C (I)) / Pi * 180.0; end loop; end return; end To_Angles; end Angles;package Rotation.Tests is procedure Test_Rotation (N : Positive); procedure Test_Angles (N : Positive); end Rotation.Tests;with Ada.Text_IO; use Ada.Text_IO; with Ada.Text_IO.Complex_IO; with Ada.Numerics; use Ada.Numerics; with Angles; use Angles; package body Rotation.Tests is package C_IO is new Ada.Text_IO.Complex_IO (Complex_Types); package F_IO is new Ada.Text_IO.Float_IO (Float); -- -- Adapt value due to floating-point inaccuracies -- function Adapt (C : Complex) return Complex is function Check_Zero (F : Float) return Float is (if F <= 0.0 and F >= -0.01 then 0.0 else F); begin return C_Out : Complex := C do C_Out.Re := Check_Zero (C_Out.Re); C_Out.Im := Check_Zero (C_Out.Im); end return; end Adapt; function Adapt (A : Angle) return Angle is (if A <= -179.99 and A >= -180.01 then 180.0 else A); procedure Test_Rotation (N : Positive) is C : constant Complex_Points := Rotation (N); begin Put_Line ("---- Points for " & Positive'Image (N) & " slices ----"); for V of C loop Put ("Point: "); C_IO.Put (Adapt (V), 0, 1, 0); New_Line; end loop; end Test_Rotation; procedure Test_Angles (N : Positive) is C : constant Complex_Points := Rotation (N); A : constant Angles.Angles := To_Angles (C); begin Put_Line ("---- Angles for " & Positive'Image (N) & " slices ----"); for V of A loop Put ("Angle: "); F_IO.Put (Adapt (V), 0, 2, 0); Put_Line (" degrees"); end loop; end Test_Angles; end Rotation.Tests;with Ada.Command_Line; use Ada.Command_Line; with Ada.Text_IO; use Ada.Text_IO; with Rotation.Tests; use Rotation.Tests; procedure Main is type Test_Case_Index is (Rotation_Chk, Angles_Chk); procedure Check (TC : Test_Case_Index; N : Positive) is begin case TC is when Rotation_Chk => Test_Rotation (N); when Angles_Chk => Test_Angles (N); end case; end Check; begin if Argument_Count < 2 then Put_Line ("ERROR: missing arguments! Exiting..."); return; elsif Argument_Count > 2 then Put_Line ("Ignoring additional arguments..."); end if; Check (Test_Case_Index'Value (Argument (1)), Positive'Value (Argument (2))); end Main;