Subversion Repositories NaviCtrl

Rev

Rev 504 | Rev 514 | Go to most recent revision | Only display areas with differences | Ignore whitespace | Details | Blame | Last modification | View Log | RSS feed

Rev 504 Rev 513
1
/*#######################################################################################*/
1
/*#######################################################################################*/
2
/* !!! THIS IS NOT FREE SOFTWARE !!!                                                     */
2
/* !!! THIS IS NOT FREE SOFTWARE !!!                                                     */
3
/*#######################################################################################*/
3
/*#######################################################################################*/
4
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
4
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
5
// + www.MikroKopter.com
5
// + www.MikroKopter.com
6
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
6
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
7
// + Software Nutzungsbedingungen (english version: see below)
7
// + Software Nutzungsbedingungen (english version: see below)
8
// + der Fa. HiSystems GmbH, Flachsmeerstrasse 2, 26802 Moormerland - nachfolgend Lizenzgeber genannt -
8
// + der Fa. HiSystems GmbH, Flachsmeerstrasse 2, 26802 Moormerland - nachfolgend Lizenzgeber genannt -
9
// + Der Lizenzgeber räumt dem Kunden ein nicht-ausschließliches, zeitlich und räumlich* unbeschränktes Recht ein, die im den
9
// + Der Lizenzgeber räumt dem Kunden ein nicht-ausschließliches, zeitlich und räumlich* unbeschränktes Recht ein, die im den
10
// + Mikrocontroller verwendete Firmware für die Hardware Flight-Ctrl, Navi-Ctrl, BL-Ctrl, MK3Mag & PC-Programm MikroKopter-Tool 
10
// + Mikrocontroller verwendete Firmware für die Hardware Flight-Ctrl, Navi-Ctrl, BL-Ctrl, MK3Mag & PC-Programm MikroKopter-Tool 
11
// + - nachfolgend Software genannt - nur für private Zwecke zu nutzen.
11
// + - nachfolgend Software genannt - nur für private Zwecke zu nutzen.
12
// + Der Einsatz dieser Software ist nur auf oder mit Produkten des Lizenzgebers zulässig.
12
// + Der Einsatz dieser Software ist nur auf oder mit Produkten des Lizenzgebers zulässig.
13
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
13
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
14
// + Die vom Lizenzgeber gelieferte Software ist urheberrechtlich geschützt. Alle Rechte an der Software sowie an sonstigen im
14
// + Die vom Lizenzgeber gelieferte Software ist urheberrechtlich geschützt. Alle Rechte an der Software sowie an sonstigen im
15
// + Rahmen der Vertragsanbahnung und Vertragsdurchführung überlassenen Unterlagen stehen im Verhältnis der Vertragspartner ausschließlich dem Lizenzgeber zu.
15
// + Rahmen der Vertragsanbahnung und Vertragsdurchführung überlassenen Unterlagen stehen im Verhältnis der Vertragspartner ausschließlich dem Lizenzgeber zu.
16
// + Die in der Software enthaltenen Copyright-Vermerke, Markenzeichen, andere Rechtsvorbehalte, Seriennummern sowie
16
// + Die in der Software enthaltenen Copyright-Vermerke, Markenzeichen, andere Rechtsvorbehalte, Seriennummern sowie
17
// + sonstige der Programmidentifikation dienenden Merkmale dürfen vom Kunden nicht verändert oder unkenntlich gemacht werden.
17
// + sonstige der Programmidentifikation dienenden Merkmale dürfen vom Kunden nicht verändert oder unkenntlich gemacht werden.
18
// + Der Kunde trifft angemessene Vorkehrungen für den sicheren Einsatz der Software. Er wird die Software gründlich auf deren
18
// + Der Kunde trifft angemessene Vorkehrungen für den sicheren Einsatz der Software. Er wird die Software gründlich auf deren
19
// + Verwendbarkeit zu dem von ihm beabsichtigten Zweck testen, bevor er diese operativ einsetzt.
19
// + Verwendbarkeit zu dem von ihm beabsichtigten Zweck testen, bevor er diese operativ einsetzt.
20
// + Die Haftung des Lizenzgebers wird - soweit gesetzlich zulässig - begrenzt in Höhe des typischen und vorhersehbaren
20
// + Die Haftung des Lizenzgebers wird - soweit gesetzlich zulässig - begrenzt in Höhe des typischen und vorhersehbaren
21
// + Schadens. Die gesetzliche Haftung bei Personenschäden und nach dem Produkthaftungsgesetz bleibt unberührt. Dem Lizenzgeber steht jedoch der Einwand 
21
// + Schadens. Die gesetzliche Haftung bei Personenschäden und nach dem Produkthaftungsgesetz bleibt unberührt. Dem Lizenzgeber steht jedoch der Einwand 
22
// + des Mitverschuldens offen.
22
// + des Mitverschuldens offen.
23
// + Der Kunde trifft angemessene Vorkehrungen für den Fall, dass die Software ganz oder teilweise nicht ordnungsgemäß arbeitet.
23
// + Der Kunde trifft angemessene Vorkehrungen für den Fall, dass die Software ganz oder teilweise nicht ordnungsgemäß arbeitet.
24
// + Er wird die Software gründlich auf deren Verwendbarkeit zu dem von ihm beabsichtigten Zweck testen, bevor er diese operativ einsetzt.
24
// + Er wird die Software gründlich auf deren Verwendbarkeit zu dem von ihm beabsichtigten Zweck testen, bevor er diese operativ einsetzt.
25
// + Der Kunde wird er seine Daten vor Einsatz der Software nach dem Stand der Technik sichern.
25
// + Der Kunde wird er seine Daten vor Einsatz der Software nach dem Stand der Technik sichern.
26
// + Der Kunde ist darüber unterrichtet, dass der Lizenzgeber seine Daten im zur Vertragsdurchführung erforderlichen Umfang
26
// + Der Kunde ist darüber unterrichtet, dass der Lizenzgeber seine Daten im zur Vertragsdurchführung erforderlichen Umfang
27
// + und auf Grundlage der Datenschutzvorschriften erhebt, speichert, verarbeitet und, sofern notwendig, an Dritte übermittelt.
27
// + und auf Grundlage der Datenschutzvorschriften erhebt, speichert, verarbeitet und, sofern notwendig, an Dritte übermittelt.
28
// + *) Die räumliche Nutzung bezieht sich nur auf den Einsatzort, nicht auf die Reichweite der programmierten Software.
28
// + *) Die räumliche Nutzung bezieht sich nur auf den Einsatzort, nicht auf die Reichweite der programmierten Software.
29
// + #### ENDE DER NUTZUNGSBEDINGUNGEN ####'
29
// + #### ENDE DER NUTZUNGSBEDINGUNGEN ####'
30
// +  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.
30
// +  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.
31
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
31
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
32
// + Software LICENSING TERMS
32
// + Software LICENSING TERMS
33
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
33
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
34
// + of HiSystems GmbH, Flachsmeerstrasse 2, 26802 Moormerland, Germany - the Licensor -
34
// + of HiSystems GmbH, Flachsmeerstrasse 2, 26802 Moormerland, Germany - the Licensor -
35
// + 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 
35
// + 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 
36
// + (the Software) exclusively for private purposes. The License is unrestricted with respect to time and territory*.
36
// + (the Software) exclusively for private purposes. The License is unrestricted with respect to time and territory*.
37
// + The Software may only be used with the Licensor's products.
37
// + The Software may only be used with the Licensor's products.
38
// + The Software provided by the Licensor is protected by copyright. With respect to the relationship between the parties to this
38
// + The Software provided by the Licensor is protected by copyright. With respect to the relationship between the parties to this
39
// + agreement, all rights pertaining to the Software and other documents provided during the preparation and execution of this
39
// + agreement, all rights pertaining to the Software and other documents provided during the preparation and execution of this
40
// + agreement shall be the property of the Licensor.
40
// + agreement shall be the property of the Licensor.
41
// + The information contained in the Software copyright notices, trademarks, other legal reservations, serial numbers and other
41
// + The information contained in the Software copyright notices, trademarks, other legal reservations, serial numbers and other
42
// + features that can be used to identify the program may not be altered or defaced by the customer.
42
// + features that can be used to identify the program may not be altered or defaced by the customer.
43
// + The customer shall be responsible for taking reasonable precautions
43
// + The customer shall be responsible for taking reasonable precautions
44
// + for the safe use of the Software. The customer shall test the Software thoroughly regarding its suitability for the
44
// + for the safe use of the Software. The customer shall test the Software thoroughly regarding its suitability for the
45
// + intended purpose before implementing it for actual operation. The Licensor's liability shall be limited to the extent of typical and
45
// + intended purpose before implementing it for actual operation. The Licensor's liability shall be limited to the extent of typical and
46
// + foreseeable damage to the extent permitted by law, notwithstanding statutory liability for bodily injury and product
46
// + foreseeable damage to the extent permitted by law, notwithstanding statutory liability for bodily injury and product
47
// + liability. However, the Licensor shall be entitled to the defense of contributory negligence.
47
// + liability. However, the Licensor shall be entitled to the defense of contributory negligence.
48
// + The customer will take adequate precautions in the case, that the software is not working properly. The customer will test
48
// + The customer will take adequate precautions in the case, that the software is not working properly. The customer will test
49
// + the software for his purpose before any operational usage. The customer will backup his data before using the software.
49
// + the software for his purpose before any operational usage. The customer will backup his data before using the software.
50
// + The customer understands that the Licensor collects, stores and processes, and, where required, forwards, customer data
50
// + The customer understands that the Licensor collects, stores and processes, and, where required, forwards, customer data
51
// + to third parties to the extent necessary for executing the agreement, subject to applicable data protection and privacy regulations.
51
// + to third parties to the extent necessary for executing the agreement, subject to applicable data protection and privacy regulations.
52
// + *) The territory aspect only refers to the place where the Software is used, not its programmed range.
52
// + *) The territory aspect only refers to the place where the Software is used, not its programmed range.
53
// + #### END OF LICENSING TERMS ####
53
// + #### END OF LICENSING TERMS ####
54
// + Note: For information on license extensions (e.g. commercial use), please contact us at info(@)hisystems.de.
54
// + Note: For information on license extensions (e.g. commercial use), please contact us at info(@)hisystems.de.
55
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
55
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
56
#include <ctype.h>
56
#include <ctype.h>
57
#include <stdio.h>
57
#include <stdio.h>
58
#include <stdlib.h>      
58
#include <stdlib.h>      
59
#include <string.h>
59
#include <string.h>
60
#include "91x_lib.h"
60
#include "91x_lib.h"
61
#include "waypoints.h"
61
#include "waypoints.h"
62
#include "uart1.h"
62
#include "uart1.h"
63
#include "fat16.h"
63
#include "fat16.h"
64
#include "main.h"
64
#include "main.h"
-
 
