GPS

自己做一個GPS衛星分佈狀態顯示程序

http://www.iteye.com/topic/1121633


非常喜歡GPSTest這款Android上查看GPS狀態及GPS衛星分佈的軟件,心想自己也仿照做一個,仔細研究了Android提供的接口,是完全可有可能的,目前只實現了固定羅盤上展示GPS衛星的分佈,考慮到將來動態羅盤(自動指北,技術有限,沒實現)。希望大家能幫我改進,以下是GPSTest的一個界面圖 

 
這是我的程序界面 
 
首先建Activity,來監聽GPS狀態變化 
Java代碼  收藏代碼
  1. public class GpsViewActivity extends Activity {  
  2.     private int minTime = 1000;  
  3.     private int minDistance = 0;  
  4.     private static final String TAG = "GpsView";  
  5.   
  6.     private LocationManager locationManager;  
  7.     private SatellitesView satellitesView;  
  8.     private TextView lonlatText;  
  9.     private TextView gpsStatusText;  
  10.   
  11.     @Override  
  12.     protected void onCreate(Bundle savedInstanceState) {  
  13.         super.onCreate(savedInstanceState);  
  14.         setContentView(R.layout.gps_view_activity);  
  15.   
  16.         gpsStatusText = (TextView) findViewById(R.id.gps_status_text);  
  17.         lonlatText = (TextView) findViewById(R.id.lonlat_text);  
  18.         satellitesView = (SatellitesView) findViewById(R.id.satellitesView);  
  19.           
  20.         registerListener();  
  21.   
  22.     }  
  23.     /** 
  24.      * 註冊監聽 
  25.      */  
  26.     private void registerListener() {  
  27.         if (locationManager == null) {  
  28.             locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);  
  29.         }  
  30.         //偵聽位置信息(經緯度變化)  
  31.         locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER,  
  32.                 minTime, minDistance, locationListener);  
  33.         // 偵聽GPS狀態,主要是捕獲到的各個衛星的狀態  
  34.         locationManager.addGpsStatusListener(gpsStatusListener);  
  35.         //TODO:考慮增加監聽傳感器中的方位數據,以使羅盤的北能自動指向真實的北向  
  36.     }  
  37.     /** 
  38.      * 移除監聽 
  39.      */  
  40.     private void unregisterListener() {  
  41.         if (locationManager != null) {  
  42.             locationManager.removeGpsStatusListener(gpsStatusListener);  
  43.             locationManager.removeUpdates(locationListener);  
  44.         }  
  45.     }  
  46.     /** 
  47.      * 座標位置監聽 
  48.      */  
  49.     private LocationListener locationListener = new LocationListener() {  
  50.   
  51.         @Override  
  52.         public void onLocationChanged(Location location) {  
  53.             StringBuffer sb = new StringBuffer();  
  54.             int fmt = Location.FORMAT_DEGREES;  
  55.             sb.append(Location.convert(location.getLongitude(), fmt));  
  56.             sb.append(" ");  
  57.             sb.append(Location.convert(location.getLatitude(), fmt));  
  58.             lonlatText.setText(sb.toString());  
  59.   
  60.         }  
  61.   
  62.         @Override  
  63.         public void onStatusChanged(String provider, int status, Bundle extras) {  
  64.             gpsStatusText.setText("onStatusChanged");  
  65.   
  66.         }  
  67.   
  68.         @Override  
  69.         public void onProviderEnabled(String provider) {  
  70.             gpsStatusText.setText("onProviderEnabled");  
  71.   
  72.         }  
  73.   
  74.         @Override  
  75.         public void onProviderDisabled(String provider) {  
  76.             gpsStatusText.setText("onProviderDisabled");  
  77.   
  78.         }  
  79.   
  80.     };  
  81.       
  82.     /** 
  83.      * Gps狀態監聽 
  84.      */  
  85.     private GpsStatus.Listener gpsStatusListener = new GpsStatus.Listener() {  
  86.         public void onGpsStatusChanged(int event) {  
  87.             GpsStatus gpsStatus = locationManager.getGpsStatus(null);  
  88.             switch (event) {  
  89.             case GpsStatus.GPS_EVENT_FIRST_FIX: {  
  90.                 gpsStatusText.setText("GPS_EVENT_FIRST_FIX");  
  91.                 // 第一次定位時間UTC gps可用  
  92.                 // Log.v(TAG,"GPS is usable");  
  93.                 int i = gpsStatus.getTimeToFirstFix();  
  94.                 break;  
  95.             }  
  96.   
  97.             case GpsStatus.GPS_EVENT_SATELLITE_STATUS: {// 週期的報告衛星狀態  
  98.                 // 得到所有收到的衛星的信息,包括 衛星的高度角、方位角、信噪比、和僞隨機號(及衛星編號)  
  99.                 Iterable<GpsSatellite> satellites = gpsStatus.getSatellites();  
  100.   
  101.                 List<GpsSatellite> satelliteList = new ArrayList<GpsSatellite>();  
  102.   
  103.                 for (GpsSatellite satellite : satellites) {  
  104.                     // 包括 衛星的高度角、方位角、信噪比、和僞隨機號(及衛星編號)  
  105.                     /* 
  106.                      * satellite.getElevation(); //衛星仰角 
  107.                      * satellite.getAzimuth();   //衛星方位角  
  108.                      * satellite.getSnr();       //信噪比 
  109.                      * satellite.getPrn();       //僞隨機數,可以認爲他就是衛星的編號 
  110.                      * satellite.hasAlmanac();   //衛星曆書  
  111.                      * satellite.hasEphemeris(); 
  112.                      * satellite.usedInFix(); 
  113.                      */  
  114.                     satelliteList.add(satellite);  
  115.                 }  
  116.   
  117.                 satellitesView.repaintSatellites(satelliteList);  
  118.                 gpsStatusText.setText("GPS_EVENT_SATELLITE_STATUS:"  
  119.                         + satelliteList.size());  
  120.                 break;  
  121.             }  
  122.   
  123.             case GpsStatus.GPS_EVENT_STARTED: {  
  124.                 gpsStatusText.setText("GPS_EVENT_STARTED");  
  125.                 break;  
  126.             }  
  127.   
  128.             case GpsStatus.GPS_EVENT_STOPPED: {  
  129.                 gpsStatusText.setText("GPS_EVENT_STOPPED");  
  130.                 break;  
  131.             }  
  132.   
  133.             default:  
  134.                 gpsStatusText.setText("GPS_EVENT:" + event);  
  135.                 break;  
  136.             }  
  137.         }  
  138.     };  
  139.   
  140.     @Override  
  141.     protected void onResume() {  
  142.         super.onResume();  
  143.         registerListener();  
  144.     }  
  145.   
  146.     @Override  
  147.     protected void onDestroy() {  
  148.         unregisterListener();  
  149.         super.onDestroy();  
  150.     }  
  151.   
  152. }  


