2009년 10월 29일 목요일

GPGSV,GPGSA를 이용한 위성정보 표시

0. 참고

 NMEA sentence : http://aprs.gids.nl/nmea/

 용어정리 : http://www.cgrer.uiowa.edu/cgrer_lab/gps/gpsdefs.html

 참고프로젝트 : http://www.codeproject.com/KB/mobile/WritingGPSApplications1.aspx

 

1. 목적

GPGSV 와 GPGSA 정보를 이용한 주변의 GPS위성 상태를 알아보는 프로그램 작성

 

2. 개념정리

 2-1. GPGSV

    - satellite view(SV) 정보를 가진다.(GPS가 잡은 위성 정보)

    - GPGSA의 SV영역을 고려하면 최대 12개의 위성으로 부터 정보를 수신

    - 구문분석

       - 예.$GPGSV,3,1,11,03,03,111,00,04,15,270,00,06,01,010,00,13,06,292,00*74

          - 노란색 :

             - 첫번째자리 : GPGSV메세지의 총 갯수, 3이면 한번 수신할때 $GPGSV~ 메세지가 3개 넘어옴

             - 두번째자리 : GPGSV메세지의 순번

             - 세번째자리 : 총 수신한 위성 정보 수

 

          - 주황색 :

             - 4개가 한세트(예에서는 4개의 위성정보가 수신되었음)

             - 첫번째자리 : PRN number

             - 두번째자리 : Elevation in degrees ,최대 90도 ,

                                 0도면 수평 90도면 수직(머리위), 쉽게 달을 볼때 지평면에서 각도를 생각하면 될듯

             - 세번째자리 : Azimuth , 북쪽을 중심으로 한 위성의 위치, 범위 = 0~359

             - 네번째자리 : SNR

 

    - 그럼 이메시지로 무엇이 가능한가?

        - 위성의 위치, 수신상태 확인 가능

 

 2-2. GPGSA

    - active satellite 라고 해야하나....

    -  SV의 ID들과 DOP정보를 가진다.

    - 구문분석 : 생략

    - 정확도를 계산할때 사용할 수 있다.

 

3. 용어정리

 - 3-1. PRN

      - Pseudo-random noise

      - 위성 아이디쯤 된다. 그 밖의 의미를 두기 힘듬

 - 3-2. SNR

      - Signal to Noise Ratio

      - 정보 수신률(높을수록 위성으로부터 정보를 잘 수신함)

      - 0~00 범위이지만, 참고의 글에서 보면, 50을 넘는 경우를 못봤다고도 하고.....

 - 3-3. DOP

      - Dilution of Precision

      - 정보 오차율 정도의 의미를 가진다.

      - PDOP , HDOP , VDOP의 종류가 있고, 각각, percent , horizontal , vertical 의 의미가 있다.

       