65
#include "spi_slave.h"
65
 
66
 
66
 
67
 
67
WPL_Store_t WPL_Store;
68
WPL_Store_t WPL_Store;
68
 
69
 
69
// the waypoints list
70
// the waypoints list
70
#define MAX_LIST_LEN 101
71
#define MAX_LIST_LEN 101
71
 
72
 
72
Point_t PointList[MAX_LIST_LEN];
73
Point_t PointList[MAX_LIST_LEN];
73
u8 WPIndex = 0;         // list index of GPS point representig the current WP, can be maximal WPCount
74
u8 WPIndex = 0;         // list index of GPS point representig the current WP, can be maximal WPCount
74
u8 POIIndex = 0;        // list index of GPS Point representing the current POI, can be maximal WPCount
75
u8 POIIndex = 0;        // list index of GPS Point representing the current POI, can be maximal WPCount
75
u8 WPCount = 0;         // number of waypoints
76
u8 WPCount = 0;         // number of waypoints
76
u8 PointCount = 0;      // number of points in the list can be maximal equal to MAX_LIST_LEN
77
u8 PointCount = 0;      // number of points in the list can be maximal equal to MAX_LIST_LEN
77
u8 POICount = 0;        // number of point of interest in the list
78
u8 POICount = 0;        // number of point of interest in the list
78
 
79
 
79
u8 WPActive = FALSE;
80
u8 WPActive = FALSE;
80
 
81
 
81
u8 PointList_Init(void)
82
u8 PointList_Init(void)
82
{
83
{
83
        return PointList_Clear();
84
        return PointList_Clear();
84
}
85
}
85
 
86
 
86
u8 PointList_Clear(void)
87
u8 PointList_Clear(void)
87
{
88
{
88
        u8 i;
89
        u8 i;
89
        WPIndex = 0;    // real list position are 1 ,2, 3 ...
90
        WPIndex = 0;    // real list position are 1 ,2, 3 ...
90
        POIIndex = 0;   // real list position are 1 ,2, 3 ...
91
        POIIndex = 0;   // real list position are 1 ,2, 3 ...
91
        WPCount = 0;    // no waypoints
92
        WPCount = 0;    // no waypoints
92
    POICount = 0;
93
    POICount = 0;
93
        PointCount = 0; // no contents
94
        PointCount = 0; // no contents
94
        WPActive = FALSE;
95
        WPActive = FALSE;
95
        NaviData.WaypointNumber = WPCount;
96
        NaviData.WaypointNumber = WPCount;
96
        NaviData.WaypointIndex = 0;
97
        NaviData.WaypointIndex = 0;
97
 
98
 
98
        for(i = 0; i < MAX_LIST_LEN; i++)
99
        for(i = 0; i < MAX_LIST_LEN; i++)
99
        {
100
        {
100
                PointList[i].Position.Status = INVALID;
101
                PointList[i].Position.Status = INVALID;
101
                PointList[i].Position.Latitude = 0;
102
                PointList[i].Position.Latitude = 0;
102
                PointList[i].Position.Longitude = 0;
103
                PointList[i].Position.Longitude = 0;
103
                PointList[i].Position.Altitude = 0;
104
                PointList[i].Position.Altitude = 0;
104
                PointList[i].Heading = 361;             // invalid value
105
                PointList[i].Heading = 361;             // invalid value
105
                PointList[i].ToleranceRadius = 0;       // in meters, if the MK is within that range around the target, then the next target is triggered
106
                PointList[i].ToleranceRadius = 0;       // in meters, if the MK is within that range around the target, then the next target is triggered
106
                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
107
                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
107
                PointList[i].Event_Flag = 0;            // future implementation
108
                PointList[i].Event_Flag = 0;            // future implementation
108
                PointList[i].Index = 0;
109
                PointList[i].Index = 0;
109
                PointList[i].Type = POINT_TYPE_INVALID;
110
                PointList[i].Type = POINT_TYPE_INVALID;
110
                PointList[i].WP_EventChannelValue = 0;
111
                PointList[i].WP_EventChannelValue = 0;
111
                PointList[i].AltitudeRate = 0;          // no change of setpoint
112
                PointList[i].AltitudeRate = 0;          // no change of setpoint
112
                PointList[i].Speed = 0;
113
                PointList[i].Speed = 0;
113
                PointList[i].CamAngle = 0;
114
                PointList[i].CamAngle = 0;
114
                PointList[i].Name[0] = 0;
115
                PointList[i].Name[0] = 0;
115
        }
116
        }
116
        ClearWLP_Name();
117
        ClearWLP_Name();
117
        return TRUE;           
118
        return TRUE;           
118
}
119
}
119
 
120
 
120
u8 PointList_GetCount(void)
121
u8 PointList_GetCount(void)
121
{
122
{
122
        return PointCount; // number of points in the list
123
        return PointCount; // number of points in the list
123
}
124
}
124
 
125
 
125
Point_t* PointList_GetAt(u8 index)
126
Point_t* PointList_GetAt(u8 index)
126
{
127
{
127
        if((index > 0) && (index <= PointCount)) return(&(PointList[index-1])); // return pointer to this waypoint
128
        if((index > 0) && (index <= PointCount)) return(&(PointList[index-1])); // return pointer to this waypoint
128
        else return(NULL);
129
        else return(NULL);
129
}
130
}
130
 
131
 
