Subversion Repositories NaviCtrl

Rev

Rev 545 | Blame | Compare with Previous | Last modification | View Log | RSS feed

/*#######################################################################################*/
/* !!! THIS IS NOT FREE SOFTWARE !!!                                                     */
/*#######################################################################################*/
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// + www.MikroKopter.com
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// + Software Nutzungsbedingungen (english version: see below)
// + der Fa. HiSystems GmbH, Flachsmeerstrasse 2, 26802 Moormerland - nachfolgend Lizenzgeber genannt -
// + Der Lizenzgeber räumt dem Kunden ein nicht-ausschließliches, zeitlich und räumlich* unbeschränktes Recht ein, die im den
// + Mikrocontroller verwendete Firmware für die Hardware Flight-Ctrl, Navi-Ctrl, BL-Ctrl, MK3Mag & PC-Programm MikroKopter-Tool
// + - nachfolgend Software genannt - nur für private Zwecke zu nutzen.
// + Der Einsatz dieser Software ist nur auf oder mit Produkten des Lizenzgebers zulässig.
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// + Die vom Lizenzgeber gelieferte Software ist urheberrechtlich geschützt. Alle Rechte an der Software sowie an sonstigen im
// + Rahmen der Vertragsanbahnung und Vertragsdurchführung überlassenen Unterlagen stehen im Verhältnis der Vertragspartner ausschließlich dem Lizenzgeber zu.
// + Die in der Software enthaltenen Copyright-Vermerke, Markenzeichen, andere Rechtsvorbehalte, Seriennummern sowie
// + sonstige der Programmidentifikation dienenden Merkmale dürfen vom Kunden nicht verändert oder unkenntlich gemacht werden.
// + Der Kunde trifft angemessene Vorkehrungen für den sicheren Einsatz der Software. Er wird die Software gründlich auf deren
// + Verwendbarkeit zu dem von ihm beabsichtigten Zweck testen, bevor er diese operativ einsetzt.
// + Die Haftung des Lizenzgebers wird - soweit gesetzlich zulässig - begrenzt in Höhe des typischen und vorhersehbaren
// + Schadens. Die gesetzliche Haftung bei Personenschäden und nach dem Produkthaftungsgesetz bleibt unberührt. Dem Lizenzgeber steht jedoch der Einwand
// + des Mitverschuldens offen.
// + Der Kunde trifft angemessene Vorkehrungen für den Fall, dass die Software ganz oder teilweise nicht ordnungsgemäß arbeitet.
// + Er wird die Software gründlich auf deren Verwendbarkeit zu dem von ihm beabsichtigten Zweck testen, bevor er diese operativ einsetzt.
// + Der Kunde wird er seine Daten vor Einsatz der Software nach dem Stand der Technik sichern.
// + Der Kunde ist darüber unterrichtet, dass der Lizenzgeber seine Daten im zur Vertragsdurchführung erforderlichen Umfang
// + und auf Grundlage der Datenschutzvorschriften erhebt, speichert, verarbeitet und, sofern notwendig, an Dritte übermittelt.
// + *) Die räumliche Nutzung bezieht sich nur auf den Einsatzort, nicht auf die Reichweite der programmierten Software.
// + #### ENDE DER NUTZUNGSBEDINGUNGEN ####'
// +  Hinweis: Informationen über erweiterte Nutzungsrechte (wie z.B. Nutzung für nicht-private Zwecke) sind auf Anfrage per Email an info(@)hisystems.de verfügbar.
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// + Software LICENSING TERMS
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// + of HiSystems GmbH, Flachsmeerstrasse 2, 26802 Moormerland, Germany - the Licensor -
// + The Licensor grants the customer a non-exclusive license to use the microcontroller firmware of the Flight-Ctrl, Navi-Ctrl, BL-Ctrl, and MK3Mag hardware
// + (the Software) exclusively for private purposes. The License is unrestricted with respect to time and territory*.
// + The Software may only be used with the Licensor's products.
// + The Software provided by the Licensor is protected by copyright. With respect to the relationship between the parties to this
// + agreement, all rights pertaining to the Software and other documents provided during the preparation and execution of this
// + agreement shall be the property of the Licensor.
// + The information contained in the Software copyright notices, trademarks, other legal reservations, serial numbers and other
// + features that can be used to identify the program may not be altered or defaced by the customer.
// + The customer shall be responsible for taking reasonable precautions
// + for the safe use of the Software. The customer shall test the Software thoroughly regarding its suitability for the
// + intended purpose before implementing it for actual operation. The Licensor's liability shall be limited to the extent of typical and
// + foreseeable damage to the extent permitted by law, notwithstanding statutory liability for bodily injury and product
// + liability. However, the Licensor shall be entitled to the defense of contributory negligence.
// + The customer will take adequate precautions in the case, that the software is not working properly. The customer will test
// + the software for his purpose before any operational usage. The customer will backup his data before using the software.
// + The customer understands that the Licensor collects, stores and processes, and, where required, forwards, customer data
// + to third parties to the extent necessary for executing the agreement, subject to applicable data protection and privacy regulations.
// + *) The territory aspect only refers to the place where the Software is used, not its programmed range.
// + #### END OF LICENSING TERMS ####
// + Note: For information on license extensions (e.g. commercial use), please contact us at info(@)hisystems.de.
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>      
#include <string.h>
#include "91x_lib.h"
#include "waypoints.h"
#include "uart1.h"
#include "fat16.h"
#include "main.h"
#include "spi_slave.h"


