Standard library: Numerics
Decibel Factor
Goal: implement functions to convert from Decibel values to factors and vice-versa.
Steps:
Implement the
Decibelspackage.
Implement the
To_Decibelfunction.Implement the
To_Factorfunction.
Requirements:
The subtypes
DecibelandFactorare based on a floating-point type.Function
To_Decibelconverts a multiplication factor (or ratio) to decibels.
For the implementation, use \(20 * log_{10}(F)\), where F is the factor/ratio.
Function
To_Factorconverts 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_Functionspackage.
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
Signalspackage.
Implement the
Rmsfunction.
Requirements:
Subtype
Sig_Valueis based on a floating-point type.Type
Signalis an unconstrained array ofSig_Valueelements.Function
Rmscalculates 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
Rotationpackage.
Implement the
Rotationfunction.
Requirements:
Type
Complex_Pointsis an unconstrained array of complex values.Function
Rotationreturns a list of positions (represented by theComplex_Pointstype) when dividing a circle inNequal slices.
See the remarks below for a more detailed explanation.
You must use functions from
Ada.Numerics.Complex_Typesto implementRotation.Subtype
Angleis based on a floating-point type.Type
Anglesis an unconstrained array of angles.Function
To_Anglesreturns 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:
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;