131
u8 PointList_SetAt(Point_t* pPoint)
132
u8 PointList_SetAt(Point_t* pPoint)
132
{
133
{
133
        // if index is in range
134
        // if index is in range
134
        if((pPoint->Index > 0) && (pPoint->Index <= MAX_LIST_LEN))
135
        if((pPoint->Index > 0) && (pPoint->Index <= MAX_LIST_LEN))
135
        {
136
        {
136
                // check list entry before update
137
                // check list entry before update
137
                switch(PointList[pPoint->Index-1].Type)
138
                switch(PointList[pPoint->Index-1].Type)
138
                {
139
                {
139
                        case POINT_TYPE_INVALID: // was invalid
140
                        case POINT_TYPE_INVALID: // was invalid
140
                                switch(pPoint->Type)
141
                                switch(pPoint->Type)
141
                                {
142
                                {
142
                                        default:
143
                                        default:
143
                                        case POINT_TYPE_INVALID:
144
                                        case POINT_TYPE_INVALID:
144
                                                // nothing to do
145
                                                // nothing to do
145
                                                break;
146
                                                break;
146
 
147
 
147
                                        case POINT_TYPE_WP:
148
                                        case POINT_TYPE_WP:
148
                                                WPCount++;
149
                                                WPCount++;
149
                                                PointCount++;
150
                                                PointCount++;
150
                                                break;
151
                                                break;
151
                                       
152
                                       
152
                                        case POINT_TYPE_POI:
153
                                        case POINT_TYPE_POI:
153
                                                POICount++;
154
                                                POICount++;
154
                                                PointCount++;
155
                                                PointCount++;
155
                                                break;
156
                                                break;
156
                                }
157
                                }
157
                                break;
158
                                break;
158
                               
159
                               
159
                        case POINT_TYPE_WP: // was a waypoint
160
                        case POINT_TYPE_WP: // was a waypoint
160
                                switch(pPoint->Type)
161
                                switch(pPoint->Type)
161
                                {
162
                                {
162
                                        case POINT_TYPE_INVALID:
163
                                        case POINT_TYPE_INVALID:
163
                                                WPCount--;
164
                                                WPCount--;
164
                                                PointCount--;
165
                                                PointCount--;
165
                                                break;
166
                                                break;
166
 
167
 
167
                                        default:
168
                                        default:
168
                                        case POINT_TYPE_WP:
169
                                        case POINT_TYPE_WP:
169
                                                //nothing to do
170
                                                //nothing to do
170
                                                break;
171
                                                break;
171
                                       
172
                                       
172
                                        case POINT_TYPE_POI:
173
                                        case POINT_TYPE_POI:
173
                                                POICount++;
174
                                                POICount++;
174
                                                WPCount--;
175
                                                WPCount--;
175
                                                break;
176
                                                break;
176
                                }
177
                                }
177
                                break;
178
                                break;
178
                               
179
                               
179
                        case POINT_TYPE_POI: // was a poi
180
                        case POINT_TYPE_POI: // was a poi
180
                                switch(pPoint->Type)
181
                                switch(pPoint->Type)
181
                                {
182
                                {
182
                                        case POINT_TYPE_INVALID:
183
                                        case POINT_TYPE_INVALID:
183
                                                POICount--;
184
                                                POICount--;
184
                                                PointCount--;
185
                                                PointCount--;
185
                                                break;
186
                                                break;
186
 
187
 
187
                                        case POINT_TYPE_WP:
188
                                        case POINT_TYPE_WP:
188
                                                WPCount++;
189
                                                WPCount++;
189
                                                POICount--;
190
                                                POICount--;
190
                                                break;
191
                                                break;
191
                                       
192
                                       
192
                                        case POINT_TYPE_POI:
193
                                        case POINT_TYPE_POI:
193
                                        default:
194
                                        default:
194
                                                // nothing to do
195
                                                // nothing to do
195
                                                break;
196
                                                break;
196
                                }
197
                                }
197
                                break;         
198
                                break;         
198
                }
199
                }
199
                memcpy(&PointList[pPoint->Index-1], pPoint, sizeof(Point_t)); // copy data to list entry                                                                                
200
                memcpy(&PointList[pPoint->Index-1], pPoint, sizeof(Point_t)); // copy data to list entry                                                                                
200
                NaviData.WaypointNumber = WPCount;
201
                NaviData.WaypointNumber = WPCount;
201
                return pPoint->Index;
202
                return pPoint->Index;
202
        }
203
        }
203
        else return(0);
204
        else return(0);
204
}
205
}
205
 
206
 
206
// returns the pointer to the first waypoint within the list
207
// returns the pointer to the first waypoint within the list
207
Point_t* PointList_WPBegin(void)
208
Point_t* PointList_WPBegin(void)
208
{
209
{
209
        u8 i;
210
        u8 i;
210
        WPIndex = 0; // set list position invalid
211
        WPIndex = 0; // set list position invalid
211
 
212
 
212
        if(WPActive == FALSE) return(NULL);
213
        if(WPActive == FALSE) return(NULL);
213
 
214
 
214
        POIIndex = 0; // set invalid POI
215
        POIIndex = 0; // set invalid POI
215
        if(PointCount > 0)
216
        if(PointCount > 0)
216
        {
217
        {
217
                // search for first wp in list
218
                // search for first wp in list
218
                for(i = 0; i <MAX_LIST_LEN; i++)
219
                for(i = 0; i <MAX_LIST_LEN; i++)
219
                {
220
                {
220
                        if((PointList[i].Type == POINT_TYPE_WP) && (PointList[i].Position.Status != INVALID))
221
                        if((PointList[i].Type == POINT_TYPE_WP) && (PointList[i].Position.Status != INVALID))
221
                        {
222
                        {
222
                                WPIndex = i + 1;
223
                                WPIndex = i + 1;
223
                                break;
224
                                break;
224
                        }
225
                        }
225
                }
226
                }
226
                if(WPIndex) // found a WP in the list
227
                if(WPIndex) // found a WP in the list
227
                {
228
                {
228
                        NaviData.WaypointIndex = 1;
229
                        NaviData.WaypointIndex = 1;
229
                        // update index to POI
230
                        // update index to POI
230
                        if(PointList[WPIndex-1].Heading < 0) POIIndex = (u8)(-PointList[WPIndex-1].Heading);
231
                        if(PointList[WPIndex-1].Heading < 0) POIIndex = (u8)(-PointList[WPIndex-1].Heading);
231
                        else POIIndex = 0;                     
232
                        else POIIndex = 0;                     
232
                }
233
                }
233
                else // some points in the list but no WP found
234
                else // some points in the list but no WP found
234
                {
235
                {
235
                        NaviData.WaypointIndex = 0;
236
                        NaviData.WaypointIndex = 0;
236
                        //Check for an existing POI
237
                        //Check for an existing POI
237
                        for(i = 0; i < MAX_LIST_LEN; i++)
238
                        for(i = 0; i < MAX_LIST_LEN; i++)
238
                        {
239
                        {
239
                                if((PointList[i].Type == POINT_TYPE_POI) && (PointList[i].Position.Status != INVALID))
240
                                if((PointList[i].Type == POINT_TYPE_POI) && (PointList[i].Position.Status != INVALID))
240
                                {
241
                                {
241
                                        POIIndex = i + 1;
242
                                        POIIndex = i + 1;
242
                                        break;
243
                                        break;
243
                                }
244
                                }
244
                        }
245
                        }
245
                }
246
                }
246
        }
247
        }
247
        else // no point in the list
248
        else // no point in the list
248
        {
249
        {
249
                POIIndex = 0;
250
                POIIndex = 0;
250
                NaviData.WaypointIndex = 0;    
251
                NaviData.WaypointIndex = 0;    
251
        }
252
        }
252
 
253
 
253
        if(WPIndex) return(&(PointList[WPIndex-1]));
254
        if(WPIndex) return(&(PointList[WPIndex-1]));
254
        else return(NULL);
255
        else return(NULL);
255
}
256
}
256
 
257
 
257
// returns the last waypoint
258
// returns the last waypoint
258
Point_t* PointList_WPEnd(void)
259
Point_t* PointList_WPEnd(void)
259
{
260
{
260
       
261
       
261
        u8 i;
262
        u8 i;
262
        WPIndex = 0; // set list position invalid
263
        WPIndex = 0; // set list position invalid
263
        POIIndex = 0; // set invalid
264
        POIIndex = 0; // set invalid
264
 
265
 
265
        if(WPActive == FALSE) return(NULL);
266
        if(WPActive == FALSE) return(NULL);
266
 
267
 
267
        if(PointCount > 0)
268
        if(PointCount > 0)
268
        {
269
        {
269
                // search backward!
270
                // search backward!
270
                for(i = 1; i <= MAX_LIST_LEN; i++)
271
                for(i = 1; i <= MAX_LIST_LEN; i++)
271
                {
272
                {
272
                        if((PointList[MAX_LIST_LEN - i].Type == POINT_TYPE_WP) && (PointList[MAX_LIST_LEN - i].Position.Status != INVALID))
273
                        if((PointList[MAX_LIST_LEN - i].Type == POINT_TYPE_WP) && (PointList[MAX_LIST_LEN - i].Position.Status != INVALID))
273
                        {      
274
                        {      
274
                                WPIndex = MAX_LIST_LEN - i + 1;
275
                                WPIndex = MAX_LIST_LEN - i + 1;
275
                                break;
276
                                break;
276
                        }
277
                        }
277
                }
278
                }
278
                if(WPIndex) // found a WP within the list
279
                if(WPIndex) // found a WP within the list
279
                {
280
                {
280
                        NaviData.WaypointIndex = WPCount;
281
                        NaviData.WaypointIndex = WPCount;
281
                        if(PointList[WPIndex-1].Heading < 0) POIIndex = (u8)(-PointList[WPIndex-1].Heading);
282
                        if(PointList[WPIndex-1].Heading < 0) POIIndex = (u8)(-PointList[WPIndex-1].Heading);
282
                        else POIIndex = 0;     
283
                        else POIIndex = 0;     
283
                }
284
                }
284
                else // list contains some points but no WP in the list
285
                else // list contains some points but no WP in the list
285
                {
286
                {
286
                        // search backward for a POI!
287
                        // search backward for a POI!
287
                        for(i = 1; i <= MAX_LIST_LEN; i++)
288
                        for(i = 1; i <= MAX_LIST_LEN; i++)
288
                        {
289
                        {
289
                                if((PointList[MAX_LIST_LEN - i].Type == POINT_TYPE_POI) && (PointList[MAX_LIST_LEN - i].Position.Status != INVALID))
290
                                if((PointList[MAX_LIST_LEN - i].Type == POINT_TYPE_POI) && (PointList[MAX_LIST_LEN - i].Position.Status != INVALID))
290
                                {      
291
                                {      
291
                                        POIIndex = MAX_LIST_LEN - i + 1;
292
                                        POIIndex = MAX_LIST_LEN - i + 1;
292
                                        break;
293
                                        break;
293
                                }
294
                                }
294
                        }
295
                        }
295
                        NaviData.WaypointIndex = 0;    
296
                        NaviData.WaypointIndex = 0;    
296
                }
297
                }
297
        }
298
        }
298
        else // no point in the list
299
        else // no point in the list
299
        {
300
        {
300
                POIIndex = 0;
301
                POIIndex = 0;
301
                NaviData.WaypointIndex = 0;
302
                NaviData.WaypointIndex = 0;
302
        }
303
        }
303
        if(WPIndex) return(&(PointList[WPIndex-1]));
304
        if(WPIndex) return(&(PointList[WPIndex-1]));
304
        else return(NULL);
305
        else return(NULL);
305
}
306
}
306
 