WPL_Store_t WPL_Store;

// the waypoints list
#define MAX_LIST_LEN 101

Point_t PointList[MAX_LIST_LEN];
u8 WPIndex = 0;         // list index of GPS point representig the current WP, can be maximal WPCount
u8 POIIndex = 0;        // list index of GPS Point representing the current POI, can be maximal WPCount
u8 WPCount = 0;         // number of waypoints
u8 PointCount = 0;      // number of points in the list can be maximal equal to MAX_LIST_LEN
u8 POICount = 0;        // number of point of interest in the list
s16 HeadingOld = -1;

u8 WPActive = FALSE;

u8 PointList_Init(void)
{
        return PointList_Clear();
}

u8 PointList_Clear(void)
{
        u8 i;
        WPIndex = 0;    // real list position are 1 ,2, 3 ...
        POIIndex = 0;   // real list position are 1 ,2, 3 ...
        WPCount = 0;    // no waypoints
    POICount = 0;
        PointCount = 0; // no contents
        WPActive = FALSE;
        NaviData.WaypointNumber = WPCount;
        NaviData.WaypointIndex = 0;
        for(i = 0; i < MAX_LIST_LEN; i++)
        {
                PointList[i].Position.Status = INVALID;
                PointList[i].Position.Latitude = 0;
                PointList[i].Position.Longitude = 0;
                PointList[i].Position.Altitude = 0;
                PointList[i].Heading = 361;             // invalid value
                PointList[i].ToleranceRadius = 0;       // in meters, if the MK is within that range around the target, then the next target is triggered
                PointList[i].HoldTime = 0;                      // in seconds, if the was once in the tolerance area around a WP, this time defines the delay before the next WP is triggered
                PointList[i].Event_Flag = 0;            // future implementation
                PointList[i].Index = 0;
                PointList[i].Type = POINT_TYPE_INVALID;
                PointList[i].WP_EventChannelValue = 0;
                PointList[i].AltitudeRate = 0;          // no change of setpoint
                PointList[i].Speed = 0;
                PointList[i].CamAngle = 0;
                PointList[i].Name[0] = 0;
        }
        ClearWLP_Name();
        return TRUE;           
}

u8 PointList_GetCount(void)
{
        return PointCount; // number of points in the list
}

Point_t* PointList_GetAt(u8 index)
{
        if((index > 0) && (index <= PointCount)) return(&(PointList[index-1])); // return pointer to this waypoint
        else return(NULL);
}

