Subversion Repositories Projects

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
1198 - 1
 
2
/****************************************************************/
3
/*                                                                                                                              */
4
/*                               NG-Video 5,8GHz                                                        */
5
/*                                                                                                                              */
6
/*                              Copyright (C) 2011 - gebad                                              */
7
/*                                                                                                                              */
8
/*  This code is distributed under the GNU Public License               */
9
/*      which can be found at http://www.gnu.org/licenses/gpl.txt       */
10
/*                                                                                                                              */
11
/****************************************************************/
12
 
13
#include "usart.h"
14
#include "math.h"
15
#include <stdio.h>
16
 
17
#include "servo.h"
18
 
19
#define DLEFT   0
20
#define DRIGHT  1
21
#define DEG_TO_RAD       0.0174533// degrees to radians = PI  / 180
22
#define RAD_TO_DEG      57.2957795// radians to degrees = 180 / PI
23
#define AltFaktor       22.5
24
 
25
 
26
typedef struct {
27
  double distance;
28
  double bearing;
29
} geo_t;
30
 
31
geo_t geo;
32
int AngelPan, AngelTilt;
33
float Altitude;
34
uint16_t dDCurrent = 0;
35
uint16_t dUsedCapacity = 0;
36
int32_t time_remaining = 0;
37
uint16_t Ikorr;
38
uint16_t Wkorr;
39
 
40
/**************************************************************/
41
/*                                                                                                                        */
42
/*                      RSSI Tracking                                             */
43
/*                                                                                                                        */
44
/**************************************************************/
45
 
46
void Tracking_RSSI(void)
47
{ uint16_t u0, u1;
48
  static uint8_t direction;
49
  static uint16_t servo1pos;
50
 
51
  u0 = (ADC_Read(RSSI0) * (uint32_t)udbm.korr_1)/UDBM_KORR_FA;
52
  u1 = (ADC_Read(RSSI1) * (uint32_t)udbm.korr_2)/UDBM_KORR_FA;
53
 
54
  if (direction == DLEFT) {
55
    if (u0 < u1) {
56
      if ( servo1pos > 0) --servo1pos;                                  // Servo Endposition?
57
          servoSetPosition(SERVO_PAN, servo1pos);                       // beibehaltene Richtung ==> sofort ausführen
58
        }
59
    else
60
          if (u0 > (u1 + track_hyst)) direction = DRIGHT;       // Richtungwechsel, wenn Hysterese überschritten
61
  }
62
  else {
63
    if (u0 > u1) {
64
      if ( servo1pos < ServoSteps()) ++servo1pos;               // Servo Endposition?
65
          servoSetPosition(SERVO_PAN, servo1pos);                       // beibehaltene Richtung ==> sofort ausführen
66
        }
67
    else
68
          if ((track_hyst + u0) < u1) direction = DLEFT;        // Richtungwechsel, wenn Hysterese überschritten
69
  }
70
}
71
 
72
/**************************************************************/
73
/*                                                                                                                        */
74
/*                      GPS Tracking                                              */
75
/*                                                                                                                        */
76
/**************************************************************/
77
 
78
// Umrechnung Winkel in Servoschritte
79
void servo_track(uint8_t servo_nr, int16_t Angel)
80
{
81
  servoSetPosition(servo_nr, (uint16_t)((uint32_t)Angel * ServoSteps() / 180));
82
}
83
 
84
// Anzeige eines GPS-Wertes auf LCD
85
void Displ_Format_Data(uint8_t x, uint8_t y, char *description, char *data, char measure)
86
{
87
  lcdGotoXY(x, y);
88
  lcdPuts(description);
89
  lcdPuts(data);
90
  if (measure != '\0')
91
    lcdPutc(measure);
92
}
93
 
94
void Displ_Val_comma(int32_t zahl)
95
{
96
  if (zahl < 0)
97
    lcdPuts(my_itoa(zahl / 10, 1, 2, 0, 0));    // Übergabe Integer, vorher *10 für eventuelle Kommastelle
98
  else
99
    if (zahl < 100)
100
      lcdPuts(my_itoa(zahl, 0, 3, 1, 1));
101
    else
102
          lcdPuts(my_itoa(zahl / 10, 0, 3, 0, 0));
103
  lcdPutc('m');
104
}
105
 