307
 
307
// returns a pointer to the next waypoint or NULL if the end of the list has been reached
308
// returns a pointer to the next waypoint or NULL if the end of the list has been reached
308
Point_t* PointList_WPNext(void)
309
Point_t* PointList_WPNext(void)
309
{
310
{
310
        u8 wp_found = 0;
311
        u8 wp_found = 0;
311
        if(WPActive == FALSE) return(NULL);
312
        if(WPActive == FALSE) return(NULL);
312
               
313
               
313
        if(WPIndex < MAX_LIST_LEN) // if there is a next entry in the list
314
        if(WPIndex < MAX_LIST_LEN) // if there is a next entry in the list
314
        {
315
        {
315
                u8 i;
316
                u8 i;
316
                for(i = WPIndex; i < MAX_LIST_LEN; i++) // start search for next at next list entry
317
                for(i = WPIndex; i < MAX_LIST_LEN; i++) // start search for next at next list entry
317
                {
318
                {
318
                        if((PointList[i].Type == POINT_TYPE_WP) && (PointList[i].Position.Status != INVALID)) // jump over POIs
319
                        if((PointList[i].Type == POINT_TYPE_WP) && (PointList[i].Position.Status != INVALID)) // jump over POIs
319
                        {
320
                        {
320
                                wp_found = i+1;
321
                                wp_found = i+1;
321
                                break;
322
                                break;
322
                        }
323
                        }
323
                }
324
                }
324
        }
325
        }
325
        if(wp_found)
326
        if(wp_found)
326
        {
327
        {
327
                WPIndex = wp_found; // update list position
328
                WPIndex = wp_found; // update list position
328
                NaviData.WaypointIndex++;
329
                NaviData.WaypointIndex++;
329
                if(PointList[WPIndex-1].Heading < 0) POIIndex = (u8)(-PointList[WPIndex-1].Heading);
330
                if(PointList[WPIndex-1].Heading < 0) POIIndex = (u8)(-PointList[WPIndex-1].Heading);
330
                else POIIndex = 0;
331
                else POIIndex = 0;
331
                return(&(PointList[WPIndex-1]));        // return pointer to this waypoint
332
                return(&(PointList[WPIndex-1]));        // return pointer to this waypoint
332
        }
333
        }
333
        else
334
        else
334
        {  // no next wp found
335
        {  // no next wp found
335
                NaviData.WaypointIndex = 0;    
336
                NaviData.WaypointIndex = 0;    
336
                POIIndex = 0;
337
                POIIndex = 0;
337
                return(NULL);
338
                return(NULL);
338
        }
339
        }
339
}      
340
}      
340
 
341
 
341
void PointList_WPActive(u8 set)
342
void PointList_WPActive(u8 set)
342
{
343
{
343
        if(set)
344
        if(set)
344
        {      
345
        {      
345
                WPActive = TRUE;
346
                WPActive = TRUE;
346
                PointList_WPBegin(); // updates POI index
347
                PointList_WPBegin(); // updates POI index
347
        }
348
        }
348
        else
349
        else
349
        {
350
        {
350
                WPActive = FALSE;
351
                WPActive = FALSE;
351
                POIIndex = 0;  // disable POI also
352
                POIIndex = 0;  // disable POI also
352
        }
353
        }
353
}
354
}
354
 
355
 
355
Point_t* PointList_GetPOI(void)
356
Point_t* PointList_GetPOI(void)
356
{
357
{
357
        return PointList_GetAt(POIIndex);      
358
        return PointList_GetAt(POIIndex);      
358
}
359
}
-
 
360
 
359
 
361
 
360
#define LINE_MAX 70
362
#define LINE_MAX 70
361
#define WP_FILE_VERSION_COMPATIBLE 3
-
 
-
 
363
#define WP_FILE_VERSION_COMPATIBLE 3
362
// save actual point list to SD card
364
 
