//version vom 25.11.22
//bibliotheken einbinden :
#include <avr/interrupt.h>
#include <avr/io.h>

//Klassen einbinden :
#include "io_settings.h"
#include "scheduler.h"
#include "signalgast.h"
#include "funker.h"
#include "maschine.h"
#include "deckshand.h"
#include "navigator.h"
#include "segel.h"

//instanzen erstellen (klasse -> objekt),konstruktoren initialisieren :
//zuerst prozessorzeug, nur der Konstruktorlauf wird ausgeführt (=Initialisierung):
io_settings Io;
scheduler Nprm;

//jetzt die mannschaft (objekte heißen wie klassen, aber großer Anfangsbuchstabe) :
signalgast Signalgast;
funker Funker;
maschine Maschine;
deckshand Matrose;      //aus Kostengründen muß einer Alles machen
navigator Navigator;

//variablen volatile für die Interruptwerte:
volatile unsigned char trigger_10hz, trigger_1hz;             //Variablen für scheduler-ISR
volatile unsigned char kanalcounter, valid;                   //Fernsteuerkanäle
volatile unsigned char servo = 1;                             //Servos, Winden usw.
volatile int wert;
volatile int pulsdauer = 0;
volatile int synccounter = 0;
volatile unsigned char mystring;
volatile unsigned char start = 1;
volatile unsigned char riggKlar = 0;
volatile unsigned char int_done0 = 0;
volatile unsigned char int_done1 = 0;
volatile unsigned char int_done2 = 0;
unsigned char testbyte;

//in der main-routine die deckskommunikation zwischen den mannschaftsmitgliedern :
int main(void)
{
    sei();                          //interrupts ein
    //


    while(1)                        //endlosschleife (wie bei ner sps)
    {
        if (trigger_10hz == 1)              //timergesteuerte ausführung alle 100ms  -> NPRM-Echtzeit-Betriebssystem
        {
            trigger_10hz = 0;               //..und hier gleich wieder null, um auf die nächste 1 zu warten
            Signalgast.heartbeat();         //wenn nicht blinkt ist tot
            Funker.decodieren();            //Befehle vom Sender holen

            //Fock.fall_runter(3,100,10);
            //Gross.fall_runter(1,100,10);
            //Gross.fall_runter(2,100,10);
            //Signalgast.sendInt(Fock.FallInkrement);
            //testbyte = PINH & (1<<PINH6);
            //Signalgast.sendbyte(testbyte);


            if (start == 1)                 //Nur beim Start (=1) warten auf GPS
            {
                if (Navigator.gps_valid == 0)
                {
                    Signalgast.beep();
                }
                else
                {
                    start = 0;
                    Navigator.request_position();                           //Position holen
                    Navigator.LatBhome_meters = Navigator.LatB_meters;      //als "home" speichern
                    Navigator.LonLhome_meters = Navigator.LonL_meters;
                }
            }

            if (Funker.sender == 1)
            {
                //wenn der Sender erkannt wird, Funkerinfo an Mannschaft :
                Maschine.motorlauf = Funker.motorlauf;              //Funkerinfo an Maschine
                Maschine.motorrichtung = Funker.motorrichtung;      //voraus/zurück
                Maschine.drehzahl = Funker.motordrehzahl;           //0...100
                Matrose.ruderwinkel = Funker.ruderwinkel;         //RC-Befehle an Decksmannschaft
                Matrose.fockwinkel = Funker.fockwinkel;           //Fockschot
                Matrose.gaffelwinkel = Funker.gaffelwinkel;       //Groß und Besan
                Matrose.segel = Funker.segel;         //Übergabe des RC-Wertes

            }
            else
            {
                //autopilot steuert : motorlauf, segel runter,  gps-navi nach home
                Maschine.motorlauf = 1;                 //Maschinentelegraf
                Maschine.motorrichtung = 1;
                Maschine.drehzahl = 100;                //volle Kraft voraus (0...100)
                //Navigator.zielkurs();                 //am Kartentisch : Zielkurs nach Hause rechnen
                //geht nicht ohne hw :**************
                //Navigator.regeln();                   //Zielkurs mit PID-Regler laufend korrigieren
                //*************************************
                //Deckshand.ruderwinkel = Navigator.ruderwinkel;         //Navigator an Decksmannschaft
                Matrose.segel = 0;                    //Unter Autopilot : alle Segel bergen
            }

            Maschine.fahren();                      //Befehl an Maschinenraum
            Matrose.trimm_ausfuehren();           //Befehle an Decksmannschaft
            Matrose.segelbedienen();

        }//ende der 10hz-task in der "while 1" -main-schleife
    };//ende "while 1" - schleife innerhalb main
    return 0;

}//ende von main ******************************************************************************************************************************

