2009년 11월 2일 월요일

NETCF 국제화

Visual Studio 2008에서 자체적으로 생성하는 리소스 파일 형태가 resx이므로 가장 일반적인 방법은 resx 파일을 이용하는 방법이 아닌가 싶다. 다른 대안으로는 resource 파일을 이용하는 형태이나 이는 일련의 추가 과정들도 있으므로 스킵.

일단 나라별 리소스 파일들이 필요하다. Nice+의 경우는

  • Resources.resx
  • Resources.ko-KR.resx

2개의 파일을 생성한다. 2개의 리소스 파일들의 내용은 같은 key를 쓰고 언어에 맞는 value들을 작성한다.

그 다음에 필요한 것은 ResourceManager를 생성하는 것. ResourceManager 이외에 Assembly와 CultureInfo를 불러 와야하므로 총 3개의 namespace 이용이 필요하다.

using System.Globalization;
using System.Reflection;
using System.Resources;

다음은 ResourceManager 객체를 만드는 일.

ResourceManager resources =
    new ResourceManager(Resources.ResourceManager.BaseName, Assembly.GetExecutingAssembly());

이제는 ResourceManager를 통해서 필요한 내용의 값들만 가져오면 된다.

string value = resources.GetString("key", CultureInfo.CurrentUICulture)

NETCF가 아닌 환경에서는 현재 thread에 CultureInfo를 등록하고 가져올 수 있는 방법이 있는데 NETCF는 그런 방법이 없어서 따로 CultureInfo를 가져오는 클래스를 만들던가 아니면 Nice+ 처럼 언어 변경은 하지 못하고 그냥 CultureInfo.CurrentUICulture를 이용하는 방법이 있다. Nice+에서는 T스토어는 그냥 한국어 버전만 다른 App store들은 영문만 지원하면 되어 각 스토어 사용자들의 폰의 CultureInfo.CurrentUICulture들이 각각 한국어이거나 아니거나의 경우이기에 위와 같이 처리를 했다.

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 을

발급받을 수 있다.