4. 구현

 4-1. Satellite.cs

  - 기능 : GPGSV , GPGSA 처리

   1: using System;
   2: using System.Collections.Generic;
   3: using System.Linq;
   4: using System.Text;
   5: using System.Text.RegularExpressions;
   6: using System.Diagnostics;
   9:     public struct SatelliteView{        
  10:         public int PRN{get;set;}
  11:  
  12:         public int Elevation{get;set;}
  13:         public int Azimuth{get;set;}
  14:         public int? SNR{get;set;}
  15:     }
  16:  
  17:     public struct SatelliteDOP {
  18:         public float? Percent { get; set; }
  19:         public float? Horizontal { get; set; }
  20:         public float? Vertical { get; set; }
  21:  
  22:     }
  23:  
  24:     public class Satellite {        
  25:         
  26:         public static List<SatelliteView> GetSatelliteViews(string sentence) {
  27:             List<SatelliteView> result = new List<SatelliteView>();            
  28:             foreach (Match match in Regex.Matches(sentence, @"GPGSV,([0-9]+),([0-9]+),([0-9]+),(.[^*]+)[*]")) {
  29:                 string[] data = match.Groups[4].Value.Split(','); 
  30:                 int svCount = Convert.ToInt32(data.Length / 4);
  31:                 for (int i = 0; i < svCount; i++) {
  32:  
  33:                     SatelliteView entity = new SatelliteView();
  34:                     entity.PRN = Convert.ToInt32(data[i * 4 + 0]);
  35:                     entity.Elevation = Convert.ToInt32(data[i * 4 + 1]);
  36:                     entity.Azimuth = Convert.ToInt32(data[i * 4 + 2]);
  37:                     if (!string.IsNullOrEmpty(data[i * 4 + 3])) entity.SNR = Convert.ToInt32(data[i * 4 + 3]);;
  38:                     result.Add(entity);
  39:                 }
  40:             }            
  41:             return result;
  42:         }
  43:  
  44:         public static SatelliteDOP GetDOP(string sentence) {            
  45:             string expression = @"GPGSA,(.[^*]+)[*]";
  46:             SatelliteDOP result = new SatelliteDOP();
  47:  
  48:             if (!Regex.IsMatch(sentence, expression)) return result;
  49:             Match match = Regex.Match(sentence, expression);
  50:  
  51:             string[] data = match.Groups[1].Value.Split(',');
  52:             if (string.IsNullOrEmpty(data[14])) return result;
  53:             result.Percent = Convert.ToSingle(data[14]);
  54:             result.Horizontal = Convert.ToSingle(data[15]);
  55:             result.Vertical = Convert.ToSingle(data[16]);            
  56:  
  57:             return result;
  58:         }
  59:     }

 

 4-2. ui.cs(winForm class)

   1: void Update(string msg) {
   2:     float radius = this.ImageSize.Width / 2;
   3:     textBox2.Text = "";
   4:     lbDOP.Text = "";
   5:  
   6:     List<SatelliteView> result = Satellite.GetSatelliteViews(msg);
   7:     if (result.Count < 1) {
   8:         textBox2.Text = "no signal";
   9:         return;
  10:     }
  11:  
  12:     this.DrawRectangle();
  13:     foreach (SatelliteView sv in result) {
  14:         textBox2.AppendText(string.Format("prn:{0},elevation:{1},azimuth:{2},snr:{3},distance:{4}\n"
  15:                 , sv.PRN.ToString(), sv.Elevation, sv.Azimuth, sv.SNR
  16:                 , this.GetDistance(radius, sv.Elevation).ToString()
  17:             )
  18:         );
  19:         this.DrawPoint(sv.PRN, this.GetDistance(radius, sv.Elevation), sv.Azimuth, sv.SNR);
  20:     }
  21:  
  22:     SatelliteDOP dop = Satellite.GetDOP(msg);
  23:     this.ShowDOP(dop);        
  24: }
  25:  
  26: void DrawRectangle() {            
  27:     pictureBox1.Image = new Bitmap(this.ImageSize.Width, this.ImageSize.Height);
  28:     pictureBox1.BackColor = Color.WhiteSmoke;
  29:  
  30:     Graphics line = Graphics.FromImage(pictureBox1.Image);
  31:     line.DrawLine(Pens.Black, 0, this.ImageSize.Height / 2, this.ImageSize.Width, this.ImageSize.Height / 2);
  32:     line.DrawLine(Pens.Black, ImageSize.Width / 2, 0, ImageSize.Height / 2, ImageSize.Height);
  33:  
  34:     line.DrawString("N", new Font("arial", 8F), Brushes.Black, this.ImageSize.Width / 2 - 6, 0);
  35:     line.DrawString("E", new Font("arial", 8F), Brushes.Black, this.ImageSize.Width - 12, this.ImageSize.Height / 2 - 6);
  36:     line.DrawString("W", new Font("arial", 8F), Brushes.Black, 0, this.ImageSize.Height / 2 - 6);
  37:     line.DrawString("S", new Font("arial", 8F), Brushes.Black, this.ImageSize.Width / 2 - 6, this.ImageSize.Height - 12);
  38:     
  39: }
  40:  
  41: void DrawPoint(int prn , float distance, int degree , int? snr) {
  42:     double y = 0.0;
  43:     double x = 0.0;        
  44:  
  45:     if (degree <= 90) {
  46:         x += ImageSize.Width / 2;
  47:         y = ImageSize .Height / 2 - Math.Abs(Math.Sin(Math.PI * degree / 180F) * distance);
  48:         x += Math.Abs(Math.Cos(Math.PI * degree / 180F) * distance);                
  49:     }
  50:     if (degree > 90 && degree <= 180) {
  51:         x += ImageSize.Width / 2;
  52:         y += ImageSize.Height / 2;
  53:         degree = degree - 90;
  54:         y += Math.Abs(Math.Sin(Math.PI * degree / 180F) * distance);
  55:         x += Math.Abs(Math.Cos(Math.PI * degree / 180F) * distance);                
  56:     }
  57:     if (degree > 180 && degree <= 270) {
  58:         y += ImageSize.Height / 2;

59: degree = degree - 180;

  60:         y += Math.Abs(Math.Cos(Math.PI * degree / 180F) * distance);
  61:         x = ImageSize.Width / 2 - Math.Abs(Math.Sin(Math.PI * degree / 180F) * distance);                
  62:     }
  63:     if (degree > 270 && degree <= 360) {                
  64:         degree = degree - 270;
  65:         y = ImageSize.Height / 2 - Math.Abs(Math.Sin(Math.PI * degree / 180F) * distance);
  66:         x = ImageSize.Width / 2 - Math.Abs(Math.Cos(Math.PI * degree / 180F) * distance);
  67:     }        
  68:     Graphics circle = Graphics.FromImage(pictureBox1.Image);
  69:     circle.FillEllipse(this.GetSNRColor(snr), Convert.ToInt32(x) - 8, Convert.ToInt32(y) - 8, 16, 16);
  70:     circle.DrawString(prn.ToString(), new Font("arial" , 8F), Brushes.Black, Convert.ToInt32(x)-6, Convert.ToInt32(y)-6);
  71: }
  72:  
  73: Brush GetSNRColor(int? val) {
  74:     if (!val.HasValue) return Brushes.Black;
  75:     if (val < 20) return Brushes.Gray;
  76:     if (val >= 20 && val < 30) return Brushes.Pink;
  77:     if (val >= 30) return Brushes.Red;
  78:  
  79:     return Brushes.Black;
  80: }