u8 PointList_SetAt(Point_t* pPoint)
{
        // if index is in range
        if((pPoint->Index > 0) && (pPoint->Index <= MAX_LIST_LEN))
        {
                // check list entry before update
                switch(PointList[pPoint->Index-1].Type)
                {
                        case POINT_TYPE_INVALID: // was invalid
                                switch(pPoint->Type)
                                {
                                        default:
                                        case POINT_TYPE_INVALID:
                                                // nothing to do
                                                break;

                                        case POINT_TYPE_WP:
                                                WPCount++;
                                                PointCount++;
                                                break;
                                       
                                        case POINT_TYPE_POI:
                                                POICount++;
                                                PointCount++;
                                                break;
                                }
                                break;
                               
                        case POINT_TYPE_WP: // was a waypoint
                                switch(pPoint->Type)
                                {
                                        case POINT_TYPE_INVALID:
                                                WPCount--;
                                                PointCount--;
                                                break;

                                        default:
                                        case POINT_TYPE_WP:
                                                //nothing to do
                                                break;
                                       
                                        case POINT_TYPE_POI:
                                                POICount++;
                                                WPCount--;
                                                break;
                                }
                                break;
                               
                        case POINT_TYPE_POI: // was a poi
                                switch(pPoint->Type)
                                {
                                        case POINT_TYPE_INVALID:
                                                POICount--;
                                                PointCount--;
                                                break;

                                        case POINT_TYPE_WP:
                                                WPCount++;
                                                POICount--;
                                                break;
                                       
                                        case POINT_TYPE_POI:
                                        default:
                                                // nothing to do
                                                break;
                                }
                                break;         
                }
                memcpy(&PointList[pPoint->Index-1], pPoint, sizeof(Point_t)); // copy data to list entry                                                                               
                NaviData.WaypointNumber = WPCount;
                return pPoint->Index;
        }
        else return(0);
}

// returns the pointer to the first waypoint within the list
Point_t* PointList_WPBegin(void)
{
        u8 i;
        WPIndex = 0; // set list position invalid

        if(WPActive == FALSE) return(NULL);

        POIIndex = 0; // set invalid POI
        if(PointCount > 0)
        {
                // search for first wp in list
                for(i = 0; i <MAX_LIST_LEN; i++)
                {
                        if((PointList[i].Type == POINT_TYPE_WP) && (PointList[i].Position.Status != INVALID))
                        {
                                WPIndex = i + 1;
                                break;
                        }
                }
                if(WPIndex) // found a WP in the list
                {
                        NaviData.WaypointIndex = 1;
                        // update index to POI
                        if(PointList[WPIndex-1].Heading < 0) POIIndex = (u8)(-PointList[WPIndex-1].Heading);
                        else POIIndex = 0;                     
                }
                else // some points in the list but no WP found
                {
                        NaviData.WaypointIndex = 0;
                        //Check for an existing POI
                        for(i = 0; i < MAX_LIST_LEN; i++)
                        {
                                if((PointList[i].Type == POINT_TYPE_POI) && (PointList[i].Position.Status != INVALID))
                                {
                                        POIIndex = i + 1;
                                        break;
                                }
                        }
                }
        }
        else // no point in the list
        {
                POIIndex = 0;
                NaviData.WaypointIndex = 0;    
        }

        if(WPIndex) return(&(PointList[WPIndex-1]));
        else return(NULL);
}

// returns the last waypoint
Point_t* PointList_WPEnd(void)
{
       
        u8 i;
        WPIndex = 0; // set list position invalid
        POIIndex = 0; // set invalid

        if(WPActive == FALSE) return(NULL);

        if(PointCount > 0)
        {
                // search backward!
                for(i = 1; i <= MAX_LIST_LEN; i++)
                {
                        if((PointList[MAX_LIST_LEN - i].Type == POINT_TYPE_WP) && (PointList[MAX_LIST_LEN - i].Position.Status != INVALID))
                        {      
                                WPIndex = MAX_LIST_LEN - i + 1;
                                break;
                        }
                }
                if(WPIndex) // found a WP within the list
                {
                        NaviData.WaypointIndex = WPCount;
                        if(PointList[WPIndex-1].Heading < 0) POIIndex = (u8)(-PointList[WPIndex-1].Heading);
                        else POIIndex = 0;     
                }
                else // list contains some points but no WP in the list
                {
                        // search backward for a POI!
                        for(i = 1; i <= MAX_LIST_LEN; i++)
                        {
                                if((PointList[MAX_LIST_LEN - i].Type == POINT_TYPE_POI) && (PointList[MAX_LIST_LEN - i].Position.Status != INVALID))
                                {      
                                        POIIndex = MAX_LIST_LEN - i + 1;
                                        break;
                                }
                        }
                        NaviData.WaypointIndex = 0;    
                }
        }
        else // no point in the list
        {
                POIIndex = 0;
                NaviData.WaypointIndex = 0;
        }
        if(WPIndex) return(&(PointList[WPIndex-1]));
        else return(NULL);
}