然後,是創建一個類來用於繪製GPS衛星的分佈,這裏我是用繼承的SurfaceView的類來實現的,我這裏模仿了SDK中提供的範例中的LunarLander遊戲的繪製方法,把繪製工作交給一個線程完成,如下,各個繪製函數註釋已經寫得很清楚了,我不再重複了: 
Java代碼  收藏代碼
  1. package ylybbs.study.mygpstest;  
  2.   
  3. import java.util.List;  
  4. import java.util.concurrent.LinkedBlockingQueue;  
  5.   
  6. import android.content.Context;  
  7. import android.content.res.Resources;  
  8. import android.graphics.Bitmap;  
  9. import android.graphics.BitmapFactory;  
  10. import android.graphics.Canvas;  
  11. import android.graphics.Color;  
  12. import android.graphics.Paint;  
  13. import android.graphics.PaintFlagsDrawFilter;  
  14. import android.graphics.Paint.Align;  
  15. import android.location.GpsSatellite;  
  16. import android.view.SurfaceHolder;  
  17.   
  18. public class DrawSatellitesThread extends Thread {  
  19.     // 衛星圖  
  20.     private Bitmap satelliteBitmap;  
  21.     private Bitmap compassBitmap;  
  22.   
  23.     private Paint paint;  
  24.   
  25.     /** Handle to the surface manager object we interact with */  
  26.     private SurfaceHolder surfaceHolder;  
  27.   
  28.     /** Indicate whether the surface has been created & is ready to draw */  
  29.     private boolean isRunning = false;  
  30.     private int cx=0;  
  31.     private int cy=0;  
  32.     private int compassRadius = 434 / 2;  
  33.       
  34.     PaintFlagsDrawFilter pfd = new PaintFlagsDrawFilter(0,  
  35.             Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG);  
  36.       
  37.     public static final LinkedBlockingQueue<List<GpsSatellite>> queue =   
  38.             new LinkedBlockingQueue<List<GpsSatellite>>(60);  
  39.   
  40.     public DrawSatellitesThread(SurfaceHolder surfaceHolder, Context context) {  
  41.         this.surfaceHolder = surfaceHolder;  
  42.         Resources res = context.getResources();  
  43.         // cache handles to our key sprites & other drawables  
  44.         compassBitmap = BitmapFactory.decodeResource(res, R.drawable.compass);  
  45.         compassRadius = compassBitmap.getWidth() / 2;  
  46.   
  47.         satelliteBitmap = BitmapFactory.decodeResource(res,  
  48.                 R.drawable.satellite_mark);  
  49.   
  50.         paint = new Paint();  
  51.         paint.setSubpixelText(true);  
  52.         paint.setAntiAlias(true);  
  53.         paint.setFilterBitmap(true);  
  54.         paint.setColor(Color.RED);  
  55.         paint.setTextSize(24);  
  56.         paint.setTextAlign(Align.CENTER);  
  57.     }  
  58.       
  59.     /* Callback invoked when the surface dimensions change. */  
  60.     public void setSurfaceSize(int width, int height) {  
  61.         synchronized (surfaceHolder) {  
  62.             cx = width / 2;  
  63.             cy = height  / 2;  
  64.         }  
  65.     }  
  66.       
  67.     @Override  
  68.     public void run() {       
  69.         List<GpsSatellite> list=null;  
  70.         Canvas c = null;  
  71.           
  72.         try {  
  73.             c = surfaceHolder.lockCanvas(null);  
  74.             //初始化畫板的中心座標  
  75.             cx = c.getWidth() / 2;  
  76.             cy = c.getWidth()  / 2;  
  77.             synchronized (surfaceHolder) {  
  78.                 doDraw(c,null);  
  79.             }  
  80.         } finally {  
  81.             if (c != null) {  
  82.                 surfaceHolder.unlockCanvasAndPost(c);  
  83.             }  
  84.         }  
  85.         while (isRunning) {  
  86.             try{  
  87.                 list = queue.take();                  
  88.             } catch (InterruptedException e) {  
  89.                 e.printStackTrace();  
  90.             }  
  91.             try {  
  92.                 c = surfaceHolder.lockCanvas(null);  
  93.                 synchronized (surfaceHolder) {  
  94.                     doDraw(c,list);  
  95.                 }  
  96.             } finally {  
  97.                 if (c != null) {  
  98.                     surfaceHolder.unlockCanvasAndPost(c);  
  99.                 }  
  100.             }  
  101.         }  
  102.     }  
  103.   
  104.   
  105.     public void setRunning(boolean b) {  
  106.         isRunning = b;  
  107.     }  
  108.   
  109.     public void repaintSatellites(List<GpsSatellite> list) {  
  110.         synchronized (surfaceHolder) {  
  111.             try {  
  112.                 queue.offer(list);  
  113.             } catch (Exception e) {  
  114.                 e.printStackTrace();  
  115.             }  
  116.         }  
  117.     }  
  118.   
  119.     /** 
  120.      * 繪製背景羅盤 
  121.      * @param canvas 
  122.      * @param cx  羅盤中心點位於畫布上的X座標 
  123.      * @param cy  羅盤中心點位於畫布上的Y座標 
  124.      * @param r   羅盤的半徑 
  125.      */  
  126.     private void drawBackground(Canvas canvas, int cx, int cy, int r) {  
  127.         int x = cx - r;  
  128.         int y = cy - r;  
  129.         canvas.drawBitmap(compassBitmap, x, y, paint);  
  130.     }  
  131.   
  132.     /** 
  133.      * 將角度轉換爲弧度,以用於三角函數的運算 
  134.      *  
  135.      * @param degree 
  136.      * @return 
  137.      */  
  138.     private double degreeToRadian(double degree) {  
  139.         return (degree * Math.PI) / 180.0d;  
  140.     }  
  141.   
  142.     /* 
  143.      * 將SNR的值,轉化爲通用的信號強度級別,主要用於在繪製衛星時,通過顏色來表明它的信號強度,暫時沒用到 
  144.      * SNR is mapped to signal strength [0,1,4-9] COMMENT SNR: >500 >100 >50 >10 
  145.      * >5 >0 bad n/a COMMENT sig: 9 8 7 6 5 4 1 0 COMMENT 
  146.      */  
  147.     private int snrToSignalLevel(float snr) {  
  148.         int level = 0;  
  149.         if (snr >= 0 && snr < 5) {  
  150.             level = 4;  
  151.         } else if (snr >= 5 && snr < 10) {  
  152.             level = 5;  
  153.         } else if (snr >= 10 && snr < 50) {  
  154.             level = 6;  
  155.         } else if (snr >= 50 && snr < 100) {  
  156.             level = 7;  
  157.         } else if (snr >= 100 && snr < 500) {  
  158.             level = 8;  
  159.         } else if (snr >= 500) {  
  160.             level = 9;  
  161.         }  
  162.         return level;  
  163.     }  
  164.       
  165.     /** 
  166.      * 在背景羅盤上繪製衛星 
  167.      * @param canvas 
  168.      * @param satellite 
  169.      * @param cx  中心圓點的X座標 
  170.      * @param cy  中心圓點的Y座標 
  171.      * @param r   羅盤背景的半徑 
  172.      */  
  173.     private void drawSatellite(Canvas canvas,GpsSatellite satellite, int cx, int cy, int r) {  
  174.   
  175.         /** 
  176.          * GPS衛星導航儀通常選用仰角大於5º,小於85º。 因爲當衛星仰角大於85º時,L1波段的電離層折射誤差較大,故規定仰角大於85º時, 
  177.          * 定位無效,不進行數據更新。而衛星仰角越小,則對流層折射誤差越大,故一般選用仰角大於5º的衛星來定位。 
  178.          */  
  179.         //得到仰角  
  180.         float elevation = satellite.getElevation();  
  181.         //通過仰角,計算出這個衛星應該繪製到離圓心多遠的位置,這裏用的是角度的比值  
  182.         double r2 = r * ((90.0f - elevation) / 90.0f);  
  183.           
  184.         /*得到方位角(與正北向也就是Y軸順時針方向的夾角,注意我們通常幾何上的角度 
  185.          * 是與X軸正向的逆時針方向的夾角),在計算X,Y座標的三角函數時,要做轉換 
  186.          */  
  187.         double azimuth = satellite.getAzimuth();  
  188.           
  189.         /* 
  190.          * 轉換成XY座標系中的夾角,方位角是與正北向也就是Y軸順時針方向的夾角, 
  191.          * 注意我們通常幾何上的角度是與X軸正向的逆時針方向的夾角), 
  192.          * 在計算X,Y座標的三角函數時,要做轉換 
  193.          */  
  194.         double radian = degreeToRadian(360-azimuth + 90);  
  195.              
  196.         double x = cx + Math.cos(radian) * r2;  
  197.         double y = cy + Math.sin(radian) * r2;  
  198.           
  199.         //得到衛星圖標的半徑  
  200.         int sr = satelliteBitmap.getWidth() / 2;  
  201.         //以x,y爲中心繪製衛星圖標  
  202.         canvas.drawBitmap(satelliteBitmap, (float) (x - sr), (float) (y - sr),paint);  
  203.         //在衛星圖標的位置上繪出文字(衛星編號及信號強度)  
  204.         int snr=(int)satellite.getSnr();  
  205.         int signLevel=snrToSignalLevel(snr);  //暫時不用  
  206.         String info = String.format("#%s_%s", satellite.getPrn(), snr);  
  207.         canvas.drawText(info, (float) (x), (float) (y), paint);  
  208.   
  209.     }  
  210.   
  211.   
  212.     private void doDraw(Canvas canvas, List<GpsSatellite> satellites) {  
  213.         if (canvas != null) {  
  214.             // 繪製背景羅盤  
  215.             drawBackground(canvas, cx, cy, compassRadius);  
  216.             //繪製衛星分佈  
  217.             if (satellites != null) {  
  218.                 for (GpsSatellite satellite : satellites) {  
  219.                     drawSatellite(canvas,satellite, cx, cy, compassRadius);  
  220.                 }  
  221.             }  
  222.         }  
  223.   
  224.     }  
  225.   
  226. }  


此代碼編譯發佈到設備後,搜到星後,能運行十幾秒鐘正常後,就會掛掉,原因暫時沒分析出來,看繪出的界面應該正確,可能是繪圖過程中狀態的過程可能會有死鎖之類的,關鍵是解決不好狀態更新與繪製過程之間的同步問題,有可能沒繪完上一個畫面,這時GPS狀態又有了新的更新,如果此是繪圖那邊加了鎖,我不知道這邊的listener調用會出現什麼情況。其實GPS的狀態更新,其中的衛星狀態中的衛星數目或是信號強弱的更新這種狀態變化是可以漏掉中間的某些同類事件,因爲用戶只關注衛星最新的分佈結果。有可手能幫我分析一下什麼問題嗎?或是有更好的解決方案? 

附上我的項目源碼: 


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章