106
// Datenempfang vom MK ==> blinkt Antennensymbol
107
void Displ_wiRX(void)
108
{ static uint8_t timer = 0;
109
  char Sats;
110
 
111
  if ((bat_low != 0) && (pmenu[0] == '\0')) { // nicht im Menü und nicht bei Akku leer blinken
112
    lcdGotoXY(1, 0);
113
    if (wi232RX) {                                              // Datensatz vom MK empfangen?
114
          if (timer < BLINK_PERIOD/2)
115
            lcdPutc(5);
116
          else {
117
            Sats = MK.navi_data.SatsInUse;      // aktuell empfangene Satellitenanzahl
118
                if (MK.navi_data.SatsInUse > 9)
119
                  Sats = 'X';                                   // Anzeige einstellig, da kein Platz auf Display
120
                else
121
                  Sats = MK.navi_data.SatsInUse + '0'; // Umrechnung Char
122
                lcdPutc(Sats);
123
          }
124
        }
125
        else
126
      lcdPutc(':');
127
    wi232RX = 0;
128
        if (++timer == BLINK_PERIOD) timer = 0;
129
  }
130
}
131
 
132
// Berechnung von Distanz und Winkels aus GPS-Daten home(MK eingeschaltet) 
133
// zur aktuellen Position(nach Motorstart)
134
geo_t calc_geo(HomePos_t home, GPS_Pos_t pos)
135
{ double lat1, lon1, lat2, lon2, d1, dlat;
136
  geo_t geo;
137
 
138
  lon1 = home.Home_Lon;
139
  lat1 = home.Home_Lat;
140
  lon2 = (double)pos.Longitude / 10000000;
141
  lat2 = (double)pos.Latitude  / 10000000;
142
 
143
  // Formel verwendet von http://www.kompf.de/gps/distcalc.html
144
  // 111.3 km = Abstand zweier Breitenkreise und/oder zweier Längenkreise am Äquator
145
  // es wird jedoch in Meter weiter gerechnet
146
  d1   = 111300 * (double)cos((double)(lat1 + lat2) / 2 * DEG_TO_RAD) * (lon1 - lon2);
147
  dlat = 111300 * (double)(lat1 - lat2);
148
  // returns a value in metres http://www.kompf.de/gps/distcalc.html
149
  geo.bearing = fmod((RAD_TO_DEG * (double)atan2(d1, dlat)) + 180, 360); // +180 besserer Vergleich mit MkCockpit
150
  if (geo.bearing > 360) geo.bearing -= 360; // bekam schon Werte über 400
151
  geo.distance = sqrt(d1 * d1 + dlat * dlat);  
152
  return(geo);
153
}
154
 
155
void store_LipoData(void)
156
{ uint32_t tmp_time_on;
157
 
158
  tmp_time_on = eeprom_read_dword(&ep_lipo[akku_nr].time_on);
159
  mk_timer = 0;
160
  if (tmp_time_on != lipo.time_on)  {
161
        if (!MK_Motor_run) lipo.UsedCapacity = dUsedCapacity;
162
        eeprom_write_byte(&ep_lipo[akku_nr].Umk, lipo.Umk);
163
        eeprom_write_word(&ep_lipo[akku_nr].UsedCapacity, lipo.UsedCapacity);
164
        eeprom_write_dword(&ep_lipo[akku_nr].time_on, lipo.time_on);
165
        eeprom_write_block(&current,&ep_current,sizeof(current_t));
166
        Double_Beep(DBEEPWR, DBEEPWRP);
167
  }
168
}
169
 
