* PoP
| 1 | M | Fixed Point Arithmetic                                                               | Single Person | max 5x |
| 2 |   | General fast exponentation according to Legendre / square root with Newton iteration | Single Person | max 3x |
| 3 | M | NVM Manager                                                                          | 2             | max 3x |
| 4 | M | Multiport RAM Interface MPRAM-Interface                                              | Single Person | max 3x |
| 5 |   | Numerical integration methods                                                        | Single Person | max 3x |
with callback function, uses function pointer
was mit power of two is sufficient
| 6 | M | Djikstra algorithm | Single Person | max 5x |
5-7 nodes
| 8 | M | Online methods - Variance according to Welford and arithmetic mean                   | Single person | max 5x |
| 9 |   | Message-based intertask communication                                                | 2             | max 2x |


Numerical integration methods

Topic 5: Numerical integration methods

The aim of this task is to implement three variants of numerical approaches for calculating integrals
in a software module.

A function for the rectangle method, the trapezoid method, and the Simpson method should be designed and coded in a reusable module.

When defining the interface of the C functions, pay attention to reusability.

The task also includes developing a sample application to test the three variants. Select two examples of mathematical functions whose integrals are to be determined, with corresponding integration boundaries, and compare the three methods in terms of their precision. Test the limitations of the methods. Substantiate your results.

Explain the mathematical relationships underlying the methods in your paper. A graphical representation will help to illustrate the understanding of the relationships.

Optimize the source code as far as possible for performance-optimized use on an embedded controller.

The result of the task is a project that can be compiled with MS Studio, which enables the software module to be tested (suitable test cases, etc.) using a command line application, plus the documentation specified below.

Scope of relevant task-specific documentation
- Graphical representation of the software design and compact description of the key relationships (1 page)
- Theoretical explanation of the relationships (mathematical, algorithmic, etc.) that need to be taken into account here (2 pages)
- Documentation of the reference examples used for testing, with graphical representation and explanation of method limitations (2 pages)


** Notizen zu Folien

*** keine Unions
*** enum

Implizite Deklaration:
#+begin_src c 
enum t_AlgoEnumVals{
INIT = 0u,
RUNNING = 1u,
WAITING = 2u,
INVALID = 3u
};
#+end_src
-> definiert nur den Typnamen enum t_AlgoEnumVals, reserviert aber keinen Speicher, solange keine Variable dieses Typs definiert wird.

Explizite Deklaration:
#+begin_src c 
typedef enum{
INIT = 0u,
RUNNING = 1u,
WAITING = 2u,
INVALID = 3u
} t_AlgoEnumVals;
#+end_src

-> macht das Gleiche, nur dass der Typ danach über den Alias t_AlgoEnumVals ohne enum verwendet werden kann; auch hier entsteht Speicher erst bei Variablen/Objekten dieses Typs.

*** kombination union und struct

#+begin_src c
typedef unsigned char t_BYTE;
typedef unsigned short t_WORD;
typedef unsigned long t_DWORD;

#pragma pack(1)

typedef struct{
t_BYTE Wert0;
t_BYTE Wert1;
t_BYTE Wert2;
t_WORD Wert3;
t_DWORD Wert4;
t_DWORD Wert5;
t_DWORD Wert6;
t_WORD Wert7;
t_BYTE Wert8;
}t_Struct;

typedef union{
t_BYTE vbyArray[20];
t_Struct stStruct;
}t_unStructUnion;

#pragma pack()
#+end_src

-> see 05 P9

*** Funktionsaufrufe

  Call by Value:
  #+begin_src c
int func1(int par1, int par2){
    return(par1*par2);
}

void main(void){
    int a = 0;

    // function call by value:
    a = func1(5,3);

    printf("Result %d\n",a);
}
  #+end_src
  -> Werte werden über Stack direkt übergeben.

Call by Reference:
  #+begin_src c
int func1(int par1, int par2){
    *par1 = 5;
    *par2 = 2;
}

void main(void){
    int a, b = 0;

    // function call by reference:
    func1(&a, &b);

    printf("Result %d, %d\n", a, b);
}
  #+end_src
-> Referenzen auf Variablen werden übergeben.
        Variablen sind im Hauptprogramm deklariert und definiert.


Es ist möglich für sehr schnelle Operation Register für Übergabe von Parametern benutzen.
Schlüsselwort Speicherklassenangabe /register/

Übergabe eines Arrays:
#+begin_src c  
int func3(unsigned char * DataArray, unsigned short *DataLength){
    int iRet = 0;
    if (DataArray[0] == 0x55 && DataArray[1] == 0x33){
        /* Do someting*/
    }
    else {
        iRet = -1; /* Wrong Header in array */
    }
    return(iRet);
}
#+end_src


Aufruf im Hauptprogramm (bitte die Dereferenzierung beachten)
#+begin_src c
//…
iRet = func3(&vbyData[0], &usDataLength);
//…
#+end_src

*** Rückgabewerte
nur für Fehler
Erfolg ist 0
#+begin_src c
int iRet = 0;
iRet = func1(&DataArray[0], &DataLength);
if(iRet <0){
    /* … Fehlerbehandlung … */
}
#+end_src

