Android Open Source - satstat Gps Snr View






From Project

Back to project page satstat.

License

The source code is released under:

GNU General Public License

If you think the Android project satstat listed in this page is inappropriate, such as containing malicious code/tools or violating the copyright, please email info at java2s dot com, thanks.

Java Source Code

/*
 * Copyright  2013 Michael von Glasow./*ww  w. j  a va  2  s  . co m*/
 * 
 * This file is part of LSRN Tools.
 *
 * LSRN Tools is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * LSRN Tools is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with LSRN Tools.  If not, see <http://www.gnu.org/licenses/>.
 */

package com.vonglasow.michael.satstat.widgets;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.location.GpsSatellite;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;

/**
 * Displays the signal-to-noise ratio of the GPS satellites in a bar chart.
 */
public class GpsSnrView extends View {
  private String TAG = "GpsSnrView";
  
  /**
   * The highest currently supported NMEA ID.
   */
  private final int MAX_NMEA_ID = 235;

  private Iterable<GpsSatellite> mSats;

  private Paint activePaint;
  private Paint inactivePaint;
  private Paint gridPaint;
  private Paint gridPaintStrong;
  private Paint gridPaintNone;

  //FIXME: should be DPI-dependent, this is OK for MDPI
  private int gridStrokeWidth = 2;

  /*
   * Which satellites to draw:
   * 132: GPS
   * 3354: Various SBAS systems (EGNOS, WAAS, SDCM, GAGAN, MSAS)  some IDs still unused
   * 5564: not used (might be assigned to further SBAS systems)
   * 6588: GLONASS
   * 8996: GLONASS (future extensions?)
   * 97192: not used
   * 193195: QZSS
   * 196200: QZSS (future extensions?)
   * 201235: Beidou
   */
  private boolean draw_1_32 = false;
  private boolean draw_33_54 = false;
  private boolean draw_55_64 = false;
  private boolean draw_65_88 = false;
  private boolean draw_89_96 = false;
  private boolean draw_97_192 = false;
  private boolean draw_193_195 = false;
  private boolean draw_196_200 = false;
  private boolean draw_201_235 = false;


  /**
   * @param context
   */
  public GpsSnrView(Context context) {
    super(context);
    doInit();
  }

  /**
   * @param context
   * @param attrs
   */
  public GpsSnrView(Context context, AttributeSet attrs) {
    super(context, attrs);
    doInit();
  }

  /**
   * @param context
   * @param attrs
   * @param defStyle
   */
  public GpsSnrView(Context context, AttributeSet attrs, int defStyle) {
    super(context, attrs, defStyle);
    doInit();
  }

  private void doInit() {
    activePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
    activePaint.setColor(Color.parseColor("#FF33B5E5"));
    activePaint.setStyle(Paint.Style.FILL);

    inactivePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
    inactivePaint.setColor(Color.parseColor("#FFFF4444"));
    inactivePaint.setStyle(Paint.Style.FILL);

    gridPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
    gridPaint.setColor(Color.parseColor("#FF4D4D4D"));
    gridPaint.setStyle(Paint.Style.STROKE);
    gridPaint.setStrokeWidth(gridStrokeWidth);

    gridPaintStrong = new Paint(gridPaint);
    gridPaintStrong.setColor(Color.parseColor("#FFFFFFFF"));

    gridPaintNone = new Paint(gridPaint);
    gridPaintNone.setColor(Color.parseColor("#00000000"));
  }

  /**
   * Draws the grid lines.
   */
  private void drawGrid(Canvas canvas) {
    //don't use Canvas.getWidth() and Canvas.getHeight() here, they may return incorrect values
    int w = getWidth();
    int h = getHeight();

    // left boundary
    canvas.drawLine((float) gridStrokeWidth / 2, 0, (float) gridStrokeWidth / 2, h, gridPaintStrong);
    
    // range boundaries and auxiliary lines (after every 4th satellite)
    for (int nmeaID = 1; nmeaID < MAX_NMEA_ID; nmeaID++) {
      int pos = getGridPos(nmeaID);
      if (pos > 0) {
        float x = (float) gridStrokeWidth / 2
            + pos * (w - gridStrokeWidth) / getNumBars();
        Paint paint = gridPaintNone;
        switch(nmeaID) {
        case 32:
        case 64:
        case 96:
        case 192:
        case 200:
        case 235:
          paint = gridPaintStrong;
          break;
        case 54:
          if (!draw_55_64)
            paint = gridPaintStrong;
          break;
        case 88:
          if (!draw_89_96)
            paint = gridPaintStrong;
          else
            paint = gridPaint;
          break;
        case 195:
          if (!draw_196_200)
            paint = gridPaintStrong;
        default:
          if ((nmeaID % 4) == 0)
            paint = gridPaint;
          break;
        }
        canvas.drawLine(x, 0, x, h, paint);
      }
    }
    
    // right boundary
    canvas.drawLine(w - (float) gridStrokeWidth / 2, h, w - (float) gridStrokeWidth / 2, 0, gridPaintStrong);
    
    // bottom line
    canvas.drawLine(0, h - (float) gridStrokeWidth / 2, w, h - (float) gridStrokeWidth / 2, gridPaintStrong);
  }
  