170
#define I_MOTOR_OFF 6 // IR=280mA gemessen; IR vom MK Akku voll 500mA; I Motoren an 1,3A; Grenzwert für Erkennung Akkuwechsel zwischen 500 und 1200mA
171
void Misc(void)
172
{
173
  // Spannung, Strom, Leistung sofort (ohne Motorstart) lesen
174
  if ((!MK_Motor_run) && ((lipo.Umk + 2) < MK.navi_data.UBat) && (MK.navi_data.Current <= I_MOTOR_OFF)) {       // Bei Ruhestrom kleiner 501mA Akku gewechselt? IR=280mA gemessen
175
        lipo.Umk = MK.navi_data.UBat;
176
        mk_timer = 0;
177
        dUsedCapacity = 0;
178
        lipo.UsedCapacity = 0;
179
        lipo.time_on = 0;
180
        lcdClear();
181
        // Funktion wird innhalb Task ausgeführt, auch innerhalb eines anderen Menüpunkes oder Eingabeaufforderung
182
        strcpy(tmp_pmenu, pmenu);       // da bei Change_Value(...) ein Menüpunkt zurück
183
        strcpy(pmenu,"0");
184
        Menu_MK_BatteryChangeNr();      // eingeschobenes Menü, danach aber wieder zur ursprünglichen Anzeige!
185
        strcpy(pmenu, tmp_pmenu);       // Sonst müsste vorherige aktuelle LCD-Anzeige zwischengespeichert werden.
186
        /* irgendeine Eingabe-While-Schleife (Menue, Change..) zum Verlassen zwingen und mit Jump_Menu(pmenu)
187
           den gleichen Menüpunkt wieder aufrufen */
188
        if (pmenu[0] == '\0')
189
          Displ_Main_Disp();
190
        else
191
          exit_while = 1;                               // wird nach den möglichen while, innerhalb der Fkt. wieder auf 0 gesetzt
192
        store_LipoData();
193
  }
194
  mk_timer = 1; // MK-Timer auf on stellen
195
  if ((!MK_Motor_run) && (MK.navi_data.Current <= I_MOTOR_OFF) && (MK.navi_data.UBat > 0) && (MK.navi_data.UBat < lipo.Umk))
196
    lipo.Umk = MK.navi_data.UBat;       // Bei Ruhestrom, da je nach Last UBat schwankend
197
/*  if (current.Sum > 0xfffffff) { // wesentlich größerer Zeitraum für Mittelwertbildung
198
    current.Sum /= 2;
199
        current.Count /= 2;
200
  } */
201
  if (current.Count > 18000) {                          // ungefär 30 Minuten Mittelwert
202
    current.Sum = (current.Sum * 9)/10; // um 10% verkleinern
203
        current.Count = ((uint32_t)current.Count * 9)/10;
204
  }
205
  Ikorr = mk_i_offset + (uint32_t)(MK.navi_data.Current - MK_I_OFFSET_5) * mk_i_faktor / 100;
206
  current.Sum += Ikorr;
207
  current.Count++;
208
  dDCurrent = current.Sum / current.Count;
209
 
210
  Wkorr = (uint32_t)MK.navi_data.UsedCapacity * mk_w_faktor / 100;
211
  dUsedCapacity = lipo.UsedCapacity + Wkorr;
212
  time_remaining = ((int32_t)lipo.Capacity - dUsedCapacity) * 36 / dDCurrent;
213
  if (abs(time_remaining) > M59S59) {
214
    if (time_remaining < 0)
215
          time_remaining =  -1 * M59S59;
216
    else  
217
          time_remaining = M59S59;                              // M59S59 sind 59 Minuten und 59 Sekunden
218
  }
219
}
220
 
221
// MK OSD-Daten lesen und verifizieren
222
uint8_t OSD_Data_valid(void)
223
{ uint8_t ret = 0;
224
  char *tx_osd = {"#co?]==EH\r"};
225
//  char interval[2] = {10, '\0'};
226
 
227
  if (rx_line_decode('O')) {                            // OSD-Datensatz prüfen/dekodieren
228
        if (rx_timeout < RX_TIME_OLD) {                 // GPS-Daten nicht zu alt und ok.
229
      if (MK.navi_data.NCFlags & NC_FLAG_GPS_OK) {
230
            ret = 1;
231
            MK_Motor_run = MK.navi_data.FCStatusFlags & FC_FLAG_MOTOR_RUN;
232
          }
233
          Misc();
234
    }
235
  }
236
  else
237
        if (rx_timeout == RX_TIME_END)                  // ca. 4 Sekunden nach Signalverlust
238
          store_LipoData();
239
  // ca. 210ms keine OSD-Daten empfangen ==> sende neue Anforderung an MK
240
//  if ((track_tx) && (rx_timeout > RX_TIMEOUT)) tx_Mk(NC_ADDRESS, 'o', interval, 1); //  420 * 0.5ms interval
241
  if ((track_tx) && (rx_timeout > RX_TIMEOUT)) USART_send_Str(tx_osd); // 420 * 0.5ms interval
242
  return(ret);
243
}
244
 