// returns a pointer to the next waypoint or NULL if the end of the list has been reached
Point_t* PointList_WPNext(void)
{
        u8 wp_found = 0;
        if(WPActive == FALSE) return(NULL);
               
        if(WPIndex < MAX_LIST_LEN) // if there is a next entry in the list
        {
                u8 i;
                for(i = WPIndex; i < MAX_LIST_LEN; i++) // start search for next at next list entry
                {
                        if((PointList[i].Type == POINT_TYPE_WP) && (PointList[i].Position.Status != INVALID)) // jump over POIs
                        {
                                wp_found = i+1;
                                break;
                        }
                }
        }
        if(wp_found)
        {
                WPIndex = wp_found; // update list position
                NaviData.WaypointIndex++;
                if(PointList[WPIndex-1].Heading < 0) POIIndex = (u8)(-PointList[WPIndex-1].Heading);
                else POIIndex = 0;
                return(&(PointList[WPIndex-1]));        // return pointer to this waypoint
        }
        else
        {  // no next wp found
                NaviData.WaypointIndex = 0;    
                POIIndex = 0;
                return(NULL);
        }
}      

void PointList_WPActive(u8 set)
{
        if(set)
        {      
                WPActive = TRUE;
                PointList_WPBegin(); // updates POI index
        }
        else
        {
                WPActive = FALSE;
                POIIndex = 0;  // disable POI also
        }
}
 
Point_t* PointList_GetPOI(void)
{
        return PointList_GetAt(POIIndex);      
}


#define LINE_MAX 70
#define WP_FILE_VERSION_COMPATIBLE 3

u8 PointList_Save(u8 * filename, u8* listname, u8 overwride)
{
        File_t *fp;
        s8 wpline[LINE_MAX];
        u8 retval = WPL_ERROR;

        UART1_PutString("\n\r Save WPL...");

        if(Fat16_IsValid())
        {       // check if wpl file is existing
                if(fexist_(filename))
                {       //file is existent
                        if(!(overwride))
                        {
                                UART1_PutString("Error: file exist!\r\n");
                                return(WPL_FILEEXIST);
                        }      
                }
                fp = fopen_(filename, 'w');             // try to open the file
                if(fp == NULL)
                {
                        UART1_PutString("ERROR: Creating waypoint file!\r\n");
                        return(retval);
                }
                // Create general section and key entries
                fputs_("[General]\r\n", fp);
                sprintf(wpline, "Name=%s\r\n",  listname);
                fputs_(wpline, fp);
                sprintf(wpline, "FileVersion=%d\r\n", WP_FILE_VERSION_COMPATIBLE);
                fputs_(wpline, fp);
                sprintf(wpline, "NumberOfWaypoints=%d\r\n", PointCount);
                fputs_(wpline, fp);
                // dump all points if existent
                if(PointCount)
                {
                        u8 i, u8_1;
                        s32 i32_1, i32_2;
                        NewWPL_Name = 1;
                        for (i = 0; i < PointCount; i++)
                        {
                                sprintf(wpline, "[Point%d]\r\n",PointList[i].Index);
                                fputs_(wpline, fp);
                                // write latitude in deg
                                if(PointList[i].Position.Latitude < 0) u8_1 = '-';
                                else u8_1 = '+';
                                i32_1 = abs(PointList[i].Position.Latitude)/10000000L;
                                i32_2 = abs(PointList[i].Position.Latitude)%10000000L;
                                sprintf(wpline, "Latitude=%c%ld.%07ld\r\n", u8_1, i32_1, i32_2);
                                fputs_(wpline, fp);
                                // write longitude in deg
                                if(PointList[i].Position.Longitude < 0) u8_1 = '-';
                                else u8_1 = '+';
                                i32_1 = abs(PointList[i].Position.Longitude)/10000000L;
                                i32_2 = abs(PointList[i].Position.Longitude)%10000000L;
                                sprintf(wpline, "Longitude=%c%ld.%07ld\r\n", u8_1, i32_1, i32_2);
                                fputs_(wpline, fp);
                                // write tolerace radius in m
                                sprintf(wpline, "Radius=%d\r\n", PointList[i].ToleranceRadius);
                                fputs_(wpline, fp);
                                // write altitude in m
                                if(PointList[i].Position.Altitude < 0) u8_1 = '-';
                                else u8_1 = '+';
                                if(PointList[i].Type == POINT_TYPE_POI)
                                {
                                        i32_1 = abs(PointList[i].Position.Altitude)/100L;  // cm --> m
                                        i32_2 = abs(PointList[i].Position.Altitude)%100L;
                                }
                                else
                                {
                                        i32_1 = abs(PointList[i].Position.Altitude)/10L; // dm --> m
                                        i32_2 = abs(PointList[i].Position.Altitude)%10L;
                                }
                                sprintf(wpline, "Altitude=%c%ld.%01ld\r\n",  u8_1, i32_1, i32_2);
                                fputs_(wpline, fp);
                                // write climb rate in 0.1 m/s
                                sprintf(wpline, "ClimbRate=%d\r\n", PointList[i].AltitudeRate);
                                fputs_(wpline, fp);
                                // write hold time in s
                                sprintf(wpline, "DelayTime=%d\r\n", PointList[i].HoldTime);
                                fputs_(wpline, fp);
                                // write event channel value
                                sprintf(wpline, "WP_Event_Channel_Value=%d\r\n", PointList[i].WP_EventChannelValue);
                                fputs_(wpline, fp);
                                // write heading in deg (0= nothing, neg. values index to poi)
                                sprintf(wpline, "Heading=%d\r\n", PointList[i].Heading);
                                fputs_(wpline, fp);
                                // write speed in 0.1 m/s
                                sprintf(wpline, "Speed=%d\r\n", PointList[i].Speed);
                                fputs_(wpline, fp);
                                // write cam angle in degree (255 -> POI-Automatic)
                                sprintf(wpline, "CAM-Nick=%d\r\n", PointList[i].CamAngle);
                                fputs_(wpline, fp);
                                // write point type
                                sprintf(wpline, "Type=%d\r\n", PointList[i].Type + 1);
                                fputs_(wpline, fp);    
                                // write prefix
                                sprintf(wpline, "Prefix=%s\r\n", PointList[i].Name);
                                fputs_(wpline, fp);
                        } // EOF loop over all points
                } // EOF if(PointCount)
                if(EOF == fclose_(fp))
                {
                        UART1_PutString("failed!\r\n");
                }
                else
                {
                        UART1_PutString("ok\r\n");
                        retval = WPL_OK;
                }                                
        } // EOF if(Fat16_IsValid())
        else
        {
                UART1_PutString("no file system found!\r\n");
                retval = WPL_NO_SDCARD_FOUND;
        }
        return(retval);
}