4-3. 결과 화면,

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

5. 결론

GPRMC 나 GPGGA를 통해 얻어진 정보의 신뢰가 의심스러울때는,

GPGSV와 GPGSA정보를 보조 정보로 활용하는것이 효과적이다.

 

   

2009년 10월 27일 화요일

TweetSharp 을 이용한 web-application 인증

0. 소스 및 참고

tweetSharp : http://tweetsharp.com/

C# Library : http://apiwiki.twitter.com/Libraries#C/NET

OAuth .net : http://www.voiceoftech.com/swhitley/?p=681

OAuth  : http://oauth.net/

 

1. 목적

C#과 ASP.NET을 이용한 웹어플리케이션을 트위터에 등록하기

 

 

 

 

 

2. 오픈소스 선정

웹어플리케이션을 트위터에 등록할수 있게 해주는,

오픈소스를 찾기위해 트위터에서 제공하는 오픈소스를 모두 사용해 보았다.

그렇듯. 후잡한 것부터 훌륭한것까지 있는데,

정작 필요한기능을 만족하면서 훌륭한 오픈소스를 찾기는 쉽지 않았다.

 

그와중에 쓸만한 물건이라 판단된 TweetSharp 을 사용하기로 했다.

LINQ형태, 유들이 소감=편한다.

 

3. 구현

tweetSharp 개발자 사이트에서 샘플코드를 구할 수 있다.

이를,

토큰 등록페이지,인증확인페이지,메세지 업데이트 페이지에 사용되는

기능을 가지는 클래스로 구성해 보았다.

 

   1: using System;
   2: using System.Collections.Generic;
   3: using System.Linq;
   4: using System.Web;
   5: using Dimebrain.TweetSharp.Core.OAuth;
   6: using Dimebrain.TweetSharp.Extensions;
   7: using Dimebrain.TweetSharp.Fluent;
   8: using Dimebrain.TweetSharp.Model;
   9: using Dimebrain.TweetSharp;
  10: using System.Configuration;
  11:  
  12: public class Twitter {
  13:  
  14:     string consumerKey;
  15:     string consumerSecret;
  16:  
  17:     public Twitter(string consumerKey , string consumerSecret) {
  18:         this.consumerKey = consumerKey;
  19:         this.consumerSecret = consumerSecret;
  20:     }
  21:  
  22:     public bool Update(string msg , string token , string tokenSecret) {
  23:  
  24:         var result = FluentTwitter.CreateRequest()
  25:             .AuthenticateWith(this.consumerKey, this.consumerSecret, token , tokenSecret)
  26:             .Statuses().Update(msg).Request();
  27:  
  28:         return (string.IsNullOrEmpty(result.AsError().ErrorMessage))
  29:             ? true
  30:             : false;            
  31:     }
  32:  
  33:     public void Authenticate(HttpContext context) {
  34:         var authorizeUrl = FluentTwitter.CreateRequest()
  35:                     .Authentication
  36:                     .GetAuthorizationUrl(this.GetRequestToken().Token);
  37:                 
  38:         context.Response.Redirect(authorizeUrl);                    
  39:     }
  40:  
  41:     public bool IsValid(string token, string tokenSecret) {
  42:         var query = FluentTwitter.CreateRequest()
  43:                     .AuthenticateWith(this.consumerKey, this.consumerSecret, token , tokenSecret)
  44:                     .Account().VerifyCredentials().AsXml();
  45:  
  46:         var response = query.Request();
  47:         return (string.IsNullOrEmpty(response.AsError().ErrorMessage))? true : false;                
  48:     }
  49:  
  50:     public OAuthToken GetAccessToken(string requestToken) {
  51:         var accessToken = FluentTwitter.CreateRequest()
  52:             .Authentication.GetAccessToken(this.consumerKey, this.consumerSecret, requestToken);
  53:  
  54:         var response = accessToken.Request();
  55:  
  56:         var result = response.AsToken();
  57:         if (result == null) {
  58:  
  59:             var error = response.AsError();
  60:             if (error != null) {
  61:                 throw new Exception(error.ErrorMessage);
  62:             }
  63:         }
  64:         return result;
  65:     }
  66:  
  67:     OAuthToken GetRequestToken() {
  68:         var requestToken = FluentTwitter.CreateRequest()
  69:             .Authentication.GetRequestToken(this.consumerKey, this.consumerSecret);
  70:  
  71:         var response = requestToken.Request();
  72:  
  73:         var result = response.AsToken();
  74:         if (result == null) {
  75:  
  76:             var error = response.AsError();
  77:             if (error != null) {
  78:                 throw new Exception(error.ErrorMessage);
  79:             }
  80:         }
  81:         return result;
  82:     } 
  83:  
  84:  
  85: }

 

4. consumer Key & Secret 발급

트위터 로그인후, http://twitter.com/apps 페이지로 가서 정보를 입력하면, key와 secret 을

발급받을 수 있다.