245
// MK eingeschaltet und GPS-ok, danach Motoren gestartet ==> Berechnung horizontaler/vertikaler Servowinkel
246
// Hauptprogramm von GPS Antennen-Nachführung
247
void Tracking_GPS(void)
248
{ static uint8_t track_running = 0;
249
 
250
  if (!track_running) {
251
    track_running = 1;  // verhindert doppelten Aufruf, wenn in Eingabeschleife Menu_MK_BatteryChangeNr()
252
    if (OSD_Data_valid()) {
253
          if (coldstart) {
254
            // erst nach Neustart NGVideo und beim Motorstart werden Daten vom MK übernommen
255
            if (MK.navi_data.FCStatusFlags & FC_FLAG_MOTOR_START) {
256
              // doppelt um nur einmal nach Start zu rechnen und Anzeige ohne! Minimalistic printf version
257
                  MK_pos.Home_Lon         = (double)MK.navi_data.HomePosition.Longitude / 10000000;
258
              MK_pos.Home_Lat     = (double)MK.navi_data.HomePosition.Latitude  / 10000000;
259
              MK_pos.Home_Lon_7           = MK.navi_data.HomePosition.Longitude;
260
              MK_pos.Home_Lat_7           = MK.navi_data.HomePosition.Latitude;
261
              MK_pos.Home_Alt     = MK.navi_data.HomePosition.Altitude;
262
              MK_pos.direction = MK.navi_data.CompassHeading;
263
              coldstart = 0;
264
            }
265
          }
266
          else {
267
            geo = calc_geo(MK_pos, MK.navi_data.CurrentPosition);
268
            // aus MkCockpit http://forum.mikrokopter.de/topic-post216136.html#post216136
269
            // (4 * (........))/5 ==> Wichtung Luftdruck-Höhe zu GPS
270
            Altitude = (4 * (MK.navi_data.Altimeter/AltFaktor) + ((float)(MK.navi_data.CurrentPosition.Altitude - MK_pos.Home_Alt) / 1000)) /5;
271
            AngelTilt = RAD_TO_DEG * atan2(Altitude, geo.distance);
272
            if (geo.distance < 4) {     // Abstand MK- zu Antennenposition < 4m ==> Pan-Servo in Mittelstellung
273
              geo.bearing = MK_pos.direction;
274
                  if (Altitude < 3)     AngelTilt = 0; // wenn (Altitude <= 0) ist AngelTilt sowieso 0
275
            }
276
            // egal wo der Übergangspunkt 359, 360, 1grd ist => Winkelübergangspunkt auf 0 bzw. 180grd des Servos bringen
277
            // 360 grd negative Winkelwerte als positive
278
            AngelPan = fmod(geo.bearing - MK_pos.direction + 360 + 90, 360);
279
        if (AngelPan > 360) AngelPan -= 360;
280
 
281
            if (AngelTilt < 0) AngelTilt = 0;
282
            if (AngelTilt > 180) AngelTilt = 180;
283
 
284
            if (AngelPan >= 180) {              // zwecks 360grd-Abdeckung flipt Pan-/Tilt-Servo
285
              AngelPan = AngelPan - 180;
286
                  AngelTilt = 180 - AngelTilt;
287
            }
288
 
289
        servo_track(0, AngelPan);
290
            servo_track(1, AngelTilt);      
291
          }
292
    }
293
    if ((gps_display > GPS_DISP_NONE) && ((!coldstart) || (gps_display == GPS_MISC)) && (bat_low != 0)) {
294
      if ((gps_disp_clear)  && (!coldstart)){
295
            gps_disp_clear = 0;
296
            lcdClear();
297
          }
298
          // makefile ohne! Minimalistic printf version
299
          switch(gps_display) {
300
            case GPS_DISP_CALC:
301
              Displ_Format_Data(0, 0, "Dir:", my_itoa(MK_pos.direction, 0, 3, 0, 0), '\xdf'); // '\xdf' entspricht '°'
302
          lcdGotoXY(8,0);
303
                  lcdPuts("Dis:");
304
                  Displ_Val_comma(geo.distance * 10);
305
              Displ_Format_Data(0, 1, "Bea:", my_itoa((uint32_t)(geo.bearing), 0, 3, 0, 0), '\xdf');
306
          lcdGotoXY(8,1);
307
                  lcdPuts("Alt:");
308
                  Displ_Val_comma(Altitude * 10);
309
                  Displ_Format_Data(0, 2, "Pan:", my_itoa(AngelPan, 0, 3, 0, 0), '\xdf');
310
              Displ_Format_Data(8, 2, "Til:", my_itoa(AngelTilt, 0, 3, 0, 0), '\xdf');
311
                  break;
312
            case GPS_DISP_CURRENT:
313
              Displ_Format_Data(0, 0, "aLon:", my_itoa(MK.navi_data.CurrentPosition.Longitude, 0, 11, 7, 7), '\0');
314
              Displ_Format_Data(0, 1, "aLat:", my_itoa(MK.navi_data.CurrentPosition.Latitude, 0, 11, 7, 7), '\0');
315
              Displ_Format_Data(0, 2, "aAlt: ", my_itoa(MK.navi_data.CurrentPosition.Altitude, 1, 11, 3, 1), '\0');
316
                  break;
317
            case GPS_DISP_HOME:
318
              Displ_Format_Data(0, 0, "hLon:", my_itoa(MK_pos.Home_Lon_7, 0, 11, 7, 7), '\0');
319
              Displ_Format_Data(0, 1, "hLat:", my_itoa(MK_pos.Home_Lat_7, 0, 11, 7, 7), '\0');
320
              Displ_Format_Data(0, 2, "hAlt: ", my_itoa(MK_pos.Home_Alt, 1, 11, 3, 1), '\0');
321
                  break;
322
            case GPS_MISC:
323
          Displ_Format_Data(0, 0, "U:", my_itoa(MK.navi_data.UBat, 0, 4, 1, 1), 'V');
324
          Displ_Format_Data(8, 0, "W:", my_itoa(dUsedCapacity, 0, 4, 0, 0), '\0');
325
              Displ_Format_Data(0, 1, "I:", my_itoa(Ikorr, 0, 4, 1, 1), 'A');
326
                  Displ_Format_Data(8, 1, "\xee:", my_itoa(dDCurrent, 0, 4, 1, 1), 'A');       
327
          lcdGotoXY(0,2);
328
                  lcdPuts("\x1a");
329
                  Displ_TimeMS(lipo.time_on / T2SECDIV);
330
          lcdGotoXY(8,2);
331
                  lcdPuts("R");
332
                  Displ_TimeMS(time_remaining);
333
      }
334
    }
335
    Displ_wiRX();
336
    track_running = 0;
337
  }
338
}
339
 