u8 PointList_Load(u8 * filename, u8* listname, u8 listnamelen, u8 use_preset_speed)
{
        File_t *fp;
        s8 wpline[LINE_MAX];
        u8 retval = WPL_ERROR;
       
        s8 *name, *value;
        u8 i;

        u8 IsGeneralSection = 0;
        u8 IsPointSection  = 0;
        u8 WPNumber = 0;

        // clear point list first
        PointList_Clear();
        HeadingOld = -1; // updates the direction if the new direction is the same like last time
        UART1_PutString("\n\r Read ");
        UART1_PutString(filename);
        UART1_PutString("...");

        if(Fat16_IsValid())
        {       // check if wpl file is existing
                fp = fopen_(filename, 'r');             // try to open the file
                if(fp == NULL)
                {
                        UART1_PutString("ERROR: Reading waypoint file!\r\n");
                        return(retval);
                }
                // read all lines from file
                while(fgets_(wpline, LINE_MAX, fp) != 0)
                {
                        if ( // ignorelines starting with \r,\n,' ',';','#'
                                (wpline[0] != '\n') &&
                                (wpline[0] != '\r') &&
                                (wpline[0] != ' ' ) &&
                                (wpline[0] != ';' ) &&
                                (wpline[0] != '#' )
                                )
                        {
                                // check for section line found
                                if(wpline[0] == '[')
                                {
                                        // next section found
                                        IsGeneralSection = 0;
                                        IsPointSection = 0;

                                        name  = strtok(&wpline[1], "]");
                                        if(name != NULL)   // if section name
                                        {
                                                // check section type
                                                for(i=0; name[i]; i++) name[i] = toupper(name[i]); // convert to upper case

                                                if(strncmp(name, "POINT", 5) == 0)
                                                {
                                                        IsPointSection = (u8)atoi(&name[5]);
                                                        PointCount++;
                                                }
                                                else if(strcmp(name, "GENERAL") == 0)
                                                {
                                                        IsGeneralSection = 1;
                                                }
                                                else
                                                {
                                                        UART1_PutString("Unknown section: ");
                                                        UART1_PutString(name);
                                                        UART1_PutString("\r\n");
                                                }
                                        }
                                } // EOF section line
                                else
                                {       // look for key entrys of each sections
                                        name  = strtok(wpline, "="); // get name
                                        value = strtok(NULL, "="); // get value
                                        if ((name != NULL) && (value != NULL))
                                        {
                                                for(i=0; name[i]; i++) name[i] = toupper(name[i]); // convert to upper case
                                                if(IsPointSection &&  (IsPointSection <= WPNumber))
                                                {
                                                        if(strcmp(name, "LATITUDE") == 0)
                                                        {
                                                                PointList[IsPointSection-1].Position.Latitude = (s32)(atof(value) * 1E7);
                                                        }
                                                        else if(strcmp(name, "LONGITUDE") == 0)
                                                        {
                                                                PointList[IsPointSection-1].Position.Longitude = (s32)(atof(value) * 1E7);
                                                        }
                                                        else if(strcmp(name, "RADIUS") == 0)
                                                        {
                                                                PointList[IsPointSection-1].ToleranceRadius = (u8)atoi(value);
                                                        }
                                                        else if(strcmp(name, "ALTITUDE") == 0)
                                                        {
                                                                PointList[IsPointSection-1].Position.Altitude = (s32)(atof(value) * 100.0);      // in cm
                                                                PointList[IsPointSection-1].Position.Status = NEWDATA;
                                                        }
                                                        else if(strcmp(name, "CLIMBRATE") == 0)
                                                        {
                                                                PointList[IsPointSection-1].AltitudeRate = (u8)atoi(value);
                                                        }
                                                        else if(strcmp(name, "DELAYTIME") == 0)
                                                        {
                                                                PointList[IsPointSection-1].HoldTime = (u8)atoi(value);
                                                        }
                                                        else if(strcmp(name, "WP_EVENT_CHANNEL_VALUE") == 0)
                                                        {
                                                                PointList[IsPointSection-1].WP_EventChannelValue = (u8)atoi(value);
                                                        }
                                                        else if(strcmp(name, "HEADING") == 0)
                                                        {
                                                                PointList[IsPointSection-1].Heading = (s16)atoi(value);
                                                        }
                                                        else if(strcmp(name, "SPEED") == 0)
                                                        {
                                                                if(use_preset_speed) PointList[IsPointSection-1].Speed = use_preset_speed;
                                                                else PointList[IsPointSection-1].Speed = (u8)atoi(value);
                                                        }
                                                        else if(strcmp(name, "CAM-NICK") == 0)
                                                        {
                                                                PointList[IsPointSection-1].CamAngle = (u8)atoi(value);
                                                        }
                                                        else if(strcmp(name, "TYPE") == 0)
                                                        {
                                                                PointList[IsPointSection-1].Type = (u8)atoi(value);
                                                                if(PointList[IsPointSection-1].Type > 0) PointList[IsPointSection-1].Type--;  // index shift
                                                                else PointList[IsPointSection-1].Type = POINT_TYPE_INVALID;
                                                       
                                                                switch(PointList[IsPointSection-1].Type)
                                                                {
                                                                        case POINT_TYPE_WP:
                                                                                // this works only if altitude key is set before point type key in WPL file     !!
                                                                                PointList[IsPointSection-1].Position.Altitude /= 10; // dm only for WPs
                                                                                WPCount++;
                                                                                break;

                                                                        case POINT_TYPE_POI:
                                                                                POICount++;
                                                                                break;
                                                                }
                                                        }
                                                        else if(strcmp(name, "PREFIX") == 0)
                                                        {
                                                                strncpy(PointList[IsPointSection-1].Name, value, 4);
                                                                PointList[IsPointSection-1].Name[3] = 0; // Terminate string
                                                        }
                                                        else
                                                        {
                                                                UART1_PutString("Unknown key: ");
                                                                UART1_PutString(name);
                                                                UART1_PutString("\r\n");
                                                        }      
                                                } // EOF point section
                                                else if(IsGeneralSection)
                                                {
                                                        if(strcmp(name, "NUMBEROFWAYPOINTS") == 0)
                                                        {      
                                                                WPNumber = (u8)atoi(value);
                                                                if(!WPNumber) // no waypoints in file
                                                                {
                                                                        return(WPL_NO_WAYPOINTS); // we are done here  
                                                                }
                                                                else if(WPNumber > MAX_LIST_LEN) // number o points larger than ram list
                                                                {
                                                                        UART1_PutString("To many points!");
                                                                        return(WPL_ERROR);
                                                                }
                                                        }
                                                        else if (strcmp(name, "FILEVERSION") == 0)
                                                        {
                                                                if((u8)atoi(value) != WP_FILE_VERSION_COMPATIBLE)
                                                                {
                                                                        PointList_Clear();
                                                                        UART1_PutString("Bad file version!\r\n");
                                                                        return(WPL_ERROR);     
                                                                }
                                                        }
                                                        else if (strcmp(name, "NAME") == 0)
                                                        {
                                                                if(listname)
                                                                {
                                                                        u8 len = strlen(value);
                                                                        if(len)
                                                                        {
                                                                                if(value[len-1] == '\r')
                                                                                {
                                                                                        value[len-1] = 0;
//                                                                                      len--;
                                                                                }
                                                                        }
                                                                        if(len > listnamelen) len = listnamelen;
                                                                        if(len)
                                                                        {
                                                                                value[len-1] = 0; // terminate string
                                                                                if(listname) memcpy(listname, value, len);
                                                                        }
                                                                        NewWPL_Name = 1;
                                                                }
                                                        }
                                                        else
                                                        {
                                                                UART1_PutString("Unknown key: ");
                                                                UART1_PutString(name);
                                                                UART1_PutString("\r\n");
                                                        }
                                                } // EOF general section
                                        } // EOF valid key entry
                                } // EOF key entry line
                        } // valid line
                } // EOF loop over all lines
                fclose_(fp);
                NaviData.WaypointNumber = WPCount;
                retval = WPL_OK;        
                UART1_PutString("ok\r\n");                               
        } // EOF if(Fat16_IsValid())
        else
        {
                UART1_PutString("no file system found!\r\n");
                retval = WPL_NO_SDCARD_FOUND;
        }      
        return(retval);
}

