00001 /***************************************************************************** 00002 * 00003 * $RCSfile: HandleMessage_8c-source.html,v $ 00004 * 00005 * GPS4Palm NMEA Message Handler function 00006 * 00007 * This program is Copyright (C) 03/2003 Matthias Prinke 00008 * <matthias.prinke@surfeu.de> and covered by GNU's GPL. 00009 * In particular, this program is free software and comes WITHOUT 00010 * ANY WARRANTY. 00011 * 00012 * Changes for Palm TX are Copyright (C) 07/2007 Frank Saurbier 00013 * <frank.saurbier@surfeu.de> and covered by GNU's GPL. 00014 * In particular, this program is free software and comes WITHOUT 00015 * ANY WARRANTY. 00016 * 00017 * $Author: mp $ 00018 * 00019 * $Date: 2007-10-08 20:40:33 $ 00020 * 00021 * $Revision: 1.7.2.1 $ 00022 * 00023 * $Log: HandleMessage_8c-source.html,v $ 00023 * Revision 1.7.2.1 2007-10-08 20:40:33 mp 00023 * updated for gps4palm V0.9.5 beta 00023 * 00024 * Revision 1.22.2.1 2007-10-05 22:20:12 mp 00025 * added Frank's changes (fix SkyviewForm on TX) 00026 * 00027 * Revision 1.23 2007/07/12 13:36:04 fs 00028 * delete sat view only one time 00029 * 00030 * Revision 1.22 2005/05/06 13:36:04 mp 00031 * added High-Density Display support for SkyviewForm 00032 * 00033 * Revision 1.21 2005/04/24 14:05:00 mp 00034 * added High-Density Display support for NavigationForm 00035 * 00036 * Revision 1.20 2005/02/19 15:50:47 mp 00037 * fixed invocation from WayptEditForm 00038 * 00039 * Revision 1.19 2004/12/19 10:07:09 mp 00040 * modified UpdateStatus() 00041 * 00042 * Revision 1.18 2004/12/11 13:45:39 mp 00043 * fixed GPS status display 00044 * 00045 * Revision 1.17 2004/12/10 19:52:40 mp 00046 * replaced DmGet1Resource by DmGetResource 00047 * 00048 * Revision 1.16 2004/12/09 17:34:03 mp 00049 * replaced strings by string resources 00050 * 00051 * Revision 1.15 2004/12/07 18:50:21 mp 00052 * added assignment to gGPSData.time 00053 * 00054 * Revision 1.14 2004/11/24 21:11:11 mp 00055 * moved static function declarations from header to implementation file 00056 * 00057 * Revision 1.13 2004/11/23 17:52:49 mp 00058 * removed unused variables, fixed typo, added braces 00059 * 00060 * Revision 1.12 2004/03/20 13:37:06 mp 00061 * changed Position Form display 00062 * 00063 * Revision 1.11 2004/03/13 15:43:36 mp 00064 * Replaced usage of SetFieldTextFromStr() by SetFieldText(). Moved invocation 00065 * of Set_UTM_Parameters() to AppStart() (GPS.c). Modified message handling. 00066 * 00067 * Revision 1.10 2004/03/11 21:31:55 mp 00068 * Replaced gLon/gLat/gGPSok by equivalent gGPSData members, moved navigation 00069 * pointer display to NavigationForm.c. Changed from CMG to DTK. 00070 * 00071 * Revision 1.9 2004/03/06 15:57:22 mp 00072 * updated calling of cvt_atof() to new prototype 00073 * 00074 * Revision 1.8 2004/02/28 13:23:52 mp 00075 * modified UpdateLatAndLon(): now uses deg_to_dms_str() 00076 * 00077 * Revision 1.7 2004/01/18 19:02:48 mp 00078 * added UTP position display mode 00079 * 00080 * Revision 1.6 2003/12/31 14:59:21 mp 00081 * added comments, cleaned up code 00082 * 00083 * Revision 1.5 2003/12/30 19:47:58 mp 00084 * added unit conversion for PositionForm 00085 * 00086 * Revision 1.4 2003/12/28 18:41:49 mp 00087 * added GPS status flag for MapForm, added mapprefs debug info 00088 * 00089 * Revision 1.3 2003/11/20 20:37:23 mp 00090 * added update on request (STAT_REDRAW) 00091 * 00092 * Revision 1.2 2003/10/20 17:22:32 mp 00093 * changed UpdateLostSatellite to UpdateStatus 00094 * 00095 * Revision 1.1 2003/10/15 19:14:01 mp 00096 * moved code from GPS.c 00097 * 00098 * 00099 * 00100 ****************************************************************************/ 00101 #include <PalmOS.h> 00102 #include <PalmCompatibility.h> 00103 #include "ResourceDefines.h" 00104 #include "MathLib.h" 00105 #include "Sinetab.h" 00106 #include "HandleMessage.h" 00107 #include "GPS.h" 00108 #include "NavigationForm.h" 00109 #include "SkyviewForm.h" 00110 #include "Utils.h" 00111 #include "common.h" 00112 #include "fp.h" 00113 #include "utm.h" 00114 #include "geo.h" 00115 00116 /* Satellite Information */ 00117 typedef struct { 00118 Int16 prn; /* Sat PRN */ 00119 Int16 azi; /* Azimuth, 0..359 deg. */ 00120 Int16 ele; /* Elevation, 0..90 deg. */ 00121 UInt16 pwr; /* SNR, 0..99 dBHz */ 00122 Boolean act; /* Satellite active flag (i.e. used for fix) */ 00123 } SatType; 00124 00125 /* Preferences data structure */ 00126 extern PrefsType gPrefs; 00127 00128 /* GPS Data */ 00129 GPSType gGPSData; 00130 00131 extern UInt32 gLastSuccessfulReception; 00132 extern UInt32 gLastTimeDisplay; 00133 extern UInt32 gNextReadTime; 00134 extern WinHandle gNavigationH; 00135 extern WinHandle gSkyViewH; 00136 extern Boolean gHdFtrSet; /* High-Density Display */ 00137 00138 #ifdef DEBUG_FORM 00139 extern UInt32 gMinReceived; 00140 extern UInt32 gMaxReceived; 00141 extern UInt32 gChkErrs; 00142 #endif 00143 00144 00145 /* Constants for unit conversion and display */ 00146 const struct { 00147 char *unit; 00148 double conv; 00149 } dst_c[] = { 00150 {"km", 1.0}, 00151 {"mi", 1.0/1.609344}, 00152 {"nm", 1.0/1.852} 00153 }; 00154 00155 00156 const struct { 00157 char *unit; 00158 double conv; 00159 } alt_c[] = { 00160 {"m", 1.0}, 00161 {"ft", 1.0/0.3048} 00162 }; 00163 00164 00165 const struct { 00166 char *unit; 00167 double conv; 00168 } spd_c[] = { 00169 {"km/h", 1.852}, 00170 {"m/s" , 1.852/3.6}, 00171 {"mi/h", 1.852/1.609344}, 00172 {"kts", 1.0} 00173 }; 00174 00175 /* Static Functions */ 00176 static Boolean GetField(const Char *buffer, UInt16 n, 00177 Char *result) MSGHANDLER_SECTION; 00178 static void UpdatePosition(Char *message, 00179 double *p_lat, double *p_lon) MSGHANDLER_SECTION; 00180 static void UpdateLatAndLon(Char *message) MSGHANDLER_SECTION; 00181 static Boolean DecodeTimeOfFix(Char *message, Char *s) MSGHANDLER_SECTION; 00182 00183 00184 /***************************************************************************** 00185 * FUNCTION: HandleMessage 00186 * 00187 * DESCRIPTION: Decodes NMEA message, updates global variables (position, GPS 00188 * status and updates display depending on currently selected 00189 * form. 00190 * 00191 * PARAMETERS: message -- pointer to message 00192 * 00193 * RETURNED: updated display flag 00194 ****************************************************************************/ 00195 Boolean HandleMessage(Char *message) 00196 { 00197 Char s[20]; /* string buffer */ 00198 UInt32 now = TimGetTicks(); /* current system time */ 00199 Boolean updatedDisplay = false; /* flag */ 00200 UInt16 form = FrmGetActiveFormID(); /* form ID */ 00201 Char *msg; /* message pointer */ 00202 RectangleType rect; /* rectangle structure */ 00203 UInt8 bat; /* battery status (percent) */ 00204 static int prn[12]; /* PRN if SAT is used for fix, 00205 -1 otherwise */ 00206 float value; /* var. for unit conversion */ 00207 UInt16 i; /* loop index */ 00208 static SatType sat[12]; /* current satellite info */ 00209 static SatType old[12]; /* previous satellite info */ 00210 00211 /* 00212 * Message type: RMC 00213 * 00214 * Data: Date and time of fix 00215 * Latitude and Longitude 00216 * Status 00217 * Speed over ground 00218 * Course made good 00219 * Magnetic variation 00220 * 00221 * Forms: Position, Skyview, Navigation, Map 00222 */ 00223 if ( (msg = StrStr(message, "RMC")) ) { 00224 Int32 x, y; /* screen coordinates */ 00225 Int32 x2, y2; /* for navigation form */ 00226 Int32 r1, r2, r3; /* radii (navigation form) */ 00227 PointType p1; /* point (navigation form) */ 00228 Int32 phi; /* angle for navigation form */ 00229 FlpCompDouble dtrk; /* track as double */ 00230 Int32 trk; /* track as integer */ 00231 static Int32 otrk; /* old track */ 00232 const char compass[] = "N\0E\0S\0W\0"; /* compass directions */ 00233 Boolean evenMultipleOf5Seconds; 00234 00235 gLastSuccessfulReception = now; /* we successfully read */ 00236 00237 /* 00238 * Get Time 00239 */ 00240 if (GetField(msg, 1, s)) { 00241 evenMultipleOf5Seconds = (s[5] == '0' || s[5] == '5'); 00242 00243 /* 00244 * even multiple of five seconds OR it's been at 00245 * least kMaxTicksWithoutTime seconds since a display 00246 * That way, if we lose 11:11:35, we won't have the 00247 * time go from 11:11:30 to 11:11:40. Instead, it'll go 00248 * 11:11:30, 11:11:36, 11:11:40 00249 */ 00250 if (evenMultipleOf5Seconds || 00251 (now - gLastTimeDisplay) > gMaxTicksWithoutTime) { 00252 00253 /* 00254 * Most of the time, we'll be on a multiple of five. 00255 * Thus, we want to read in four and a quarter more seconds. 00256 * Otherwise, we want to read immediately 00257 */ 00258 if (evenMultipleOf5Seconds) { 00259 gNextReadTime = gLastSuccessfulReception + 00260 4*gTicksPerSecond + gTicksPerSecond / 4; 00261 } else { 00262 gNextReadTime = 0; 00263 } 00264 } /* if (evenMultipleOf5Seconds || ...) */ 00265 00266 /* Update Time */ 00267 if ( DecodeTimeOfFix(msg, s) ) { 00268 DateTimeType dt; 00269 00270 if (form == GPSMainForm || form == SkyviewForm || form == NavigationForm) { 00271 /* update the time display */ 00272 SetFieldText(TimeField, s, false, true); 00273 } 00274 00275 /* convert string to date/time struct */ 00276 dt.second = StrAToI(&s[6]); 00277 dt.minute = StrAToI(&s[3]); 00278 dt.hour = StrAToI(&s[0]); 00279 dt.day = StrAToI(&s[9]); 00280 dt.month = StrAToI(&s[12]); 00281 dt.year = 2000 + StrAToI(&s[15]); 00282 00283 /* copy GPS-time to gGPSData */ 00284 gGPSData.time = TimDateTimeToSeconds(&dt); 00285 00286 gLastTimeDisplay = gLastSuccessfulReception; 00287 } 00288 00289 } /* if (GetField(msg, 1, s)) */ 00290 00291 /* 00292 * Get GPS Status 00293 */ 00294 if (GetField(msg, 2, s)) { 00295 if (s[0] == 'V') { 00296 UpdateStatus( STAT_WARNING ); 00297 gGPSData.valid = false; 00298 } else { 00299 UpdateStatus( STAT_OK ); 00300 gGPSData.valid = true; 00301 } 00302 } /* if (GetField(msg, 2, s)) */ 00303 00304 /* 00305 * Convert string to lat/lon double values 00306 */ 00307 if (gGPSData.valid) { 00308 UpdatePosition(msg, &gGPSData.lat, &gGPSData.lon); 00309 } 00310 00311 switch (form) { 00312 00313 case GPSMainForm: 00314 if (gGPSData.valid) { 00315 /* display position */ 00316 UpdateLatAndLon(msg); 00317 00318 if (GetField(msg, 7, s)) { 00319 /* Speed (with unit conversion) */ 00320 value = cvt_atof(s, NULL, NULL); 00321 gGPSData.sog = value; 00322 value *= spd_c[gPrefs.units.spd_unit].conv; 00323 format_number(value, 2, s); 00324 StrCat(s, " "); 00325 StrCat(s, spd_c[gPrefs.units.spd_unit].unit); 00326 00327 SetFieldText(SpeedField, s, false, true); 00328 } 00329 if (GetField(msg, 8, s)) { 00330 /* Course Made Good */ 00331 StrCat(s, "°"); 00332 SetFieldText(CMGField, s, false, true); 00333 } 00334 if (GetField(msg, 10, s) && 00335 GetField(msg, 11, s + StrLen(s))) { 00336 /* Magnetic Variation */ 00337 StrCat(s, "°"); 00338 SetFieldText(MagField, s, false, true); 00339 } 00340 updatedDisplay = true; 00341 } else { 00342 SetFieldText(SpeedField, NULL, false, true); 00343 SetFieldText(CMGField, NULL, false, true); 00344 SetFieldText(MagField, NULL, false, true); 00345 } /* if (gGPSData.valid) */ 00346 00347 /* GPSMainForm */ 00348 break; 00349 00350 case SkyviewForm: 00351 SysBatteryInfo (false /* set */, NULL, NULL, NULL, NULL, NULL, 00352 &bat /* *percentP */); 00353 StrIToA(s, bat); 00354 StrCat(s, "%"); 00355 SetFieldText(BatField, s, false, true); 00356 00357 if (gGPSData.valid) 00358 updatedDisplay = true; 00359 00360 /* SkyviewForm */ 00361 break; 00362 00363 case NavigationForm: 00364 if (GetField(msg, 7, s)) { 00365 /* Speed Over Ground */ 00366 value = cvt_atof(s, NULL, NULL); 00367 gGPSData.sog = value; 00368 } 00369 00370 if (GetField(msg, 8, s)) { 00371 /* get CMG field */ 00372 /* get floating point value from string */ 00373 FlpBufferAToF(&dtrk.fd, s); 00374 gGPSData.cmg = dtrk.d; 00375 00376 /* store "double"-field of union FlpCompDouble in variable trk */ 00377 trk = (Int32)dtrk.d - 90; 00378 00379 if (gHdFtrSet) { 00380 WinPushDrawState(); 00381 WinSetCoordinateSystem(kCoordinatesNative); 00382 } 00383 00384 /* Erase Compass Directions */ 00385 r1 = (gHdFtrSet) ? WinScaleCoord(NAV_R1, false) : NAV_R1; 00386 r2 = (gHdFtrSet) ? WinScaleCoord(NAV_R2, false) : NAV_R2; 00387 r3 = (gHdFtrSet) ? WinScaleCoord(NAV_R3, false) : NAV_R3; 00388 p1.x = (gHdFtrSet) ? WinScaleCoord(NAV_X, false) : NAV_X; 00389 p1.y = (gHdFtrSet) ? WinScaleCoord(NAV_Y, false) : NAV_Y; 00390 for (i = 0, phi = 45; i <= 3; i++, phi += 90) { 00391 Int16 w = FntCharWidth(compass[2*i]); 00392 Int16 h = FntCharHeight(); 00393 00394 x = p1.x + r1 * icos(phi - otrk) / 32768; 00395 y = p1.y + r1 * isin(phi - otrk) / 32768; 00396 x2 = p1.x + r2 * icos(phi - otrk) / 32768; 00397 y2 = p1.y + r2 * isin(phi - otrk) / 32768; 00398 WinEraseLine(x, y, x2, y2); 00399 x = p1.x + r3 * icos(180 - otrk + i*90) / 32768; 00400 y = p1.y + r3 * isin(180 - otrk + i*90) / 32768; 00401 WinEraseChars(&compass[2*i], 1, x - w/2, y - h/2); 00402 } 00403 00404 /* Restore Compass */ 00405 p1.x = NAV_X-NAV_R2; 00406 p1.y = NAV_Y-NAV_R2; 00407 if (gHdFtrSet) { 00408 WinScalePoint(&p1, false); 00409 } 00410 x = y = (gHdFtrSet) ? WinScaleCoord(2 * NAV_R2, false) 00411 : 2 * NAV_R2; 00412 00413 RctSetRectangle(&rect, 0, 0, x /* w */, y /* h */); 00414 00415 WinCopyRectangle( gNavigationH, /* WinHandle srcWin */ 00416 NULL, /* WinHandle dstWin, draw window */ 00417 &rect, /* RectangleType *srcRect */ 00418 p1.x, /* Coord destX */ 00419 p1.y, /* Coord destY */ 00420 winOverlay /* WinDrawOperation mode */); 00421 00422 /* Draw Compass Directions */ 00423 p1.x = (gHdFtrSet) ? WinScaleCoord(NAV_X, false) : NAV_X; 00424 p1.y = (gHdFtrSet) ? WinScaleCoord(NAV_Y, false) : NAV_Y; 00425 for (i = 0, phi = 45; i <= 3; i++, phi += 90) { 00426 Int16 w = FntCharWidth(compass[2*i]); 00427 Int16 h = FntCharHeight(); 00428 00429 x = p1.x + r1 * icos(phi - trk) / 32768; 00430 y = p1.y + r1 * isin(phi - trk) / 32768; 00431 x2 = p1.x + r2 * icos(phi - trk) / 32768; 00432 y2 = p1.y + r2 * isin(phi - trk) / 32768; 00433 WinDrawLine(x, y, x2, y2); 00434 x = p1.x + r3 * icos(180 - trk + i*90) / 32768; 00435 y = p1.y + r3 * isin(180 - trk + i*90) / 32768; 00436 WinDrawChars(&compass[2*i], 1, x - w/2, y - h/2); 00437 } 00438 00439 if (gHdFtrSet) { 00440 WinPopDrawState(); 00441 } 00442 00443 /* save old track */ 00444 otrk = trk; 00445 } 00446 00447 /* NavigationForm: */ 00448 break; 00449 00450 case MapForm: 00451 /* Note: further processing is done in MapFormHandleEvent() */ 00452 00453 /* MapForm */ 00454 break; 00455 00456 #ifdef DEBUG_FORM 00457 case DebugForm: 00458 { 00459 Char tmp[40]; 00460 00461 StrCopy(tmp, "RcvMin: "); 00462 StrPrintF(&tmp[8], "%5lu", gMinReceived); 00463 WinDrawChars(tmp, StrLen(tmp), 0, 15); 00464 00465 StrCopy(tmp, "RcvMax: "); 00466 StrPrintF(&tmp[8], "%5lu", gMaxReceived); 00467 WinDrawChars(tmp, StrLen(tmp), 0, 30); 00468 00469 StrCopy(tmp, "ChkErr: "); 00470 StrPrintF(&tmp[8], "%lu", gChkErrs); 00471 WinDrawChars(tmp, StrLen(tmp), 0, 45); 00472 00473 StrCopy(tmp, "Map Select: "); 00474 StrCat(tmp, 00475 (gPrefs.mapprefs.select == SEL_USER) ? "User" : "Auto"); 00476 WinDrawChars(tmp, StrLen(tmp), 0, 60); 00477 00478 StrCopy(tmp, "Fetch State: "); 00479 if (gPrefs.mapprefs.fetch == FETCH_READY) { 00480 StrCat(tmp, "Ready"); 00481 } else if (gPrefs.mapprefs.fetch == FETCH_START) { 00482 StrCat(tmp, "Start"); 00483 } else { 00484 StrCat(tmp, "Stop "); 00485 } 00486 WinDrawChars(tmp, StrLen(tmp), 0, 75); 00487 } 00488 updatedDisplay = true; 00489 /* DebugForm */ 00490 break; 00491 #endif 00492 } /* switch (form) */ 00493 } /* if (StrStr(message, "RMC")) */ 00494 00495 /* 00496 * Message type: GSV 00497 * 00498 * Data: Number of satellites in view, 00499 * Satellite Nr (PNR), Elevation, Azimuth, Signal/Noise Ratio (SNR) 00500 * (per satellite) 00501 * 00502 * Forms: Skyview 00503 */ 00504 if ((msg = StrStr(message, "$GPGSV")) && (form == SkyviewForm)) { 00505 UInt16 n; /* loop index */ 00506 UInt16 f; /* field index */ 00507 Int32 x, y; /* screen coordinates */ 00508 PointType p1; /* point */ 00509 Coord w; /* object width */ 00510 Coord h; /* object height */ 00511 Int32 r; /* radius (screen) */ 00512 Int32 phi; /* angle (screen) */ 00513 UInt16 nr_sats = 0; /* nr. of sats in view */ 00514 UInt16 curr; /* current GSV message */ 00515 00516 /* Sats in view */ 00517 if (GetField(msg, 3, s)) { 00518 SetFieldText(SatsField, s, false, true); 00519 nr_sats = StrAToI(s); 00520 } 00521 00522 /* clear data in unused entries */ 00523 for (i=nr_sats; i<12; i++) { 00524 sat[i].prn = -1; 00525 } 00526 00527 /* 00528 * decode current sentence 00529 * f: field index within message 00530 * i: sat index 00531 */ 00532 if (GetField(msg, 2, s)) { 00533 curr = StrAToI(s) - 1; 00534 00535 /* first sat data starts at 4th field in a message */ 00536 f = 4; 00537 00538 /* loop all sats in current message (max. 4) */ 00539 for (i=curr*4; (i < nr_sats) && (i < curr*4+4); i++) { 00540 if (GetField(msg, f++, s)) { 00541 sat[i].prn = StrAToI(s); 00542 } 00543 if (GetField(msg, f++, s)) { 00544 sat[i].ele = StrAToI(s); 00545 } 00546 if (GetField(msg, f++, s)) { 00547 sat[i].azi = StrAToI(s); 00548 } 00549 if (GetField(msg, f++, s)) { 00550 sat[i].pwr = StrAToI(s); 00551 } 00552 } 00553 } /* if (GetField(msg, 2, s)) */ 00554 00555 if (gHdFtrSet) { 00556 WinPushDrawState(); 00557 WinSetCoordinateSystem(kCoordinatesNative); 00558 } 00559 00560 00561 /******************************************************************************** 00562 * (fs) delete sat-view one time - now I can see all satellites 00563 *********************************************************************************/ 00564 p1.x = (gHdFtrSet) ? WinScaleCoord(SKY_X-SKY_R2, false) : SKY_X-SKY_R2; 00565 p1.y = (gHdFtrSet) ? WinScaleCoord(SKY_Y-SKY_R2, false) : SKY_Y-SKY_R2; 00566 WinCopyRectangle( gSkyViewH, /* WinHandle srcWin */ 00567 NULL, /* WinHandle dstWin, draw window */ 00568 &rect, /* RectangleType *srcRect */ 00569 p1.x, /* Coord destX */ 00570 p1.y, /* Coord destY */ 00571 winOverlay /* WinDrawOperation mode */); 00572 00573 00574 for (i=0; i<12; i++) { 00575 /* check if current satellite is active (i.e. used for fix) */ 00576 sat[i].act = false; 00577 for (n=0; n<12; n++) { 00578 if (prn[n] == sat[i].prn) { 00579 sat[i].act = true; 00580 break; 00581 } 00582 } 00583 00584 /* erase previous data if it has been valid */ 00585 if (old[i].prn > -1) { 00586 /* PRN */ 00587 StrPrintF(s, "%02d", old[i].prn); 00588 p1.x = (gHdFtrSet) ? WinScaleCoord(9+(i*12), false) : 9+(i*12); 00589 p1.y = (gHdFtrSet) ? WinScaleCoord(SKY_L2+1, false) : SKY_L2+1; 00590 WinEraseChars(s, StrLen(s), p1.x, p1.y); 00591 00592 /* PWR (bar graphs) */ 00593 p1.y = (gHdFtrSet) ? WinScaleCoord(SKY_L2-old[i].pwr/4, false) 00594 : SKY_L2-old[i].pwr/4; 00595 h = (gHdFtrSet) ? WinScaleCoord(old[i].pwr/4, false) : old[i].pwr/4; 00596 if (old[i].act) { 00597 w = (gHdFtrSet) ? WinScaleCoord(8, false) : 8; 00598 RctSetRectangle(&rect, p1.x, p1.y, w, h); 00599 WinEraseRectangle(&rect, 0 /* cornerDiam*/ ); 00600 } else { 00601 p1.x = (gHdFtrSet) ? WinScaleCoord(10+(i*12), false) : 10+(i*12); 00602 w = (gHdFtrSet) ? WinScaleCoord(6, false) : 6; 00603 RctSetRectangle(&rect, p1.x, p1.y, w, (h > 0) ? h-1 : 0 /* h */); 00604 WinEraseRectangleFrame(simpleFrame, &rect); 00605 } 00606 00607 /* Azimut and Elevation (PRNs on vector display) */ 00608 p1.x = (gHdFtrSet) ? WinScaleCoord(SKY_X, false) : SKY_X; 00609 p1.y = (gHdFtrSet) ? WinScaleCoord(SKY_Y, false) : SKY_Y; 00610 phi = old[i].azi - 90; 00611 r = (gHdFtrSet) ? WinScaleCoord((90-old[i].ele)/2, false) 00612 : (90-old[i].ele)/2; 00613 x = p1.x + r * icos(phi) / 32768; 00614 y = p1.y + r * isin(phi) / 32768; 00615 StrPrintF(s, "%02d", old[i].prn); 00616 WinEraseChars(s, StrLen(s), x - FntCharsWidth(s, StrLen(s))/2, 00617 y - FntCharHeight()/2); 00618 w = h = (gHdFtrSet) ? WinScaleCoord(2*SKY_R2, false) : 2*SKY_R2; 00619 RctSetRectangle(&rect, 0, 0, w, h); 00620 00621 } /* if (old[i].prn > -1) */ 00622 00623 /* show new data if valid (sat in view) */ 00624 if (sat[i].prn != -1) { 00625 /* PRN */ 00626 StrPrintF(s, "%02d", sat[i].prn); 00627 p1.x = (gHdFtrSet) ? WinScaleCoord(9+(i*12), false) : 9+(i*12); 00628 p1.y = (gHdFtrSet) ? WinScaleCoord(SKY_L2+1, false) : SKY_L2+1; 00629 WinDrawChars(s, StrLen(s), p1.x, p1.y); 00630 00631 /* PWR (bar graphs) */ 00632 p1.y = (gHdFtrSet) ? WinScaleCoord(SKY_L2-sat[i].pwr/4, false) 00633 : SKY_L2-sat[i].pwr/4; 00634 h = (gHdFtrSet) ? WinScaleCoord(sat[i].pwr/4, false) : sat[i].pwr/4; 00635 /* sat active: draw solid bar / sat inactive: draw empty bar */ 00636 if (sat[i].act) { 00637 w = (gHdFtrSet) ? WinScaleCoord(8, false) : 8; 00638 RctSetRectangle(&rect, p1.x, p1.y, w, h); 00639 WinDrawRectangle(&rect, 0 /* cornerDiam*/ ); 00640 } else { 00641 p1.x = (gHdFtrSet) ? WinScaleCoord(10+(i*12), false) : 10+(i*12); 00642 w = (gHdFtrSet) ? WinScaleCoord(6, false) : 6; 00643 if (h > 0) { 00644 RctSetRectangle(&rect, p1.x, p1.y, w, h-1); 00645 WinDrawRectangleFrame(simpleFrame, &rect); 00646 } 00647 } 00648 00649 /* Azimut and Elevation (PRNs on vector display) */ 00650 p1.x = (gHdFtrSet) ? WinScaleCoord(SKY_X, false) : SKY_X; 00651 p1.y = (gHdFtrSet) ? WinScaleCoord(SKY_Y, false) : SKY_Y; 00652 phi = sat[i].azi - 90; 00653 r = (gHdFtrSet) ? WinScaleCoord((90-sat[i].ele)/2, false) 00654 : (90-sat[i].ele)/2; 00655 x = p1.x + r * icos(phi) / 32768; 00656 y = p1.y + r * isin(phi) / 32768; 00657 StrPrintF(s, "%02d", sat[i].prn); 00658 WinDrawChars(s, StrLen(s), x - FntCharsWidth(s, StrLen(s))/2, 00659 y - FntCharHeight()/2); 00660 00661 } /* if (sat[i].prn != -1) */ 00662 00663 /* save current dataset */ 00664 old[i] = sat[i]; 00665 } /* for (i=0; i<12; i++) */ 00666 00667 /* Base line for S/N bar graph */ 00668 /* get screen dimensions */ 00669 WinGetDisplayExtent(&h, &w); 00670 p1.y = (gHdFtrSet) ? WinScaleCoord(SKY_L2, false) : SKY_L2; 00671 WinDrawLine(0, p1.y, w-1, p1.y); 00672 00673 #ifdef VERBOSE 00674 RctSetRectangle(&rect, 0, 45, w, 48 /* h */); 00675 WinEraseRectangle(&rect, 0); 00676 #endif 00677 00678 if (gHdFtrSet) { 00679 WinPopDrawState(); 00680 } 00681 00682 while ( (msg = StrStr(msg, "$GPGSV")) ) { 00683 #ifdef VERBOSE 00684 WinDrawChars(msg, StrLen(msg), 0, i); 00685 i += 15; 00686 #endif 00687 if ((msg = StrStr(msg, "*")) == NULL) 00688 break; 00689 } 00690 } /* (StrStr(message, "GSV") */ 00691 00692 /* 00693 * Message type: GSA 00694 * 00695 * Data: Type of fix (none/2D/3D) 00696 * Satellites used for fix 00697 * Precision of fix (PDOP/HDOP/VDOP) 00698 * 00699 * Forms: Skyview 00700 */ 00701 if ((msg = StrStr(message, "$GPGSA")) && (form == SkyviewForm)) { 00702 UInt16 i; 00703 00704 /* Type of fix (none/2D/3D) */ 00705 if (GetField(msg, 2, s)) { 00706 if ((s[0] == '2') || (s[0] == '3')) { 00707 StrCat(s, "D"); 00708 } else { 00709 StrCopy(s, "none"); 00710 } 00711 SetFieldText(FixField, s, false, true); 00712 } 00713 00714 /* Satellites used for fix (either PRN or empty field) */ 00715 for (i=0; i<=12;i++) { 00716 if (GetField(msg, 3+i, s)) { 00717 if (s[0] != '\0') { 00718 /* store PRN */ 00719 prn[i] = StrAToI(s); 00720 } else { 00721 /* not used */ 00722 prn[i] = -1; 00723 } 00724 } 00725 } /* for (i=0; i<=12;i++) */ 00726 00727 /* Position DOP */ 00728 if (GetField(msg, 15, s)) { 00729 SetFieldText(PDOPField, s, false, true); 00730 } 00731 00732 /* Horizontal DOP */ 00733 if (GetField(msg, 16, s)) { 00734 SetFieldText(HDOPField, s, false, true); 00735 } 00736 00737 /* Vertical DOP */ 00738 if (GetField(msg, 17, s)) { 00739 SetFieldText(VDOPField, s, false, true); 00740 } 00741 } /* if ((msg = StrStr(message, "$GPGSV")) && (form == SkyviewForm)) */ 00742 00743 /* 00744 * Message type: GGA 00745 * 00746 * Data: Altitude 00747 * 00748 * Forms: Position 00749 */ 00750 if ((msg = StrStr(message, "$GPGGA")) && (form == GPSMainForm)) { 00751 if (gGPSData.valid) { 00752 /* Altitude */ 00753 if (GetField(msg, 9, s)) { 00754 value = cvt_atof(s, NULL, NULL) * alt_c[gPrefs.units.alt_unit].conv; 00755 format_number(value, 1, s); 00756 StrCat(s, " "); 00757 StrCat(s, alt_c[gPrefs.units.alt_unit].unit); 00758 00759 SetFieldText(AltField, s, false, true); 00760 } 00761 } else { 00762 SetFieldText(AltField, NULL, false, true); 00763 } 00764 } 00765 00766 return updatedDisplay; 00767 } 00768 00769 /***************************************************************************** 00770 * FUNCTION: GetField 00771 * 00772 * DESCRIPTION: returns n'th (0-based) comma-delimited field within buffer 00773 * 00774 * PARAMETERS: buffer -- pointer to message buffer 00775 * n -- (0-based field number) 00776 * result -- pointer to result buffer 00777 * 00778 * RETURNED: true if field found, false otherwise 00779 ****************************************************************************/ 00780 static 00781 Boolean GetField(const Char *buffer, UInt16 n, Char *result) 00782 { 00783 int i; /* loop index */ 00784 00785 /* skip n commas */ 00786 for (i = 0; i < n; i++) { 00787 while (*buffer && *buffer != ',') 00788 buffer++; 00789 if (*buffer == '\0') 00790 return false; 00791 buffer++; 00792 } 00793 00794 /* read until end of field (either comma or asterisk) */ 00795 while (*buffer && *buffer != ',' && *buffer != '*') 00796 *result++ = *buffer++; 00797 00798 /* terminate result string */ 00799 *result = '\0'; 00800 00801 /* ok/fail */ 00802 return *buffer == ',' || *buffer == '\0' || *buffer == '*'; 00803 } 00804 00805 00806 /***************************************************************************** 00807 * FUNCTION: DecodeTimeOfFix 00808 * 00809 * DESCRIPTION: Decodes date and time of fix from message 00810 * 00811 * PARAMETERS: message -- pointer to message buffer 00812 * s -- pointer to result buffer 00813 * 00814 * RETURNED: true if field found, false otherwise 00815 ****************************************************************************/ 00816 static 00817 Boolean DecodeTimeOfFix(Char *message, Char *s) 00818 { 00819 Boolean result = false; 00820 00821 /* Get time and date of fix (UTC) */ 00822 if (GetField(message, 1, s) && GetField(message, 9, &s[8])) { 00823 /* change from HHMMSS.DDD to HH:MM:SS */ 00824 s[7] = s[5]; 00825 s[6] = s[4]; 00826 s[5] = ':'; 00827 s[4] = s[3]; 00828 s[3] = s[2]; 00829 s[2] = ':'; 00830 00831 /* change from DDMMYY to DD/MM/YY */ 00832 s[17] = '\0'; 00833 s[16] = s[13]; 00834 s[15] = s[12]; 00835 s[14] = '/'; 00836 s[13] = s[11]; 00837 s[12] = s[10]; 00838 s[11] = '/'; 00839 s[10] = s[9]; 00840 s[ 9] = s[8]; 00841 s[ 8] = ' '; 00842 result = true; 00843 } /* if (Time- & Date-Field available) */ 00844 00845 return result; 00846 } 00847 00848 00849 /***************************************************************************** 00850 * FUNCTION: UpdateLatAndLon 00851 * 00852 * DESCRIPTION: Decodes and displays Latitude and Longitude according to 00853 * user selected format. 00854 * 00855 * PARAMETERS: message -- pointer to message buffer 00856 * 00857 ****************************************************************************/ 00858 static 00859 void UpdateLatAndLon(Char *message) 00860 { 00861 Char s[40]; /* string buffer */ 00862 Char lat_str[20]; /* string buffer */ 00863 Char lon_str[20]; /* string buffer */ 00864 Char *pt_pos; /* decimal point pos. */ 00865 double lat; /* latitude */ 00866 double lon; /* longitude */ 00867 UInt16 i; /* string index */ 00868 Int32 err; /* error code */ 00869 Int32 zone; /* UTM Zone */ 00870 char hemisphere; /* 'N' / 'S' */ 00871 double easting; /* Easting in m */ 00872 double northing; /* Northing in m */ 00873 00874 switch (gPrefs.units.pos_unit) { 00875 case POS_D: 00876 /* Degrees - convert from lat/lon double values */ 00877 if (gGPSData.lat >= 0) { 00878 StrCopy(s, "N"); 00879 format_number(gGPSData.lat, 7, &s[1]); 00880 } else { 00881 StrCopy(s, "S"); 00882 format_number(-gGPSData.lat, 7, &s[1]); 00883 } 00884 StrCat(s, "°"); 00885 SetFieldText(LatitudeField, s, false, true); 00886 00887 if (gGPSData.lon >= 0) { 00888 StrCopy(s, "E"); 00889 format_number(gGPSData.lon, 7, &s[1]); 00890 } else { 00891 StrCopy(s, "W"); 00892 format_number(-gGPSData.lon, 7, &s[1]); 00893 } 00894 StrCat(s, "°"); 00895 SetFieldText(LongitudeField, s, false, true); 00896 break; 00897 00898 case POS_DM: 00899 /* Degrees and Minutes - only a little string patching needed */ 00900 /* 4 is N or S for Lat direction, 3 is lat */ 00901 if (GetField(message, 4, s) && 00902 GetField(message, 3, s + StrLen(s))) { 00903 00904 /* find position of decimal point */ 00905 pt_pos = StrStr(s, "."); 00906 00907 /* insert "°" between degree and integer minutes and "'" at the end */ 00908 for (i = StrLen(s); &s[i]>=pt_pos-2 ; i--) { 00909 s[i+1] = s[i]; 00910 } 00911 s[++i] = '°'; 00912 StrCat(s, "'"); 00913 00914 SetFieldText(LatitudeField, s, false, true); 00915 } 00916 00917 /* 6 is E or W for Lon direction, 5 is lon */ 00918 if (GetField(message, 6, s) && 00919 GetField(message, 5, s + StrLen(s))) { 00920 00921 /* find position of decimal point */ 00922 pt_pos = StrStr(s, "."); 00923 00924 /* insert "°" between degree and integer minutes and "'" at the end */ 00925 for (i = StrLen(s); &s[i]>=pt_pos-2 ; i--) { 00926 s[i+1] = s[i]; 00927 } 00928 s[++i] = '°'; 00929 StrCat(s, "'"); 00930 00931 SetFieldText(LongitudeField, s, false, true); 00932 } 00933 break; 00934 00935 case POS_DMS: 00936 /* Degrees, Minutes, Seconds - convert from lat/lon double values */ 00937 lat = (gGPSData.lat >= 0) ? gGPSData.lat : -gGPSData.lat; 00938 StrCopy(lat_str, (gGPSData.lat >= 0) ? "N" : "S"); 00939 00940 lon = (gGPSData.lon >= 0) ? gGPSData.lon : -gGPSData.lon; 00941 StrCopy(lon_str, (gGPSData.lon >= 0) ? "E" : "W"); 00942 00943 deg_to_dms_str(lat, lon, &lat_str[1], &lon_str[1]); 00944 00945 SetFieldText(LatitudeField, lat_str, false, true); 00946 SetFieldText(LongitudeField, lon_str, false, true); 00947 00948 break; 00949 00950 case POS_UTM: 00951 /* Universal Transverse Mercator - convert from lat/lon double values */ 00952 err = Convert_Geodetic_To_UTM ( 00953 deg2rad(gGPSData.lat) /* Latitude (rad) */, 00954 deg2rad(gGPSData.lon) /* Longitude (rad) */, 00955 &zone /* long *Zone */, 00956 &hemisphere /* Hemisphere */, 00957 &easting /* *Easting */, 00958 &northing /* *Northing */); 00959 00960 if (err == UTM_NO_ERROR) { 00961 /* print zone and belt to string */ 00962 StrPrintF(s, "%ld%c ", zone, Lat_To_UTM_Belt(gGPSData.lat)); 00963 00964 /* print easting to string */ 00965 format_number(easting, 1, &s[StrLen(s)]); 00966 SetFieldText(LatitudeField, s, false, true); 00967 00968 /* print northing to string */ 00969 format_number(northing, 1, s); 00970 SetFieldText(LongitudeField, s, false, true); 00971 00972 00973 } else { 00974 SetFieldText(LatitudeField, "n.a.", false, true); 00975 SetFieldText(LongitudeField, "", false, true); 00976 } 00977 00978 break; 00979 } /* switch (gPrefs.units.pos_unit) */ 00980 } 00981 00982 /***************************************************************************** 00983 * FUNCTION: UpdatePosition 00984 * 00985 * DESCRIPTION: Updates floating point Latitude/Longitude variables 00986 * 00987 * PARAMETERS: message -- pointer to message buffer 00988 * p_lat -- pointer to latitude variable 00989 * p_lon -- pointer to longitude variable 00990 * 00991 ****************************************************************************/ 00992 void UpdatePosition(Char *message, double *p_lat, double *p_lon) 00993 { 00994 Char s[20]; /* string buffer */ 00995 Char tmp[8]; /* temp. string */ 00996 Char *pt_pos; /* decimal point pos. */ 00997 double lat; /* latitude (deg) */ 00998 double lon; /* longitude (deg) */ 00999 FlpCompDouble min; /* minutes */ 01000 01001 /* 4 is N or S for Lat direction, 3 is lat */ 01002 if (GetField(message, 4, s) && 01003 GetField(message, 3, s + StrLen(s))) { 01004 01005 /* find position of decimal point */ 01006 pt_pos = StrStr(s, "."); 01007 01008 /* degrees */ 01009 StrNCopy(tmp, pt_pos - 4, 2); 01010 tmp[2] = '\0'; 01011 lat = (double)StrAToI(tmp); 01012 01013 /* minutes */ 01014 StrCopy(tmp, pt_pos - 2); 01015 01016 /* get floating point value from string */ 01017 FlpBufferAToF(&min.fd, tmp); 01018 01019 lat += (double)min.d/60.0; 01020 01021 01022 lat *= (s[0] == 'S') ? -1 : 1; 01023 01024 *p_lat = lat; 01025 } 01026 01027 01028 /* 6 is E or W for Lon direction, 5 is lon */ 01029 if (GetField(message, 6, s) && 01030 GetField(message, 5, s + StrLen(s))) { 01031 01032 /* find position of decimal point */ 01033 pt_pos = StrStr(s, "."); 01034 01035 /* degrees */ 01036 StrNCopy(tmp, pt_pos - 5, 3); 01037 tmp[3] = '\0'; 01038 lon = (double)StrAToI(tmp); 01039 01040 /* minutes */ 01041 StrCopy(tmp, pt_pos - 2); 01042 01043 /* get floating point value from string */ 01044 FlpBufferAToF(&min.fd, tmp); 01045 01046 lon += (double)min.d/60.0; 01047 01048 lon *= (s[0] == 'W') ? -1 : 1; 01049 01050 *p_lon = lon; 01051 } 01052 } 01053 01054 01055 /***************************************************************************** 01056 * FUNCTION: UpdateStatus 01057 * 01058 * DESCRIPTION: Update Status Fields 01059 * 01060 * PARAMETERS: status 01061 * 01062 * STAT_NO_GPS 01063 * STAT_WARNING 01064 * STAT_OK 01065 * 01066 ****************************************************************************/ 01067 void UpdateStatus(UInt16 status) 01068 { 01069 UInt16 formID = FrmGetActiveFormID(); /* current form ID */ 01070 static UInt16 prevID; /* previous form ID */ 01071 static UInt16 prevStat; /* previous status */ 01072 MemHandle strH = NULL; /* string handle */ 01073 MemPtr strP = NULL; /* string ptr */ 01074 UInt16 str_id; /* string resource ID */ 01075 01076 /* Update Status Field */ 01077 if ( (status != prevStat) || (formID != prevID) || (status == STAT_REDRAW) ) { 01078 01079 if ( status == STAT_REDRAW ) { 01080 status = prevStat; 01081 } 01082 01083 /* get status string ID */ 01084 switch (status) { 01085 01086 case STAT_WARNING: 01087 str_id = GPSWarnStr; 01088 break; 01089 01090 case STAT_OK: 01091 str_id = GPSOkStr; 01092 break; 01093 01094 case STAT_NO_GPS: 01095 str_id = GPSLostStr; 01096 break; 01097 01098 default: 01099 str_id = GPSOkStr; 01100 } 01101 01102 /* get string from resource */ 01103 strH = DmGetResource(strRsc, str_id); 01104 01105 if (strH != NULL) { 01106 /* string handle valid, lock it */ 01107 strP = MemHandleLock(strH); 01108 } 01109 01110 if ( status == STAT_WARNING ) { 01111 01112 switch (formID) { 01113 01114 case GPSMainForm: 01115 SetFieldText(PositionStatusField, strP, false, true); 01116 break; 01117 01118 case SkyviewForm: 01119 SetFieldText(SkyViewStatusField, strP, false, true); 01120 break; 01121 01122 case NavigationForm: 01123 SetFieldText(NavigationStatusField, strP, false, true); 01124 break; 01125 01126 } /* switch (formID) */ 01127 } else if ( status == STAT_OK ){ 01128 01129 switch (formID) { 01130 01131 case GPSMainForm: 01132 SetFieldText(PositionStatusField, strP, false, true); 01133 break; 01134 01135 case SkyviewForm: 01136 SetFieldText(SkyViewStatusField, strP, false, true); 01137 break; 01138 01139 case NavigationForm: 01140 SetFieldText(NavigationStatusField, strP, false, true); 01141 break; 01142 01143 case MapForm: 01144 break; 01145 01146 } /* switch (formID) */ 01147 } else if ( status == STAT_NO_GPS ) { 01148 01149 switch (formID) { 01150 01151 case GPSMainForm: 01152 SetFieldText(PositionStatusField, strP, false, true); 01153 break; 01154 01155 case SkyviewForm: 01156 SetFieldText(SkyViewStatusField, strP, false, true); 01157 break; 01158 01159 case NavigationForm: 01160 SetFieldText(NavigationStatusField, strP, false, true); 01161 break; 01162 01163 } /* switch (formID) */ 01164 } 01165 } 01166 01167 prevID = formID; 01168 prevStat = status; 01169 01170 if (strH != NULL) { 01171 /* unlock string handle */ 01172 MemHandleUnlock(strH); 01173 01174 /* release string resource */ 01175 DmReleaseResource(strH); 01176 } 01177 }