340
/************************************************************************/
341
/*                                                                                                                                              */
342
/*                      MKCockPit Tracking                                                              */
343
/*                                                                                                                                              */
344
/*  http://www.pololu.com/file/download/ssc03a_guide.pdf?file_id=0J37   *                                                                                                                 */
345
/*                                                                                                                                              */
346
/************************************************************************/
347
 
348
void Tracking_MKCockpit(void)
349
{ uint16_t ServoPos;
350
 
351
  if (Get_Pololu_cmd(MK.data_decode, POLOLU_CMD)) {
352
    switch(MK.data_decode[0]) {
353
          // Command 0: Set Parameters(1 data byte)
354
          case 0x00: ;
355
                                break;
356
          // Command 1: Set Speed (1 data byte)
357
          case 0x01: ;
358
                                break;
359
      // Command 4: Set Position,Absollute(2 data bytes) - angepasst für default MKCockPit
360
          case 0x04:ServoPos = MK.data_decode[2];
361
                                ServoPos = ((ServoPos << 7) | MK.data_decode[3]) / 20; // ServoPos * ServoSteps() sonst zu groß
362
 
363
                                if (ServoPos >= 60) ServoPos -= 60; else ServoPos = 0;
364
                                if (ServoPos > 180) ServoPos = 180;
365
                                //servoSetPosition(MK.data_decode[1], (uint16_t)((uint32_t)ServoPos * ServoSteps() / 180));
366
                                servo_track(MK.data_decode[1], ServoPos);
367
        }
368
  }
369
}