363
u8 PointList_SaveToFile(WPL_Store_t * pWPL_Store)
365
u8 PointList_Save(u8 * filename, u8* listname, u8 overwride)
364
{
366
{
365
        File_t *fp;
367
        File_t *fp;
366
        s8 wpline[LINE_MAX];
368
        s8 wpline[LINE_MAX];
367
        u8 retval = WPL_ERROR;
369
        u8 retval = WPL_ERROR;
368
       
370
 
369
        if(PointCount == 0) return(WPL_NO_WAYPOINTS);
-
 
370
        // user absolute path, i.e. leading /   
-
 
371
        if(pWPL_Store->Index == 0)
-
 
372
        {
-
 
373
                sprintf(wpline, "/default.wpl");
-
 
374
        }
-
 
375
        else
-
 
376
        {
-
 
377
                sprintf(wpline, "/WPL/list_%03d.wpl", pWPL_Store->Index);
-
 
378
        }
-
 
379
        UART1_PutString("\n\r Save WPL...");
371
        UART1_PutString("\n\r Save WPL...");
380
 
372
 
381
        if(Fat16_IsValid())
373
        if(Fat16_IsValid())
382
        {       // check if wpl file is existing
374
        {       // check if wpl file is existing
383
                if(fexist_(wpline))
375
                if(fexist_(filename))
384
                {       //file is existent
376
                {       //file is existent
385
                        if(!(pWPL_Store->OverwriteFile))
377
                        if(!(overwride))
386
                        {
378
                        {
387
                                UART1_PutString("Error: file exist!\r\n");
379
                                UART1_PutString("Error: file exist!\r\n");
388
                                return(WPL_FILEEXIST);
380
                                return(WPL_FILEEXIST);
389
                        }      
381
                        }      
390
                }
382
                }
391
                fp = fopen_(wpline, 'w');               // try to open the file
383
                fp = fopen_(filename, 'w');             // try to open the file
392
                if(fp == NULL)
384
                if(fp == NULL)
393
                {
385
                {
394
                        UART1_PutString("ERROR: Creating waypoint file!\r\n");
386
                        UART1_PutString("ERROR: Creating waypoint file!\r\n");
395
                        return(retval);
387
                        return(retval);
396
                }
388
                }
397
                // Create general section and key entries
389
                // Create general section and key entries
398
                fputs_("[General]\r\n", fp);
390
                fputs_("[General]\r\n", fp);
399
                sprintf(wpline, "Name=%s\r\n",  pWPL_Store->Name);
391
                sprintf(wpline, "Name=%s\r\n",  listname);
400
                fputs_(wpline, fp);
392
                fputs_(wpline, fp);
401
                sprintf(wpline, "FileVersion=%d\r\n", WP_FILE_VERSION_COMPATIBLE);
393
                sprintf(wpline, "FileVersion=%d\r\n", WP_FILE_VERSION_COMPATIBLE);
402
                fputs_(wpline, fp);
394
                fputs_(wpline, fp);
403
                sprintf(wpline, "NumberOfWaypoints=%d\r\n", PointCount);
395
                sprintf(wpline, "NumberOfWaypoints=%d\r\n", PointCount);
404
                fputs_(wpline, fp);
396
                fputs_(wpline, fp);
405
                // dump all points if existent
397
                // dump all points if existent
406
                if(PointCount)
398
                if(PointCount)
407
                {
399
                {
408
                        u8 i, u8_1;
400
                        u8 i, u8_1;
409
                        s32 i32_1, i32_2;
401
                        s32 i32_1, i32_2;
410
                        NewWPL_Name = 1;
402
                        NewWPL_Name = 1;
411
                        for (i = 0; i < PointCount; i++)
403
                        for (i = 0; i < PointCount; i++)
412
                        {
404
                        {
413
                                sprintf(wpline, "[Point%d]\r\n",PointList[i].Index);
405
                                sprintf(wpline, "[Point%d]\r\n",PointList[i].Index);
414
                                fputs_(wpline, fp);
406
                                fputs_(wpline, fp);
415
                                // write latitude in deg
407
                                // write latitude in deg
416
                                if(PointList[i].Position.Latitude < 0) u8_1 = '-';
408
                                if(PointList[i].Position.Latitude < 0) u8_1 = '-';
417
                                else u8_1 = '+';
409
                                else u8_1 = '+';
418
                                i32_1 = abs(PointList[i].Position.Latitude)/10000000L;
410
                                i32_1 = abs(PointList[i].Position.Latitude)/10000000L;
419
                                i32_2 = abs(PointList[i].Position.Latitude)%10000000L;
411
                                i32_2 = abs(PointList[i].Position.Latitude)%10000000L;
420
                                sprintf(wpline, "Latitude=%c%ld.%07ld\r\n", u8_1, i32_1, i32_2);
412
                                sprintf(wpline, "Latitude=%c%ld.%07ld\r\n", u8_1, i32_1, i32_2);
421
                                fputs_(wpline, fp);
413
                                fputs_(wpline, fp);
422
                                // write longitude in deg
414
                                // write longitude in deg
423
                                if(PointList[i].Position.Longitude < 0) u8_1 = '-';
415
                                if(PointList[i].Position.Longitude < 0) u8_1 = '-';
424
                                else u8_1 = '+';
416
                                else u8_1 = '+';
425
                                i32_1 = abs(PointList[i].Position.Longitude)/10000000L;
417
                                i32_1 = abs(PointList[i].Position.Longitude)/10000000L;
426
                                i32_2 = abs(PointList[i].Position.Longitude)%10000000L;
418
                                i32_2 = abs(PointList[i].Position.Longitude)%10000000L;
427
                                sprintf(wpline, "Longitude=%c%ld.%07ld\r\n", u8_1, i32_1, i32_2);
419
                                sprintf(wpline, "Longitude=%c%ld.%07ld\r\n", u8_1, i32_1, i32_2);
428
                                fputs_(wpline, fp);
420
                                fputs_(wpline, fp);
429
                                // write tolerace radius in m
421
                                // write tolerace radius in m
430
                                sprintf(wpline, "Radius=%d\r\n", PointList[i].ToleranceRadius);
422
                                sprintf(wpline, "Radius=%d\r\n", PointList[i].ToleranceRadius);
431
                                fputs_(wpline, fp);
423
                                fputs_(wpline, fp);
432
                                // write altitude in m
424
                                // write altitude in m
433
                                if(PointList[i].Position.Altitude < 0) u8_1 = '-';
425
                                if(PointList[i].Position.Altitude < 0) u8_1 = '-';
434
                                else u8_1 = '+';
426
                                else u8_1 = '+';
435
                                if(PointList[i].Type == POINT_TYPE_POI)
427
                                if(PointList[i].Type == POINT_TYPE_POI)
436
                                {
428
                                {
437
                                        i32_1 = abs(PointList[i].Position.Altitude)/100L;  // cm --> m
429
                                        i32_1 = abs(PointList[i].Position.Altitude)/100L;  // cm --> m
438
                                        i32_2 = abs(PointList[i].Position.Altitude)%100L;
430
                                        i32_2 = abs(PointList[i].Position.Altitude)%100L;
439
                                }
431
                                }
440
                                else
432
                                else
441
                                {
433
                                {
442
                                        i32_1 = abs(PointList[i].Position.Altitude)/10L; // dm --> m
434
                                        i32_1 = abs(PointList[i].Position.Altitude)/10L; // dm --> m
443
                                        i32_2 = abs(PointList[i].Position.Altitude)%10L;
435
                                        i32_2 = abs(PointList[i].Position.Altitude)%10L;
444
                                }
436
                                }
445
                                sprintf(wpline, "Altitude=%c%ld.%01ld\r\n",  u8_1, i32_1, i32_2);
437
                                sprintf(wpline, "Altitude=%c%ld.%01ld\r\n",  u8_1, i32_1, i32_2);
446
                                fputs_(wpline, fp);
438
                                fputs_(wpline, fp);
447
                                // write climb rate in 0.1 m/s
439
                                // write climb rate in 0.1 m/s
448
                                sprintf(wpline, "ClimbRate=%d\r\n", PointList[i].AltitudeRate);
440
                                sprintf(wpline, "ClimbRate=%d\r\n", PointList[i].AltitudeRate);
449
                                fputs_(wpline, fp);
441
                                fputs_(wpline, fp);
450
                                // write hold time in s
442
                                // write hold time in s
451
                                sprintf(wpline, "DelayTime=%d\r\n", PointList[i].HoldTime);
443
                                sprintf(wpline, "DelayTime=%d\r\n", PointList[i].HoldTime);
452
                                fputs_(wpline, fp);
444
                                fputs_(wpline, fp);
453
                                // write event channel value
445
                                // write event channel value
454
                                sprintf(wpline, "WP_Event_Channel_Value=%d\r\n", PointList[i].WP_EventChannelValue);
446
                                sprintf(wpline, "WP_Event_Channel_Value=%d\r\n", PointList[i].WP_EventChannelValue);
455
                                fputs_(wpline, fp);
447
                                fputs_(wpline, fp);
456
                                // write heading in deg (0= nothing, neg. values index to poi)
448
                                // write heading in deg (0= nothing, neg. values index to poi)
457
                                sprintf(wpline, "Heading=%d\r\n", PointList[i].Heading);
449
                                sprintf(wpline, "Heading=%d\r\n", PointList[i].Heading);
458
                                fputs_(wpline, fp);
450
                                fputs_(wpline, fp);
459
                                // write speed in 0.1 m/s
451
                                // write speed in 0.1 m/s
460
                                sprintf(wpline, "Speed=%d\r\n", PointList[i].Speed);
452
                                sprintf(wpline, "Speed=%d\r\n", PointList[i].Speed);
461
                                fputs_(wpline, fp);
453
                                fputs_(wpline, fp);
462
                                // write cam angle in degree (255 -> POI-Automatic)
454
                                // write cam angle in degree (255 -> POI-Automatic)
463
                                sprintf(wpline, "CAM-Nick=%d\r\n", PointList[i].CamAngle);
455
                                sprintf(wpline, "CAM-Nick=%d\r\n", PointList[i].CamAngle);
464
                                fputs_(wpline, fp);
456
                                fputs_(wpline, fp);
465
                                // write point type
457
                                // write point type
466
                                sprintf(wpline, "Type=%d\r\n", PointList[i].Type + 1);
458
                                sprintf(wpline, "Type=%d\r\n", PointList[i].Type + 1);
467
                                fputs_(wpline, fp);    
459
                                fputs_(wpline, fp);    
468
                                // write prefix 
460
                                // write prefix 
469
                                sprintf(wpline, "Prefix=%s\r\n", PointList[i].Name);
461
                                sprintf(wpline, "Prefix=%s\r\n", PointList[i].Name);
470
                                fputs_(wpline, fp);
462
                                fputs_(wpline, fp);
471
                        } // EOF loop over all points
463
                        } // EOF loop over all points
472
                } // EOF if(PointCount)
464
                } // EOF if(PointCount)
473
                if(EOF == fclose_(fp))
465
                if(EOF == fclose_(fp))
474
                {
466
                {
475
                        UART1_PutString("failed!\r\n");
467
                        UART1_PutString("failed!\r\n");
476
                }
468
                }
477
                else
469
                else
478
                {
470
                {
479
                        UART1_PutString("ok\r\n");
471
                        UART1_PutString("ok\r\n");
480
                        retval = WPL_OK;
472
                        retval = WPL_OK;
481
                }                                
473
                }                                
482
        } // EOF if(Fat16_IsValid())
474
        } // EOF if(Fat16_IsValid())
483
        else
475
        else
484
        {
476
        {
485
                UART1_PutString("no file system found!\r\n");
477
                UART1_PutString("no file system found!\r\n");
486
                retval = WPL_NO_SDCARD_FOUND;
478
                retval = WPL_NO_SDCARD_FOUND;
487
        }
479
        }
488
        return(retval);
480
        return(retval);
489
}
481
}
490
 
-
 
491
// load actual point list from SD card
482
 