// load actual point list from SD card
u8 PointList_ReadFromFile(WPL_Store_t * pWPL_Store)
{
        u8 filename[30];       

        pWPL_Store->Name[0] = 0; // clear current list name

        // user absolute path, i.e. leading /
        if(pWPL_Store->Index == 0) // index 0 looks for a default WPL file in the root
        {
                sprintf(filename, "/default.wpl");     
        }
        else
        {
                sprintf(filename, "/WPL/list_%03d.wpl", pWPL_Store->Index);
        }
        return PointList_Load(filename, pWPL_Store->Name, sizeof(pWPL_Store->Name),0);
}

// save actual point list to SD card
u8 PointList_WriteToFile(WPL_Store_t * pWPL_Store)
{
        u8 filename[30];
       
       
        if(PointCount == 0) return(WPL_NO_WAYPOINTS);
        // user absolute path, i.e. leading /  
        if(pWPL_Store->Index == 0)
        {
                sprintf(filename, "/default.wpl");
        }
        else
        {
                sprintf(filename, "/WPL/list_%03d.wpl", pWPL_Store->Index);
        }
        return PointList_Save(filename, pWPL_Store->Name, pWPL_Store->OverwriteFile);
}


// save actual gps positiin and heading to file
u8 PointList_SaveSinglePoint(WPL_Store_t * pWPL_Store)
{
        u8 retval = WPL_ERROR;
        u8 filename[30];
        Point_t WP;

        UART1_PutString("\n\r write single point\n\r");
        if(GPSData.Position.Status == INVALID)
         {
                UART1_PutString("ERROR: No GPS - Fix\n\r");
                return(retval);
         }

        // clear current point list
        PointList_Clear();
        // prepare WP at current position

        if(NCFlags & NC_FLAG_FREE || NaviData.TargetPositionDeviation.Distance > 7*10)
        {  // take actual position
                GPSPos_Copy(&GPSData.Position, &(WP.Position));
        }
        else
        {  // take last target position
                GPSPos_Copy(&NaviData.TargetPosition, &(WP.Position));
        }

        GPSPos_Copy(&GPSData.Position, &(WP.Position));
        // set heading
        WP.Heading = (CompassSetpointCorrected/10 + Parameter.CamOrientation * 15) % 360;
        if(WP.Heading == 0) WP.Heading = 360;
        WP.ToleranceRadius = 120; // 12m
        WP.HoldTime  = 2;
        WP.Index  = 1;
        WP.Type = POINT_TYPE_WP;
        WP.WP_EventChannelValue = 0;
        if(FC.StatusFlags & FC_STATUS_FLY && (FromFC_VarioCharacter != ' ')) // only in flight and if the Altitude control is enabled
         {
          WP.AltitudeRate = 255;  // Auto
//        WP.Position.Altitude = NaviData.Altimeter / 2;
          WP.Position.Altitude = NaviData.SetpointAltitude / 2;
         }
        else
        {
         WP.AltitudeRate = 0;
     WP.Position.Altitude = 0;
        }
        WP.Speed = 50; // beim Laden wird der Wert nochmal neu gesetzt
        WP.CamAngle = 0;
        WP.Name[0] = 'S';
        WP.Name[1] = 'P';
        WP.Name[2] = 'T';
        WP.Name[3] = 0;
        // add this point to wp list
        PointList_SetAt(&WP);

        sprintf(filename, "/POINT/POINT%03d.wpl", pWPL_Store->Index);
        UART1_PutString(filename);
        sprintf(pWPL_Store->Name, "POINT%03d ", pWPL_Store->Index);
        retval = PointList_Save(filename, pWPL_Store->Name, 1);

        // clear current point list
        if((NC_GPS_ModeCharacter != 'w') && (NC_GPS_ModeCharacter != 'W')) PointList_Clear();
        else
        {
                if(FC.StatusFlags & FC_STATUS_FLY) PointList_WPActive(TRUE);
                GPS_pWaypoint = PointList_WPBegin(); // updates POI index
        }

        return(retval);
}
// load target gps posititon and heading from file
u8 PointList_LoadSinglePoint(WPL_Store_t * pWPL_Store)
{
        u8 filename[30], result = 0;
       
        sprintf(filename, "/POINT/POINT%03d.wpl", pWPL_Store->Index);
        pWPL_Store->Name[0] = 0; // clear current list name
        result = PointList_Load(filename, pWPL_Store->Name, sizeof(pWPL_Store->Name),Parameter.SingleWpSpeed);
        if(result) UART1_Request_ReadPoint = 1; // Sends Point 1 to the PC
        return(result);
}