//Hardwarebelegung :
//Timer 0 : PPM-Generator (für Servo und RC-Winden)
//Timer 1 : 10HZ-Generator für NPRM-Scheduler
//Timer 2 : PWM-Generator (A für Maschine )
//Timer 3 : Zähler für PPM-Decoder (vom RC-Sender)
//Timer 4 : PPM für Winden in Gruppen
//Timer 5 : Entprellung der Inkrementgeber
//UART0 : USB zum PC
//UART1 : GPS

ISR (PCINT0_vect)       //Inkremente aus Gruppe 0
{
    if (int_done0 == 0)  //falls das ein neuer interrupt ist
    {
        TCNT5 = 0;      //Timer 5 auf Null
        int_done0 = 1;   //merk dir, jetzt war ein signal
    }
    else
    {
        if (TCNT5 > 500)            // letztes Signal länger als 32ms her ?
        {
            Matrose.Gross.KlauInkrement ++ ;
            Matrose.Besan.KlauInkrement ++ ;
            int_done0 = 0;
        }
    }

}

ISR (PCINT1_vect)       //Inkremente aus Gruppe 1
{
    if (int_done1 == 0)  //falls das ein neuer interrupt ist
    {
        TCNT5 = 0;      //Timer 5 auf Null
        int_done1 = 1;   //merk dir, jetzt war ein signal
    }
    else
    {
        if (TCNT5 > 500)            // letztes Signal länger als 32ms her ?
        {
            Matrose.Fock.FallInkrement ++ ;
            int_done1 = 0;
        }
    }
}

ISR (PCINT2_vect)       //Inkremente aus Gruppe 2
{
    if (int_done2 == 0)  //falls das ein neuer interrupt ist
    {
        TCNT5 = 0;      //Timer 5 auf Null
        int_done2 = 1;   //merk dir, jetzt war ein signal
    }
    else
    {
        if (TCNT5 > 500)            // letztes Signal länger als 32ms her ?
        {
            Matrose.Gross.PiekInkrement ++ ;
            Matrose.Besan.PiekInkrement ++ ;
            int_done2 = 0;
        }
    }


}

ISR(TIMER1_COMPA_vect) //scheduler
 {
        trigger_10hz = 1;
 }

ISR(INT4_vect)          //PPM-Decoder : an INT4 (Pin 2) hängt das Summensignal
                        //kanal0 : Knüppellinks_up_down : Motor (oben vor, unten zurück)
                        //kanal1 : Schalter links : oben Maschine an, unten Maschine aus
                        //kanal2 : Schalter rechts : oben ist Segel hoch, unten Segel runter
                        //kanal3 : Knüppelrechts_rechts_links : Ruder
                        //kanal4 : linker Drehknopf CRTL7 : Fockschot
                        //kanal5 : rechter Drehknopf CRTL8 : Groß/Besan-Schot
 {
    wert = TCNT3;       //wenn ein Interrupt erkannt, wird der Zählerwert übernommen
    TCNT3=0;            //und der Zähler wieder Null gesetzt
    if (wert < 100)     //störung bei Zählwert zu klein
    {
        kanalcounter = 0;   //dann alles von vorne !
        valid = 0;
    }
    else
    {
        if (wert > 2000)            //sync-impuls falls wert groß genug
        {
            synccounter = synccounter + 1;
            if (synccounter > 5)    //wenn mehrere lange Pulse hintereinander, ist Sender aus !
            {
                Funker.sender = 0;
            }
            kanalcounter = 0;       //kanal rücksetzen
            valid = 1;              //ab jetzt gilt's, die kanäle werden nacheinander gemessen
        }
        else                        //gültiger impuls (kein snyc, keine störung)
        {
            if (valid==1)
            {
                synccounter = 0;     //steuerimpuls erkannt, Sender läuft
                Funker.sender = 1;
                if (wert < 270)
                {
                    wert = 270;
                }
                Funker.kanal[kanalcounter] = wert - 270  ; //kanal(x) wird mit zählwert gesetzt, werte : 280 .... 470
                kanalcounter ++;
                if (kanalcounter > 5) //bis zum 0...5 Kanal, dann wieder von vorne
                {
                    kanalcounter =0;
                    valid=0;
                }
            }
        }
    }
 }