492
u8 PointList_ReadFromFile(WPL_Store_t * pWPL_Store)
483
u8 PointList_Load(u8 * filename, u8* listname, u8 listnamelen)
493
{
484
{
494
        File_t *fp;
485
        File_t *fp;
495
        s8 wpline[LINE_MAX];
486
        s8 wpline[LINE_MAX];
496
        u8 retval = WPL_ERROR;
487
        u8 retval = WPL_ERROR;
497
       
488
       
498
        s8 *name, *value;
489
        s8 *name, *value;
499
        u8 i;
490
        u8 i;
500
 
491
 
501
        u8 IsGeneralSection = 0;
492
        u8 IsGeneralSection = 0;
502
        u8 IsPointSection  = 0;
493
        u8 IsPointSection  = 0;
503
        u8 WPNumber = 0;
494
        u8 WPNumber = 0;
504
 
-
 
505
 
495
 
506
        // clear point list first
496
        // clear point list first
507
        PointList_Clear();
497
        PointList_Clear();
508
        pWPL_Store->Name[0] = 0; // clear current list name
-
 
509
 
-
 
510
        // user absolute path, i.e. leading /
-
 
511
        if(pWPL_Store->Index == 0) // index 0 looks for a default WPL file in the root
-
 
512
        {
-
 
513
                sprintf(wpline, "/default.wpl");       
-
 
514
        }
-
 
515
        else
-
 
516
        {
-
 
517
                sprintf(wpline, "/WPL/list_%03d.wpl", pWPL_Store->Index);
-
 
518
        }
-
 
519
 
498
 
520
        UART1_PutString("\n\r Read ");
499
        UART1_PutString("\n\r Read ");
521
        UART1_PutString(wpline);
500
        UART1_PutString(filename);
522
        UART1_PutString("...");
501
        UART1_PutString("...");
523
 
502
 
524
        if(Fat16_IsValid())
503
        if(Fat16_IsValid())
525
        {       // check if wpl file is existing
504
        {       // check if wpl file is existing
526
                fp = fopen_(wpline, 'r');               // try to open the file
505
                fp = fopen_(filename, 'r');             // try to open the file
527
                if(fp == NULL)
506
                if(fp == NULL)
528
                {
507
                {
529
                        UART1_PutString("ERROR: Reading waypoint file!\r\n");
508
                        UART1_PutString("ERROR: Reading waypoint file!\r\n");
530
                        return(retval);
509
                        return(retval);
531
                }
510
                }
532
                // read all lines from file
511
                // read all lines from file
533
                while(fgets_(wpline, LINE_MAX, fp) != 0)
512
                while(fgets_(wpline, LINE_MAX, fp) != 0)
534
                {
513
                {
535
                        if ( // ignorelines starting with \r,\n,' ',';','#'
514
                        if ( // ignorelines starting with \r,\n,' ',';','#'
536
                                (wpline[0] != '\n') &&
515
                                (wpline[0] != '\n') &&
537
                                (wpline[0] != '\r') &&
516
                                (wpline[0] != '\r') &&
538
                                (wpline[0] != ' ' ) &&
517
                                (wpline[0] != ' ' ) &&
539
                                (wpline[0] != ';' ) &&
518
                                (wpline[0] != ';' ) &&
540
                                (wpline[0] != '#' )
519
                                (wpline[0] != '#' )
541
                                )
520
                                )
542
                        {
521
                        {
543
                                // check for section line found
522
                                // check for section line found
544
                                if(wpline[0] == '[')
523
                                if(wpline[0] == '[')
545
                                {
524
                                {
546
                                        // next section found
525
                                        // next section found
547
                                        IsGeneralSection = 0;
526
                                        IsGeneralSection = 0;
548
                                        IsPointSection = 0;
527
                                        IsPointSection = 0;
549
 
528
 
550
                                        name  = strtok(&wpline[1], "]");
529
                                        name  = strtok(&wpline[1], "]");
551
                                        if(name != NULL)   // if section name
530
                                        if(name != NULL)   // if section name
552
                                        {
531
                                        {
553
                                                // check section type
532
                                                // check section type
554
                                                for(i=0; name[i]; i++) name[i] = toupper(name[i]); // convert to upper case
533
                                                for(i=0; name[i]; i++) name[i] = toupper(name[i]); // convert to upper case
555
 
534
 
556
                                                if(strncmp(name, "POINT", 5) == 0)
535
                                                if(strncmp(name, "POINT", 5) == 0)
557
                                                {
536
                                                {
558
                                                        IsPointSection = (u8)atoi(&name[5]);
537
                                                        IsPointSection = (u8)atoi(&name[5]);
559
                                                        PointCount++;
538
                                                        PointCount++;
560
                                                }
539
                                                }
561
                                                else if(strcmp(name, "GENERAL") == 0)
540
                                                else if(strcmp(name, "GENERAL") == 0)
562
                                                {
541
                                                {
563
                                                        IsGeneralSection = 1;
542
                                                        IsGeneralSection = 1;
564
                                                }
543
                                                }
565
                                                else
544
                                                else
566
                                                {
545
                                                {
567
                                                        UART1_PutString("Unknown section: ");
546
                                                        UART1_PutString("Unknown section: ");
568
                                                        UART1_PutString(name);
547
                                                        UART1_PutString(name);
569
                                                        UART1_PutString("\r\n");
548
                                                        UART1_PutString("\r\n");
570
                                                }
549
                                                }
571
                                        }
550
                                        }
572
                                } // EOF section line
551
                                } // EOF section line
573
                                else
552
                                else
574
                                {       // look for key entrys of each sections
553
                                {       // look for key entrys of each sections
575
                                        name  = strtok(wpline, "="); // get name
554
                                        name  = strtok(wpline, "="); // get name
576
                                        value = strtok(NULL, "="); // get value
555
                                        value = strtok(NULL, "="); // get value
577
                                        if ((name != NULL) && (value != NULL))
556
                                        if ((name != NULL) && (value != NULL))
578
                                        {
557
                                        {
579
                                                for(i=0; name[i]; i++) name[i] = toupper(name[i]); // convert to upper case
558
                                                for(i=0; name[i]; i++) name[i] = toupper(name[i]); // convert to upper case
580
                                                if(IsPointSection &&  (IsPointSection <= WPNumber))
559
                                                if(IsPointSection &&  (IsPointSection <= WPNumber))
581
                                                {
560
                                                {
582
                                                        if(strcmp(name, "LATITUDE") == 0)
561
                                                        if(strcmp(name, "LATITUDE") == 0)
583
                                                        {
562
                                                        {
584
                                                                PointList[IsPointSection-1].Position.Latitude = (s32)(atof(value) * 1E7);
563
                                                                PointList[IsPointSection-1].Position.Latitude = (s32)(atof(value) * 1E7);
585
                                                        }
564
                                                        }
586
                                                        else if(strcmp(name, "LONGITUDE") == 0)
565
                                                        else if(strcmp(name, "LONGITUDE") == 0)
587
                                                        {
566
                                                        {
588
                                                                PointList[IsPointSection-1].Position.Longitude = (s32)(atof(value) * 1E7);
567
                                                                PointList[IsPointSection-1].Position.Longitude = (s32)(atof(value) * 1E7);
589
                                                        }
568
                                                        }
590
                                                        else if(strcmp(name, "RADIUS") == 0)
569
                                                        else if(strcmp(name, "RADIUS") == 0)
591
                                                        {
570
                                                        {
592
                                                                PointList[IsPointSection-1].ToleranceRadius = (u8)atoi(value);
571
                                                                PointList[IsPointSection-1].ToleranceRadius = (u8)atoi(value);
593
                                                        }
572
                                                        }
594
                                                        else if(strcmp(name, "ALTITUDE") == 0)
573
                                                        else if(strcmp(name, "ALTITUDE") == 0)
595
                                                        {
574
                                                        {
596
                                                                PointList[IsPointSection-1].Position.Altitude = (s32)(atof(value) * 100.0);      // in cm
575
                                                                PointList[IsPointSection-1].Position.Altitude = (s32)(atof(value) * 100.0);      // in cm
597
                                                                PointList[IsPointSection-1].Position.Status = NEWDATA;
576
                                                                PointList[IsPointSection-1].Position.Status = NEWDATA;
598
                                                        }
577
                                                        }
599
                                                        else if(strcmp(name, "CLIMBRATE") == 0)
578
                                                        else if(strcmp(name, "CLIMBRATE") == 0)
600
                                                        {
579
                                                        {
601
                                                                PointList[IsPointSection-1].AltitudeRate = (u8)atoi(value);
580
                                                                PointList[IsPointSection-1].AltitudeRate = (u8)atoi(value);
602
                                                        }
581
                                                        }
603
                                                        else if(strcmp(name, "DELAYTIME") == 0)
582
                                                        else if(strcmp(name, "DELAYTIME") == 0)
604
                                                        {
583
                                                        {
605
                                                                PointList[IsPointSection-1].HoldTime = (u8)atoi(value);
584
                                                                PointList[IsPointSection-1].HoldTime = (u8)atoi(value);
606
                                                        }
585
                                                        }
607
                                                        else if(strcmp(name, "WP_EVENT_CHANNEL_VALUE") == 0)
586
                                                        else if(strcmp(name, "WP_EVENT_CHANNEL_VALUE") == 0)
608
                                                        {
587
                                                        {
609
                                                                PointList[IsPointSection-1].WP_EventChannelValue = (u8)atoi(value);
588
                                                                PointList[IsPointSection-1].WP_EventChannelValue = (u8)atoi(value);
610
                                                        }
589
                                                        }
611
                                                        else if(strcmp(name, "HEADING") == 0)
590
                                                        else if(strcmp(name, "HEADING") == 0)
612
                                                        {
591
                                                        {
613
                                                                PointList[IsPointSection-1].Heading = (s16)atoi(value);
592
                                                                PointList[IsPointSection-1].Heading = (s16)atoi(value);
614
                                                        }
593
                                                        }
615
                                                        else if(strcmp(name, "SPEED") == 0)
594
                                                        else if(strcmp(name, "SPEED") == 0)
616
                                                        {
595
                                                        {
617
                                                                PointList[IsPointSection-1].Speed = (u8)atoi(value);
596
                                                                PointList[IsPointSection-1].Speed = (u8)atoi(value);
618
                                                        }
597
                                                        }
619
                                                        else if(strcmp(name, "CAM-NICK") == 0)
598
                                                        else if(strcmp(name, "CAM-NICK") == 0)
620
                                                        {
599
                                                        {
621
                                                                PointList[IsPointSection-1].CamAngle = (u8)atoi(value);
600
                                                                PointList[IsPointSection-1].CamAngle = (u8)atoi(value);
622
                                                        }
601
                                                        }
623
                                                        else if(strcmp(name, "TYPE") == 0)
602
                                                        else if(strcmp(name, "TYPE") == 0)
624
                                                        {
603
                                                        {
625
                                                                PointList[IsPointSection-1].Type = (u8)atoi(value);
604
                                                                PointList[IsPointSection-1].Type = (u8)atoi(value);
626
                                                                if(PointList[IsPointSection-1].Type > 0) PointList[IsPointSection-1].Type--;  // index shift
605
                                                                if(PointList[IsPointSection-1].Type > 0) PointList[IsPointSection-1].Type--;  // index shift
627
                                                                else PointList[IsPointSection-1].Type = POINT_TYPE_INVALID;
606
                                                                else PointList[IsPointSection-1].Type = POINT_TYPE_INVALID;
628
                                                       
607
                                                       
629
                                                                switch(PointList[IsPointSection-1].Type)
608
                                                                switch(PointList[IsPointSection-1].Type)
630
                                                                {
609
                                                                {
631
                                                                        case POINT_TYPE_WP:
610
                                                                        case POINT_TYPE_WP:
632
                                                                                // this works only if altitude key is set before point type key in WPL file     !!
611
                                                                                // this works only if altitude key is set before point type key in WPL file     !!
633
                                                                                PointList[IsPointSection-1].Position.Altitude /= 10; // dm only for WPs 
612
                                                                                PointList[IsPointSection-1].Position.Altitude /= 10; // dm only for WPs 
634
                                                                                WPCount++;
613
                                                                                WPCount++;
635
                                                                                break;
614
                                                                                break;
636
 
615
 
637
                                                                        case POINT_TYPE_POI:
616
                                                                        case POINT_TYPE_POI:
638
                                                                                POICount++;
617
                                                                                POICount++;
639
                                                                                break;
618
                                                                                break;
640
                                                                }
619
                                                                }
641
                                                        }
620
                                                        }
642
                                                        else if(strcmp(name, "PREFIX") == 0)
621
                                                        else if(strcmp(name, "PREFIX") == 0)
643
                                                        {
622
                                                        {
644
                                                                strncpy(PointList[IsPointSection-1].Name, value, 4);
623
                                                                strncpy(PointList[IsPointSection-1].Name, value, 4);
645
                                                                PointList[IsPointSection-1].Name[3] = 0; // Terminate string
624
                                                                PointList[IsPointSection-1].Name[3] = 0; // Terminate string
646
                                                        }
625
                                                        }
647
                                                        else
626
                                                        else
648
                                                        {
627
                                                        {
649
                                                                UART1_PutString("Unknown key: ");
628
                                                                UART1_PutString("Unknown key: ");
650
                                                                UART1_PutString(name);
629
                                                                UART1_PutString(name);
651
                                                                UART1_PutString("\r\n");
630
                                                                UART1_PutString("\r\n");
652
                                                        }      
631
                                                        }      
653
                                                } // EOF point section
632
                                                } // EOF point section
654
                                                else if(IsGeneralSection)
633
                                                else if(IsGeneralSection)
655
                                                {
634
                                                {
656
                                                        if(strcmp(name, "NUMBEROFWAYPOINTS") == 0)
635
                                                        if(strcmp(name, "NUMBEROFWAYPOINTS") == 0)
657
                                                        {      
636
                                                        {      
658
                                                                WPNumber = (u8)atoi(value);
637
                                                                WPNumber = (u8)atoi(value);
659
                                                                if(!WPNumber) // no waypoints in file
638
                                                                if(!WPNumber) // no waypoints in file
660
                                                                {
639
                                                                {
661
                                                                        return(WPL_NO_WAYPOINTS); // we are done here   
640
                                                                        return(WPL_NO_WAYPOINTS); // we are done here   
662
                                                                }
641
                                                                }
663
                                                                else if(WPNumber > MAX_LIST_LEN) // number o points larger than ram list
642
                                                                else if(WPNumber > MAX_LIST_LEN) // number o points larger than ram list
664
                                                                {
643
                                                                {
665
                                                                        UART1_PutString("To much points!");
644
                                                                        UART1_PutString("To much points!");
666
                                                                        return(WPL_ERROR);
645
                                                                        return(WPL_ERROR);
667
                                                                }
646
                                                                }
668
                                                        }
647
                                                        }
669
                                                        else if (strcmp(name, "FILEVERSION") == 0)
648
                                                        else if (strcmp(name, "FILEVERSION") == 0)
670
                                                        {
649
                                                        {
671
                                                                if((u8)atoi(value) != WP_FILE_VERSION_COMPATIBLE)
650
                                                                if((u8)atoi(value) != WP_FILE_VERSION_COMPATIBLE)
672
                                                                {
651
                                                                {
673
                                                                        PointList_Clear();
652
                                                                        PointList_Clear();
674
                                                                        UART1_PutString("Bad file version!\r\n");
653
                                                                        UART1_PutString("Bad file version!\r\n");
675
                                                                        return(WPL_ERROR);     
654
                                                                        return(WPL_ERROR);     
676
                                                                }
655
                                                                }
677
                                                        }
656
                                                        }
678
                                                        else if (strcmp(name, "NAME") == 0)
657
                                                        else if (strcmp(name, "NAME") == 0)
679
                                                        {
658
                                                        {
680
                                                                u8 len = strlen(value);
659
                                                                if(listname)
681
                                                                if(value[len-1] == '\r')
-
 
682
                                                                {
660
                                                                {
-
 
661
                                                                        u8 len = strlen(value);
-
 
662
                                                                        if(len)
-
 
663
                                                                        {
-
 
664
                                                                                if(value[len-1] == '\r')
-
 
665
                                                                                {
683
                                                                        value[len-1] = 0;
666
                                                                                        value[len-1] = 0;
684
                                                                        len--;
667
                                                                                        len--;
-
 
668
                                                                                }
-
 
669
                                                                        }
-
 
670
                                                                        if(len > listnamelen) len = listnamelen;
-
 
671
                                                                        if(len)
-
 
672
                                                                        {
-
 
673
                                                                                value[len-1] = 0; // terminate string
-
 
674
                                                                                if(listname) memcpy(listname, value, len);
-
 
675
                                                                        }
-
 
676
                                                                        NewWPL_Name = 1;
685
                                                                }
677
                                                                }
686
                                                                if(len > 11) value[11] = 0;
-
 
687
                                                                else for(;len < 11; len++) value[len] = ' ';
-
 
688
                                                                memcpy(pWPL_Store->Name, value, 12);
-
 
689
                                                                NewWPL_Name = 1;
-
 
690
                                                        }
678
                                                        }
691
                                                        else
679
                                                        else
692
                                                        {
680
                                                        {
693
                                                                UART1_PutString("Unknown key: ");
681
                                                                UART1_PutString("Unknown key: ");
694
                                                                UART1_PutString(name);
682
                                                                UART1_PutString(name);
695
                                                                UART1_PutString("\r\n");
683
                                                                UART1_PutString("\r\n");
696
                                                        }
684
                                                        }
697
                                                } // EOF general section
685
                                                } // EOF general section
698
                                        } // EOF valid key entry
686
                                        } // EOF valid key entry
699
                                } // EOF key entry line
687
                                } // EOF key entry line
700
                        } // valid line
688
                        } // valid line
701
                } // EOF loop over all lines
689
                } // EOF loop over all lines
702
                fclose_(fp);
690
                fclose_(fp);
703
                NaviData.WaypointNumber = WPCount;
691
                NaviData.WaypointNumber = WPCount;
704
                retval = WPL_OK;        
692
                retval = WPL_OK;        
705
                UART1_PutString("ok\r\n");                               
693
                UART1_PutString("ok\r\n");                               
706
        } // EOF if(Fat16_IsValid())
694
        } // EOF if(Fat16_IsValid())
707
        else
695
        else
708
        {
696
        {
709
                UART1_PutString("no file system found!\r\n");
697
                UART1_PutString("no file system found!\r\n");
710
                retval = WPL_NO_SDCARD_FOUND;
698
                retval = WPL_NO_SDCARD_FOUND;
711
        }      
699
        }      
712
        return(retval);
700
        return(retval);
713
}
701
}
-
 
