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的状态更新,其中的卫星状态中的卫星数目或是信号强弱的更新这种状态变化是可以漏掉中间的某些同类事件,因为用户只关注卫星最新的分布结果。有可手能帮我分析一下什么问题吗?或是有更好的解决方案? 

附上我的项目源码: 


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值