//ISR für die Servosteuerung, macht PPM aus den servowerten----------------------------------------------------------
ISR(TIMER0_COMPA_vect)
{
    TCCR0B = 0;                     //zähler aus
    PORTA = PORTA & 248;            //alle Pins auf 0
    servo = servo + 1;
    if (servo > 4)
    {
        servo = 1;
    }
    //--------------------------------------
    if (servo == 1)
    {
        PORTA |= 1;                 //Pin 22, Port A0 -> Ruder ist servo 1
        OCR0A = Matrose.ruderwinkelsollwert;
        TCCR0B = 4;                 //timer zählt los mit prescaler 256 (16us)
    }
    if (servo == 2)
    {
        PORTA |= 2;                 //Pin 23, Port A1 -> Gaffel ist servo 2
        OCR0A = Matrose.gaffelschotsollwert;
        TCCR0B = 4;                 //timer zählt los mit prescaler 256 (16 us)
    }
    if (servo == 3)
    {
        PORTA |= 4;                 //Pin 24, Port A2 -> Fock ist servo 3
        OCR0A = Matrose.fockschotsollwert;
        TCCR0B = 4;                 //timer zählt los mit prescaler 256 (16 us)
    }
    //-----------------------------------------
    if (servo == 4)
    {
        OCR0A = 255;                //Maximalwert, rund 13 ms Pause
        TCCR0B = 5;                 //timer zählt los mit prescaler 1024 (80us)
    }
}

ISR(USART1_RX_vect)
{
    Navigator.receivedata = UDR1;
    //Signalgast.sendbyte(Navigator.receivedata);   //Test : alles zum PC
    if (Navigator.receivedata == 36)                // $ erkannt ?
    {
        Navigator.gpspos=0;                         //  ja : string neu beginnen
        Navigator.validate_request=1;               //  testen, ob NMEA-String valid
    }

    Navigator.gpspos = Navigator.gpspos + 1;

    if (Navigator.gpspos == 6)              //  6. Zeichen prüfen
    {
        mystring = 0;
        if (Navigator.receivedata == 67)    //  falls C aus $GPRMC erkannt :
        {
            mystring=1;                     //  das ist mein Format !
        }
    }

    if (Navigator.gpspos == 18)             //  18. Zeichen prüfen
    {
        if (Navigator.receivedata == 65)    //  A : gps-signal ist valid
        {
            Navigator.gps_valid = 1;
        }
        else
        {
            Navigator.gps_valid = 0;
        }
    }

    if (mystring == 1 and Navigator.gps_valid == 1)           //wenn es also ein GPRMC-String ist :
    {
        if (Navigator.gpspos > 21 and Navigator.gpspos < 29)  //beim 22. Zeichen beginnt Minuten-Info der Breite
        {
            Navigator.LatBstring[Navigator.gpspos - 21] = Navigator.receivedata;
        }
        if (Navigator.gpspos > 35 and Navigator.gpspos < 43 )
        {
            Navigator.LonLstring[Navigator.gpspos - 35] = Navigator.receivedata;
        }
    }
}



