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(¤t,&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 | } |