*** Pointer
#+begin_src c
return_type function (xx_type const * pParam)
#+end_src
Beispiel:
(1) pParam ist ein
(2) Zeiger auf eine
(3) Konstante vom Typ
(4) xx_type

Zeiger auf Konstante: Eine Wertzuweisung ist nicht erlaubt/möglich (z.B.
~*pParam = 1.1f;~).

#+begin_src c
t_BYTE Data[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 };
t_BYTE *pData = &Data[0]; /* Data anstelle von &Data[0] moeglich */
/* pData = (t_BYTE *) &Data[0]; */
/* Adresszuweisung, der cast ist nicht zwingend notwendig */
printf("Der Pointer zeigt auf 0x%X mit dem Inhalt %d\n", pData, *pData);
pData++;
printf("Der Pointer zeigt auf 0x%X mit dem Inhalt %d\n", pData, *pData);
printf("Array Adresse: 0x%X 0x%x\n", &pData[0], &pData[1]);
#+end_src

*** Pointer Arithmetic
#+begin_src c
t_BYTE vbyData[20] = { 0x00 };
t_BYTE *pvbyData=NULL;
for (int i = 0; i < sizeof(vbyData); i++){ /* Init Array */
    vbyData[i] = i+1;
}
pvbyData = (t_BYTE *) &vbyData[0];
/* Adresszuweisung, der cast ist nicht notwendig */

for (int i = 0; i < 10; i++){
    printf("0x%X ", *pvbyData);
    pvbyData++;
}
printf("\n");
for (int i = 10; i > 5; i--){
    printf("0x%X ", *pvbyData);
    pvbyData--;
}
printf("\n");
#+end_src

*** Pointer to struct
#+begin_src c
typedef struct strData{
    t_WORD w1;
    t_WORD w2;
};

strData strDataValue;
strData * pstrDataValue = NULL;

pstrDataValue = &strDataValue;

strDataValue.w1 = 0xAAEE;
strDataValue.w2 = 0xDDFF;

printf("Struct Adresse: 0x%X\n", &strDataValue);
printf("Struct Adresse Pointer 0x%X\n", pstrDataValue);
printf("Struct Inhalt, via Pointer 0x%X 0x%X\n", pstrDataValue->w1,pstrDataValue->w2);
printf("Wertausgabe ueber Pointer 0x%X\n", (*pstrDataValue).w1);
#+end_src

für verschachtelten struct siehe 06 P9

Zeiger auf Array, Interpretation als struct siehe 06 P10

*** function pointer

brauche ich für callback funktion

#+begin_src c
typedef void(*func)(int *par1, int *par2);

void func1(int *par1, int *par2){
    *par1 = 5;
    *par2 = 3;
}
void main(void){
    func pFunc = NULL;
    pFunc = func1;
    pFunc(&a, &b);
    printf("Werte %d, %d\n", a, b);
}
#+end_src

Funktionszeiger werden eingesetzt bei der Registrierung von Callback Funktion einer übergeordneten Softwareschicht als Meldemedium für die unterlagerte Softwareschicht.

Funktionszeiger werden eingesetzt bei der Realisierung generischer Interfaces.

#+begin_src c
typedef int (*operation_ptr)(int,int);
int add(int a, int b) {return a + b;}
int mult(int a, int b) {return a * b;}

// or
//int do_operation(operation_ptr op, int x, int x){
int do_operation(int (*op)(int,int),int x, int y){
    return op(x,y);
}

int main(int argc, char **argv){
    int results = do_operation(add, 5, 34);
    int results2 = do_operation(mult, 2,3);
} 
#+end_src

*** UML
Mindestens jede Komponente, die aus mehreren Modulen bestehen kann, bekommt ein
Design, z.B. in UML (Enterprise Architekt).
*** Design for Testability:
Software soll bereits im Design die Testbarkeit berücksichtigen (STUB-
Barkeit, Datenaustausch per definiertem Interface, einfache Instrumentalisierung ohne die
Laufzeit zu beeinflussen, …)
*** Strukturierung eines Moduls
**** Sourcefile
- Headerkommentar:
  Was für ein Modul, Copyright Notice, Versionierung des Moduls, …)
- Includes:
  Es werden nur unterlagerte includes eingebunden mit Ausnahme weniger globaler Header
- Macros:
  modullokal
- Typedefs:
  modullokal
- Constants:
  modullokal
- Global variables:
  modullokal
- Prototypes:
  modullokal
- Functions:
  Der Kommentarblock enthält:
  - Funktionsname,
  - Beschreibung inkl. Bereichsgrenzen der Eingangs und Ausgangsvariablen,
  - Beschreibung des Rückgabewertes,
  - Beschreibung ggf. globaler verwendeter Variablen,
  - Angabe des Erstellers, der Funktion und Datum der Erstellung.
  - Jede Änderung der Funktion wird mit Datum und Name kommentiert
- EOF:
  Modulfooter