  /**
   * Draws the SNR bar for a satellite.
   * 
   * @param canvas The {@code Canvas} on which the SNR view will appear.
   * @param nmeaID The NMEA ID of the satellite, as returned by {@link android.location.GpsSatellite#getPrn()}.
   * @param snr The signal-to-noise ratio (SNR) for the satellite.
   * @param used Whether the satellite is used in the fix.
   */
  private void drawSat(Canvas canvas, int nmeaID, float snr, boolean used) {
    int w = getWidth();
    int h = getHeight();

    int i = getGridPos(nmeaID);

    int x0 = (i - 1) * (w - gridStrokeWidth) / getNumBars() + gridStrokeWidth / 2;
    int x1 = i * (w - gridStrokeWidth) / getNumBars() - gridStrokeWidth / 2;

    int y0 = h - gridStrokeWidth;
    int y1 = (int) (y0 * (1 - Math.min(snr, 60) / 60));

    canvas.drawRect(x0, y1, x1, h, used?activePaint:inactivePaint);
  }

  /**
   * Returns the position of the SNR bar for a satellite in the grid.
   * <p>
   * This function returns the position at which the SNR bar for the
   * satellite with the given {@code nmeaID} will appear in the grid, taking
   * into account the visibility of NMEA ID ranges.
   * 
   * @param nmeaID The NMEA ID of the satellite, as returned by {@link android.location.GpsSatellite#getPrn()}.
   * @return The position of the SNR bar in the grid. The position of the first visible bar is 1. If {@code nmeaID} falls within a hidden range, -1 is returned. 
   */
  private int getGridPos(int nmeaID) {
    if (nmeaID < 1) return -1;
    
    int skip = 0;
    if (nmeaID > 32) {
      if (!draw_1_32) skip+=32;
      if (nmeaID > 54) {
        if (!draw_33_54) skip+=22;
        if (nmeaID > 64) {
          if (!draw_55_64) skip+=10;
          if (nmeaID > 88) {
            if (!draw_65_88) skip+=24;
            if (nmeaID > 96) {
              if (!draw_89_96) skip+=8;
              if (nmeaID > 192) {
                if (!draw_97_192) skip+=96;
                if (nmeaID > 195) {
                  if (!draw_193_195) skip+=3;
                  if (nmeaID > 200) {
                    if (nmeaID > MAX_NMEA_ID) return -1;
                    else if (!draw_201_235) return -1;
                    else if (!draw_196_200) skip+=5;
                  } else {
                    // 195 < nmeaID <= 200
                    if (!draw_196_200) return -1;
                  }
                } else {
                  // 192 < nmeaID <= 195
                  if (!draw_193_195) return -1;
                }
              } else {
                // 96 < nmeaID <= 192
                if (!draw_97_192) return -1;
              }
            } else {
              // 88 < nmeaID <= 96
              if (!draw_89_96) return -1;
            }
          } else {
            // 64 < nmeaID <= 88
            if (!draw_65_88) return -1;
          }
        } else {
          // 54 < nmeaID <= 64
          if (!draw_55_64) return -1;
        }
      } else {
        // 32 < nmeaID <= 54
        if (!draw_33_54) return -1;
      }
    } else {
      // nmeaID <= 32
      if (!draw_1_32) return -1;
    }
    
    return nmeaID - skip;
  }
  
  /**
   * Returns the number of SNR bars to draw
   * 
   * The number of bars to draw varies depending on the systems supported by
   * the device. The most common numbers are 32 for a GPS-only receiver or 56
   * for a combined GPS/GLONASS receiver.
   * 
   * @return The number of bars to draw
   */
  private int getNumBars() {
    return (draw_1_32 ? 32 : 0) 
        + (draw_33_54 ? 22 : 0)
        + (draw_55_64 ? 10 : 0)
        + (draw_65_88 ? 24 : 0)
        + (draw_89_96 ? 8 : 0)
        + (draw_97_192 ? 96 : 0)
        + (draw_193_195 ? 3 : 0)
        + (draw_196_200 ? 5 : 0)
        + (draw_201_235 ? 35 : 0);
  }