702
 
-
 
703
// load actual point list from SD card
-
 
704
u8 PointList_ReadFromFile(WPL_Store_t * pWPL_Store)
-
 
705
{
-
 
706
        u8 filename[30];       
-
 
707
 
-
 
708
        pWPL_Store->Name[0] = 0; // clear current list name
-
 
709
 
-
 
710
        // user absolute path, i.e. leading /
-
 
711
        if(pWPL_Store->Index == 0) // index 0 looks for a default WPL file in the root
-
 
712
        {
-
 
713
                sprintf(filename, "/default.wpl");     
-
 
714
        }
-
 
715
        else
-
 
716
        {
-
 
717
                sprintf(filename, "/WPL/list_%03d.wpl", pWPL_Store->Index);
-
 
718
        }
-
 
719
        return PointList_Load(filename, pWPL_Store->Name, sizeof(pWPL_Store->Name));
-
 
720
}
-
 
721
 
-
 
722
// save actual point list to SD card
-
 
723
u8 PointList_WriteToFile(WPL_Store_t * pWPL_Store)
-
 
724
{
-
 
725
        u8 filename[30];
-
 
726
       
-
 
727
       
-
 
728
        if(PointCount == 0) return(WPL_NO_WAYPOINTS);
-
 
729
        // user absolute path, i.e. leading /   
-
 
730
        if(pWPL_Store->Index == 0)
-
 
731
        {
-
 
732
                sprintf(filename, "/default.wpl");
-
 
733
        }
-
 
734
        else
-
 
735
        {
-
 
736
                sprintf(filename, "/WPL/list_%03d.wpl", pWPL_Store->Index);
-
 
737
        }
-
 
738
        return PointList_Save(filename, pWPL_Store->Name, pWPL_Store->OverwriteFile);
-
 
739
}
-
 