- Jede Sektion wird durch einen Kommentar eingeleitet
- Eine Funktion GetVersion() zur Rückgabe der Modulversion ist verpflichtend.
  ~#define MAJOR_VERSION 01~
  ~#define MINOR_VERSION 00~
  ~#define PATCH_VERSION 00~
- Es ist wichtig ein eindeutiges Versionierungsschema möglichst früh zu vereinbaren.
- Die Anzahl der Codezeilen pro C-Datei soll 1000 NCLOC (Non Comment Lines Of Code) nicht überschreiten.
- Die Anzahl der NCLOC (Non Comment Lines Of Code) pro Funktion soll 100 nicht überschreiten.
- Globale Variablen sind grundsätzlich zu vermeiden, so wenig wie möglich.
- Globale oder modulglobale Variablen und Strukturen sollten grundsätzlich in einer MSN_Init() Funktion zur Laufzeit initialisiert werden. Dies ermöglicht es die Komponente mehrmals im Programmablauf wieder auf den Ausgangszustand zurückzusetzen, was v.a. bei Modultests sehr hilfreich ist.
**** Gruppierung modulglobaler Statusvariablen mittels struct
Guter Designstil ist es den internen Status eines Moduls in einer Struktur zu kapseln.
#+begin_src c
unsigned char ucStateCounter1;
unsigned long ulWorkingClock;
unsigned long ulMsSinceStartup;
unsigned char ucStage;
int iGlobalError;
unsigned char ucStatus;
#+end_src
->
#+begin_src c
typedef struct {
    unsigned char ucStateCounter1;
    unsigned long ulWorkingClock;
    unsigned long ulMsSinceStartup;
    unsigned char ucStage;
    int iGlobalError;
    unsigned char ucStatus;
}t_GlobalState;

t_GlobalState GlbState;
#+end_src

- Lesbarkeit:
  Ein gut dokumentierter struct erleichtert an zentraler Stelle den Einstieg in die internen Abläufe des Moduls
  Die relevanten modulglobalen Variablen sind an zentraler Stelle zusammengefasst.
- Debugability:
  Das Debuggen wird erleichtert, es muss nur eine Struktur in das WatchWindow gezogen werden.
- Testability:
  Der interne Status des Moduls lässt sich einfach serialisieren und über eine Kommunikationsschnittstelle übertragen für zum Beispiel eine messtechnische Verwendung.
**** Headerfiles
- Das zu einem Modul gehörende Headerfile bildet das Interface nach außen. Hier wird nur das abgebildet, was zur Verwendung des Moduls von außen benötigt wird.
- Das Headerfile beginnt mit einem Include Guard, um Mehrfachinkludierung zu vermeiden.
  ~#ifndef GRANDFATHER_H~
  ~#define GRANDFATHER_H~
- Globale Variablen dürfen im Headerfile nur deklariert werden aber nicht definiert werden.
  Beispiel: ~extern int iTest;~
- Externe Funktionen dürfen in einem Headerfile nur deklariert aber nicht definiert werden
  Beispiel: ~extern void func (void);~
  Anmerkung:
  Eine Ausnahme gilt für inline Funktionen.
- Ein Modul bekommt zwei Headerfiles:
➔ Modulename.h
Interface (Deklaration) für das überlagerte Modul
➔ Modulename_cfg.h
Konfigurationsinterface für das Modul (#define)
- Ein Modul wird so aufgebaut, dass ein Dritter der das Modul verwendet, nur die Headerfiles als Interface benötigt, um mit dem Modul arbeiten zu können.
- Im Headerfile deklarierte globale Variablen und Funktionen wird eine Modulkurzkennung bestehend aus drei Buchstaben gefolgt von „_“ vorangestellt.
  Beispiel:
  ~MSN_TestFunc(…)~
  MSN steht für „Module Short Name“
− Tailoring:
  Im Konfigurationsheader ist ein Tailoring (Anpassung) einer SW-Komponenten auf den aktuellen Verwendungskontext vorzunehmen.
− Tailoring optional:
  Ausprogrammierung plattform-spezifischer Funktionsinhalte mittels gesondertem _cfg.c file, das über den cfg.h eingebunden wird.

*** Kommentare
- Es gibt zwei Arten um einen Kommentar zu markieren:
  den mit ~// eingeleiteten C++ Kommentar~
  den in ~/* geklammerten C Kommentar */~
- Für automotive embedded Templates wird nur der C Kommentarstil vereinbart.
  
** Rectangle Method
$$I_2=\int_b^af(x)fx$$

$$\approx h \sum_{i=1}^n f({x_{i-1}+x_i}/2)$$

** Trapezoid Method
$$I_T = \int_a^b f(x)dx$$

$$    \approx h/2 \sum_{i=1}^{n}f(x_{i-1})+f(x_i))$$

$$    = h/2 (f(x_0)+2f(x_1)+\dots+2f(x_{n-1})+f(x_n))$$

** Simpson Method

$$I = \int_a^bf(x)dx$$
$$ \approx (h/3) [f(x_0)+4f(x_1)+2f(x_2)+ \dots 2f(x_{n-2})+4f(x_{n-1})+f(x_n)]$$