  /**
   * Initializes the SNR grid.
   * <p>
   * This method iterates through {@link #mSats} to determine which ranges of
   * NMEA IDs will be drawn. 
   */
  protected void initializeGrid() {
    // iterate through list to find out how many bars to draw
    if (mSats != null)
      for (GpsSatellite sat : mSats) {
        int prn = sat.getPrn();
        if (prn < 1) {
          Log.wtf(TAG, String.format("Got satellite with invalid NMEA ID %d", prn));
        } else if (prn <= 32) {
          draw_1_32 = true;
        } else if (prn <= 54) {
          draw_33_54 = true;
        } else if (prn <= 64) {
          // most likely an extended SBAS range, display the lower range, too
          draw_33_54 = true;
          draw_55_64 = true;
        } else if (prn <= 88) {
          draw_65_88 = true;
        } else if (prn <= 96) {
          // most likely an extended GLONASS range, display the lower range, too
          draw_65_88 = true;
          draw_89_96 = true;
        } else if (prn <= 192) {
          draw_97_192 = true; // TODO: do we really want to enable this huge 96-sat block?
          Log.w(TAG, String.format("Got satellite with NMEA ID %d (from the huge unassigned 97-192 range)", prn));
        } else if (prn <= 195) {
          draw_193_195 = true;
        } else if (prn <= 200) {
          // most likely an extended QZSS range, display the lower range, too
          draw_193_195 = true;
          draw_196_200 = true;
        } else if (prn <= 235) {
          draw_201_235 = true;
        } else {
          Log.w(TAG, String.format("Got satellite with NMEA ID %d, possibly unsupported system", prn));
        }
      }
    /*
     * If we didn't get any valid ranges, display at least the GPS range.
     * No need to check for extended ranges here - if they get drawn, so
     * will their corresponding base range.
     */
    if (!(draw_1_32 || draw_33_54 || draw_65_88 || draw_97_192 || draw_193_195 || draw_201_235))
      draw_1_32 = true;
  }
  
  /**
   * Redraws the SNR view.
   * <p>
   * This method is called whenever the view needs to be redrawn. Besides the
   * usual cases of view creation/recreation, this also occurs when the
   * {@link #showSats(Iterable)} has been called to indicate new SNR data is
   * available.
   */
  @Override
  protected void onDraw(Canvas canvas) {
    initializeGrid();
    
    // draw the SNR bars
    if (mSats != null)
      for (GpsSatellite sat : mSats)
        drawSat(canvas, sat.getPrn(), sat.getSnr(), sat.usedInFix());
    
    // draw the grid on top
    drawGrid(canvas);
  }

  @Override
  protected void onMeasure (int widthMeasureSpec, int heightMeasureSpec) {
    int mHeight = (int) Math.min(MeasureSpec.getSize(widthMeasureSpec) * 0.15f, MeasureSpec.getSize(heightMeasureSpec));
    setMeasuredDimension(MeasureSpec.getSize(widthMeasureSpec), mHeight);
  }

  /**
   * Refreshes the SNR view with current data.
   * <p>
   * Call this method when new SNR data is available. It will update the SNR
   * view's internal list of {@code GpsSatellite}s and trigger a redraw.
   * 
   * @param sats A list of satellites currently in view.
   */
  public void showSats(Iterable<GpsSatellite> sats) {
    mSats = sats;
    invalidate();
  }
}




Java Source Code List

com.vonglasow.michael.satstat.AboutActivity.java
com.vonglasow.michael.satstat.GpsEventReceiver.java
com.vonglasow.michael.satstat.MainActivity.java
com.vonglasow.michael.satstat.PasvLocListenerService.java
com.vonglasow.michael.satstat.SettingsActivity.java
com.vonglasow.michael.satstat.WifiCapabilities.java
com.vonglasow.michael.satstat.data.CellTowerCdma.java
com.vonglasow.michael.satstat.data.CellTowerGsm.java
com.vonglasow.michael.satstat.data.CellTowerListCdma.java
com.vonglasow.michael.satstat.data.CellTowerListGsm.java
com.vonglasow.michael.satstat.data.CellTowerListLte.java
com.vonglasow.michael.satstat.data.CellTowerList.java
com.vonglasow.michael.satstat.data.CellTowerLte.java
com.vonglasow.michael.satstat.data.CellTower.java
com.vonglasow.michael.satstat.mapsforge.FileLRUCache.java
com.vonglasow.michael.satstat.mapsforge.ImageFileNameFilter.java
com.vonglasow.michael.satstat.mapsforge.PersistentTileCache.java
com.vonglasow.michael.satstat.widgets.GpsSnrView.java
com.vonglasow.michael.satstat.widgets.GpsStatusView.java
com.vonglasow.michael.satstat.widgets.LocProviderPreference.java
com.vonglasow.michael.satstat.widgets.MapViewPager.java
com.vonglasow.michael.satstat.widgets.NetworkTypePreference.java
com.vonglasow.michael.satstat.widgets.SquareView.java