void ClearWLP_Name(void)
{
        u8 i;
        for(i=0; i<sizeof(WPL_Store.Name);i++) WPL_Store.Name[i] = 0;
        NewWPL_Name = 1;
}

// move actual point list to ref pos., the point in the list marked by index gets the RefPos afterwards
u8 PointList_Move(u8 RefIndex, GPS_Pos_t* pRefPos, u16 RotationAngle)
{
        u8 retval = 0;
        s32 altitude;
        GPS_Pos_t OldRefPos;
        GPS_Pos_Deviation_t RefDeviation;
         
        // check inputs for plausibility;
        if((RefIndex == 0) || (RefIndex > PointCount)) return(retval); 
        if(pRefPos == NULL) return(retval);
        if(pRefPos->Status == INVALID) return(retval);

        if(GPSPos_Copy(&(PointList[RefIndex-1].Position), &OldRefPos)) // backup old reference position
        {
                u8 i;
                // iterate the position list
                for(i = 0; i < PointCount; i++)
                {
                        retval = 0;
                        // backup altitude of this point
                        altitude = PointList[i].Position.Altitude;
                        // calculate deviation form old ref, i.e the north and east shift of each point in the list from the reference position
                        if(!GPSPos_Deviation(&(PointList[i].Position), &OldRefPos, &RefDeviation)) break;
                        // copy of the new reference position into this list place
                        if(!GPSPos_Copy(pRefPos, &(PointList[i].Position))) break;
                        // restore former altitude
                        PointList[i].Position.Altitude = altitude;
                        // move new reference according to the deviation of the old reference
                        if(RotationAngle > 0)
                        {
                                retval = GPSPos_ShiftGeodetic(&(PointList[i].Position), (RefDeviation.Bearing + 180 + RotationAngle)%360, RefDeviation.Distance );
                                // Now rotate the heading positions if they are fixed angles
                                if(PointList[i].Heading >= 0 && PointList[i].Heading <= 360) PointList[i].Heading = (PointList[i].Heading + RotationAngle) % 360;
                        }
                        else // no rotation
                        {
                                // move new reference according to the deviation of the old reference
                                retval = GPSPos_ShiftCartesian(&(PointList[i].Position), RefDeviation.North, RefDeviation.East);
                        }
                        if(!retval) break;             
                }
        } // else ref pos old not copied!
        if(!retval) PointList_Clear();
        return(retval);
}