GPS4Palm

Source Code Documentation


HandleMessage.c

Go to the documentation of this file.
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 }

Created: Mon, 08 Oct 2007 22:33:16 +0200
Copyright ©2004 M. Prinke