740
 
-
 
741
 
-
 
742
// save actual gps positiin and heading to file
-
 
743
u8 PointList_SaveSinglePoint(WPL_Store_t * pWPL_Store)
-
 
744
{
-
 
745
        u8 retval = WPL_ERROR;
-
 
746
        u8 filename[30];
-
 
747
        Point_t WP;
-
 
748
 
-
 
749
        if(GPSData.Position.Status != INVALID) return(retval);
-
 
750
 
-
 
751
        // clear current point list
-
 
752
        PointList_Clear();
-
 
753
        // prepare WP at current position
-
 
754
        GPSPos_Copy(&GPSData.Position, &(WP.Position));
-
 
755
        // set heading
-
 
756
        WP.Heading = CompassSetpointCorrected/10;
-
 
757
        if(WP.Heading == 0) WP.Heading = 360;
-
 
758
        WP.ToleranceRadius = 0;
-
 
759
        WP.HoldTime  = 0;
-
 
760
        WP.Index  = 1;
-
 
761
        WP.Type = POINT_TYPE_WP;
-
 
762
        WP.WP_EventChannelValue = 0;
-
 
763
        WP.AltitudeRate = 0;
-
 
764
        WP.Speed = 0;
-
 
765
        WP.CamAngle = 0;
-
 
766
        WP.Name[0] = 'P';
-
 
767
        WP.Name[1] = 0;
-
 
768
        // add this point to wp list
-
 
769
        PointList_SetAt(&WP);
-
 
770
       
-
 
771
        sprintf(filename, "/WPL/point_%03d.wpl", pWPL_Store->Index);
-
 
772
        sprintf(pWPL_Store->Name, "POINT%03d", pWPL_Store->Index);
-
 
773
        retval = PointList_Save(filename, pWPL_Store->Name, 1);
-
 
774
        return(retval);
-
 
775
}
-
 
776
// load target gps posititon and heading from file
-
 
777
u8 PointList_LoadSinglePoint(WPL_Store_t * pWPL_Store)
-
 
778
{
-
 
779
        u8 filename[30];
-
 
780
       
-
 
781
        sprintf(filename, "/WPL/point_%03d.wpl", pWPL_Store->Index);
-
 
782
        pWPL_Store->Name[0] = 0; // clear current list name
-
 
783
        return PointList_Load(filename, pWPL_Store->Name, sizeof(pWPL_Store->Name));
-
 
784
}
-
 
785
 
-
 
786
 
714
 
787
 
715
void ClearWLP_Name(void)
788
void ClearWLP_Name(void)
716
{
789
{
717
 unsigned char i;
790
        u8 i;
718
 for(i=0; i<sizeof(WPL_Store.Name);i++) WPL_Store.Name[i] = 0;
791
        for(i=0; i<sizeof(WPL_Store.Name);i++) WPL_Store.Name[i] = 0;
719
 NewWPL_Name = 1;
792
        NewWPL_Name = 1;
-
 
793
}
720
}
794
 
721
// move actual point list to ref pos., the point in the list marked by index gets the RefPos afterwards
795
// move actual point list to ref pos., the point in the list marked by index gets the RefPos afterwards
722
u8 PointList_Move(u8 RefIndex, GPS_Pos_t* pRefPos, u16 RotationAngle)
796
u8 PointList_Move(u8 RefIndex, GPS_Pos_t* pRefPos, u16 RotationAngle)
723
{
797
{
724
        u8 retval = 0;
798
        u8 retval = 0;
-
 
799
        s32 altitude;
725
        GPS_Pos_t FirstPoint;
800
        GPS_Pos_t OldRefPos;
726
        GPS_Pos_Deviation_t RefDeviation;
801
        GPS_Pos_Deviation_t RefDeviation;
727
         
802
         
728
        // check inputs for plausibility;
803
        // check inputs for plausibility;
729
        if((RefIndex == 0) || (RefIndex > PointCount)) return(retval); 
804
        if((RefIndex == 0) || (RefIndex > PointCount)) return(retval); 
730
        if(pRefPos == NULL) return(retval);
805
        if(pRefPos == NULL) return(retval);
731
        if(pRefPos->Status == INVALID) return(retval);
806
        if(pRefPos->Status == INVALID) return(retval);
732
 
-
 
733
        // try to copy the old reference in point list to a local buffer
807
 
734
        if(GPSPos_Copy(&(PointList[RefIndex-1].Position), &FirstPoint))
808
        if(GPSPos_Copy(&(PointList[RefIndex-1].Position), &OldRefPos)) // backup old reference position
735
        {
809
        {
736
                u8 i;
810
                u8 i;
737
                // for each point position in the list
811
                // iterate the position list
738
                for(i = 0; i < PointCount; i++)
812
                for(i = 0; i < PointCount; i++)
739
                {
813
                {
740
                        retval = 0;
814
                        retval = 0;
741
                        // Save altitude of that point
815
                        // backup altitude of this point
742
                        pRefPos->Altitude = PointList[i].Position.Altitude;
816
                        altitude = PointList[i].Position.Altitude;
743
                    // calculate deviation form old ref, i.e the north and east shift of each point in the list from the reference position
817
                        // calculate deviation form old ref, i.e the north and east shift of each point in the list from the reference position
744
                        if(!GPSPos_Deviation(&(PointList[i].Position), &FirstPoint, &RefDeviation)) break;
818
                        if(!GPSPos_Deviation(&(PointList[i].Position), &OldRefPos, &RefDeviation)) break;
745
                        // copy of the new reference position into this list place
819
                        // copy of the new reference position into this list place
746
                        if(!GPSPos_Copy(pRefPos, &(PointList[i].Position))) break;
820
                        if(!GPSPos_Copy(pRefPos, &(PointList[i].Position))) break;
-
 
821
                        // restore former altitude 
-
 
822
                        PointList[i].Position.Altitude = altitude;
747
                        // move new reference according to the deviation of the old reference
823
                        // move new reference according to the deviation of the old reference
748
                        retval = GPSPos_ShiftCartesian(&(PointList[i].Position), RefDeviation.North, RefDeviation.East);
-
 
749
                        if(!retval) break;             
824
                        if(RotationAngle > 0)
750
                }
825
                        {
751
// ++++++++++++++++++++++++++++++++++++++++++++++
826
                                retval = GPSPos_ShiftGeodetic(&(PointList[i].Position), (RefDeviation.Bearing + 180 + RotationAngle)%360, RefDeviation.Distance );
752
// Now rotate around the reference point
827
                                // Now rotate the heading positions if they are fixed angles
753
// ++++++++++++++++++++++++++++++++++++++++++++++
-
 
754
          if(RotationAngle > 0)
828
                                if(PointList[i].Heading >= 0 && PointList[i].Heading <= 360) PointList[i].Heading = (PointList[i].Heading + RotationAngle) % 360;
755
          {
829
                        }
756
                GPSPos_Copy(&(PointList[RefIndex-1].Position), &FirstPoint); // Rotate around the reference point
-
 
757
                for(i = 0; i < PointCount; i++)
830
                        else // no rotation
758
                {
831
                        {
759
                        retval = 0;
-
 
760
                        // Save altitude of that point
-
 
761
                        pRefPos->Altitude = PointList[i].Position.Altitude;
-
 
762
                    // calculate deviation form old ref, i.e the north and east shift of each point in the list from the reference position
-
 
763
                        if(!GPSPos_Deviation(&(PointList[i].Position), &FirstPoint, &RefDeviation)) break;
-
 
764
                        // copy of the new reference position into this list place
-
 
765
                        if(!GPSPos_Copy(pRefPos, &(PointList[i].Position))) break;
-
 
766
                        // move new reference according to the deviation of the old reference
832
                                // move new reference according to the deviation of the old reference
767
                        retval = GPSPos_ShiftGeodetic(&(PointList[i].Position), (RefDeviation.Bearing + 180 + RotationAngle)%360,RefDeviation.Distance );
833
                                retval = GPSPos_ShiftCartesian(&(PointList[i].Position), RefDeviation.North, RefDeviation.East);
768
                        // Now rotate the heading positions if they are fixed angles
-
 
769
                        if(PointList[i].Heading >= 0 && PointList[i].Heading <= 360) PointList[i].Heading = (PointList[i].Heading + RotationAngle) % 360;
-
 
-
 
834
                        }
770
                        if(!retval) break;             
835
                        if(!retval) break;             
771
                }
836
                }
772
          }
-
 
773
// ++++++++++++++++++++++++++++++++++++++++++++++
-
 
774
        } // else ref pos old not copied!
837
        } // else ref pos old not copied!
775
        if(!retval) PointList_Clear();
838
        if(!retval) PointList_Clear();
776
        return(retval);
839
        return(retval);
777
}
840
}
778
 
841
 
779
 
842
 
780
 
843
 
781
 
844