【IT168技術】前一段時間,我曾在另一篇文章提到,我想採取的位置廣播思想,開發後續移動解決方案。當時的問題是我沒有辦法擺脫手機來獲取位置信息,或者使位置數據變得有用的一種方法。在這篇文章中,我將演示如何讓你的手機獲取GPS坐標.即使您的手機沒有內置的全球定位系統。
定位為什麼如此的困難?
電話技術成熟的速度是驚人的。目前有一大批手機都內置了全球定位系統,「智能」手機有數百人的平均價格標籤,而我們大多數人沒有爭先購買以更換我們目前的手機。事實上,全球定位系統還不包括無處不在的位置,這是真正的困難。我們的目標是讓手機獲得任何地點的位置,不只是GPS功能的手機。困難就在於此。
谷歌能做到,為什麼我們不能?
如果您尚未與谷歌的移動地圖進行比賽,我強烈推薦它。這實在是一種方便的小應用程式,尤其是當您在旅途中,需要你的地圖時。但是,在我的電話中也有移動地圖的作品,而我並沒有全球定位系統。這怎麼可能?在沒有全球定位系統的情況下,谷歌利用普通手機的數據通過他們的手機地圖已經找到了如何確定您的位置。也就是說,沒有全球定位系統的情況下,尋找位置是可能的。事實上,讓我們先來解構谷歌移動地圖工程的項目,並用C#構建它備份。
協議分析
我不打算在此詳述有關協議或數據包分析器(我使用Microsoft網絡監視器)。我只想說,第一步解構谷歌手機地圖是分析HTTP請求,而手機地圖是通過ActiveSync運行。
快速瀏覽移動地圖揭示了幾件事情。首先,谷歌發布請求http://www.google.com/glm/mmap的位置數據。我已經衝刷了它們的API。這意味著,我們正在尋找自己如何包裝和發送數據。其次,他們的數據發送四個關鍵部分:
1、Cell Tower編號
2、位置區碼(LAC)
3、行動網路代碼(MNC)
4、移動國家代碼(MCC)
這是個好消息!事實證明,幾乎所有的手機都具有這樣隨時可用的數據。
Cell Tower數據-無線接口層
我們需要破解打開Windows Mobile無線接口層Cell塔獲取數據。此外,您可以從MSDN 搜索RIL來獲取整個RIL 庫的細節。我們只關心RIL的Cell Tower埠部分。我們首先需要做的是添加必要的PInvoke DLL Import籤名,調用無線接口層。
代碼片段:
1 [DllImport("ril.dll")]
2
3 private static extern IntPtr RIL_Initialize(uint dwIndex, RILRESULTCALLBACK pfnResult,
4
5 RILNOTIFYCALLBACK pfnNotify, uint dwNotificationClasses,
6
7 uint dwParam, out IntPtr lphRil);
8
9 [DllImport("ril.dll", EntryPoint = "RIL_GetCellTowerInfo")]
10
11 private static extern IntPtr RIL_GetCellTowerInfo(IntPtr hRil);
12
13 [DllImport("ril.dll", EntryPoint = "RIL_Hangup")]
14
15 private static extern IntPtr RIL_Hangup(IntPtr hRil);
16
17 [DllImport("ril.dll")]
18
19 private static extern IntPtr RIL_Deinitialize(IntPtr hRil);
20
21
這四個方法包含在ril.dll中,是我們獲得Cell Tower數據的通路。有了這些方法,以及他們的回調RILRESULTCALLBACK和RILNOTIFYCALLBACK結構,我們可以很容易地調用Windows 窗口來獲得我們的Cell Tower 數據。
代碼片段:
1 public static CellTower GetCellTowerInfo()
2
3 {
4
5 IntPtr radioInterfaceLayerHandle = IntPtr.Zero;
6
7 IntPtr radioResponseHandle = IntPtr.Zero;
8
9 // Initialize the radio layer with a result callback parameter.
10
11 radioResponseHandle = RIL_Initialize(1, new RILRESULTCALLBACK(CellDataCallback),
12
13 null, 0, 0, out radioInterfaceLayerHandle);
14
15 // The initialize API call will always return 0 if initialization is successful.
16
17 if (radioResponseHandle != IntPtr.Zero)
18
19 {
20
21 return null;
22
23 }
24
25 // Query for the current tower data.
26
27 radioResponseHandle = RIL_GetCellTowerInfo(radioInterfaceLayerHandle);
28
29 // Wait for cell tower info to be returned since RIL_GetCellTowerInfo invokes the
30
31 // callback method asynchronously.
32
33 waithandle.WaitOne();
34
35 // Release the RIL handle
36
37 RIL_Deinitialize(radioInterfaceLayerHandle);
38
39 // Convert the raw tower data structure data into a CellTower object
40
41 return new CellTower()
42
43 {
44
45 TowerId = Convert.ToInt32(_towerDetails.dwCellID),
46
47 LocationAreaCode = Convert.ToInt32(_towerDetails.dwLocationAreaCode),
48
49 MobileCountryCode = Convert.ToInt32(_towerDetails.dwMobileCountryCode),
50
51 MobileNetworkCode = Convert.ToInt32(_towerDetails.dwMobileNetworkCode),
52
53 };
54
55 }
56
57
現在,我們可以調用GetCellTowerInfo(),我們將得到強類型CellTower類以及所有細節返回值。我總是喜歡在一個強類型的PInvoke輸出,而不是代表編組工作的指針。
注釋:此代碼將不能在一個仿真器運行,除非你配置蜂窩仿真並且能夠很好的運行。我建議使用手機,而不是仿真器。
Cell 數據服務
我們有了Cell Tower的數據,但現在能夠做什麼?我們還沒有全球定位系統坐標。我們知道,谷歌正在使這種轉換發生,Cell Tower數據為緯度和長,我們要繼續剖析谷歌手機地圖。他們正在給上述URL上傳二進位數據。同樣,分析協議超出了本文的範圍。你會發現,我們需要換一個55位元組數組的數據。字節的大部分是空的(0),但我們確實需要填補數組中的幾個關鍵項。下面的代碼來創建和用Cell Tower數據填充字節數組:
代碼片段:
1 private static byte[] GetFormPostData(int cellTowerId, int mobileCountryCode,
2
3 int mobileNetworkCode, int locationAreaCode)
4
5 {
6
7 byte[] pd = new byte[55];
8
9 pd[1] = 14; //0x0e;
10
11 pd[16] = 27; //0x1b;
12
13 pd[47] = 255; //0xff;
14
15 pd[48] = 255; //0xff;
16
17 pd[49] = 255; //0xff;
18
19 pd[50] = 255; //0xff;
20
21 // GSM uses 4 digits while UTMS used 6 digits (hex)
22
23 pd[28] = ((Int64)cellTowerId > 65536) ? (byte)5 : (byte)3;
24
25 Shift(pd, 17, mobileNetworkCode);
26
27 Shift(pd, 21, mobileCountryCode);
28
29 Shift(pd, 31, cellTowerId);
30
31 Shift(pd, 35, locationAreaCode);
32
33 Shift(pd, 39, mobileNetworkCode);
34
35 Shift(pd, 43, mobileCountryCode);
36
37 return pd;
38
39 }
40
41 ///
42
43 /// Shifts specified data in the byte array starting at the specified array index.
44
45 ///
46
47 /// The data.
48
49 /// The start index.
50
51 /// The left operand.
52
53 private static void Shift(byte[] data, int startIndex, int leftOperand)
54
55 {
56
57 int rightOperand = 24;
58
59 for (int i = 0; i < 4; i++, rightOperand -= 8)
60
61 {
62
63 data[startIndex++] = (byte)((leftOperand >> rightOperand) & 255);
64
65 }
66
67 }
68
69
總之,我們可以通過在我們的4個參數:Cell Tower的 ID,LAV,MNC,和MCC,我們得到一個字節數組,並且可以通過HTTP上傳到谷歌。下面的代碼演示了整個HTTP請求,這需要我們的強類型的Cell Tower的數據(從上述的PInvoke),從谷歌的資料庫中返回緯度和經度!
代碼片段:
1 internal static GeoLocation GetLocation(CellTower tower)
2
3 {
4
5 try
6
7 {
8
9 // Translate cell tower data into http post parameter data
10
11 byte[] formData = GetFormPostData(tower.TowerId,
12
13 tower.MobileCountryCode,
14
15 tower.MobileNetworkCode,
16
17 tower.LocationAreaCode);
18
19 HttpWebRequest request = (HttpWebRequest)WebRequest.Create(
20
21 new Uri(Google_Mobile_Service_Uri));
22
23 request.Method = "POST";
24
25 request.ContentLength = formData.Length;
26
27 request.ContentType = "application/binary";
28
29 Stream outputStream = request.GetRequestStream();
30
31 // Write the cell data to the http stream
32
33 outputStream.Write(formData, 0, formData.Length);
34
35 outputStream.Close();
36
37 HttpWebResponse response = (HttpWebResponse)request.GetResponse();
38
39 return ReadResponse(response);
40
41 }
42
43 catch{}
44
45 return GeoLocation.Empty;
46
47 }
48
49 private static GeoLocation ReadResponse(HttpWebResponse response)
50
51 {
52
53 byte[] responseData = new byte[response.ContentLength];
54
55 int bytesRead = 0;
56
57 // Read the response into the response byte array
58
59 while (bytesRead < responseData.Length)
60
61 {
62
63 bytesRead += response.GetResponseStream()
64
65 .Read(responseData, bytesRead, responseData.Length - bytesRead);
66
67 }
68
69 // Check the response
70
71 if (response.StatusCode == HttpStatusCode.OK)
72
73 {
74
75 int successful = Convert.ToInt32(GetCode(responseData, 3));
76
77 if (successful == 0)
78
79 {
80
81 return new GeoLocation()
82
83 {
84
85 Latitude = GetCode(responseData, 7) / 1000000,
86
87 Longitude = GetCode(responseData, 11) / 1000000
88
89 };
90
91 }
92
93 }
94
95 return GeoLocation.Empty;
96
97 }
98
99 ///
100
101 /// Gets the latitude or longitude from the byte array.
102
103 ///
104
105 /// The byte array.
106
107 /// The start index.
108
109 ///
110
111 private static double GetCode(byte[] data, int startIndex)
112
113 {
114
115 return ((double)((data[startIndex++] << 24) |
116
117 (data[startIndex++] << 16) |
118
119 (data[startIndex++] << 8) |
120
121 (data[startIndex++])));
122
123 }
124
125
簡要的Cell Tower說明
讓我花一點時間來描述剛剛發生。我們從系統中讀取Cell Tower 數據,發送通過HTTP協議發送給谷歌,並回來經度和緯度。這樣能行得通嗎?
聯邦通訊委員會保留了在美國的每一個Cell Tower紀錄。谷歌沒有做任何(太)神奇的事情。他們只是簡單地為我們提交的Cell Tower數據查找細節。以緯度/經度的格式返比較知名的Tower的具體位置。
但是,谷歌並不是塔的具體位置的唯一資料庫。 OpenCellID.org是一個開源的倡議,來標記所有的Cell Tower地圖和GPS坐標。雅虎!正在用他們的ZoneTag服務做同樣的事情。
如果有更容易的選擇為什麼還要不厭其煩地使用谷歌?
這是一個很大的問題!簡單地說,谷歌是最全面的資料庫。 OpenCellID和ZoneTag較新,他們的資料庫不完整,用這些「測試」服務其中之一,在主要城市郊區的家裡甚至不能獲得一個有效的全球定位系統。但是,谷歌的作品在任何地方我已經測試過。
如果我的手機有GPS那將會是怎樣一個情況?
當然,Cell Tower的數據並不能像全球定位系統那樣精確。你並不能利用手機中Cell Tower 的數據告訴我你的具體位置,如果在你附近10英尺或1000英尺有Cell Tower。換句話說,最好使用全球定位系統,因為它的精確度。但是有例外,如當你在室內,無法獲得的GPS信號時,你很可能仍然有Cell Tower連接。
從兼容的手機獲取GPS數據比獲取Cell Tower的數據更容易。更好的是-微軟已經分發了一個庫來做所有繁重的事情。
代碼片段:
1 private Gps _gps = new Gps();
2
3 private GpsPosition _currentPosition;
4
5 public void Start()
6
7 {
8
9 _gps.LocationChanged += new
10
11 Microsoft.Location.LocationChangedEventHandler(_gps_LocationChanged);
12
13 if (!_gps.Opened)
14
15 {
16
17 _gps.Open();
18
19 }
20
21 }
22
23 private void _gps_LocationChanged(object sender,
24
25 Microsoft.Location.LocationChangedEventArgs args)
26
27 {
28
29 _currentPosition = args.Position;
30
31 }
32
33
到目前為止,我們已經建立了兩個機制來獲取您的手機的經度和緯度。我們現在有儘可能多的潛力,例如,谷歌地圖!
什麼是好的位置呢?
我一般知道我在哪裡,所以知道我的位置並沒有多大幫助。像谷歌手機地圖的應用已經顯示我的位置,但他們沒有利用它做任何東西。這是我的意見,即基於位置服務的真正價值在分享您的位置,而不僅僅是知道你的位置。這是社交網絡下一個必然會發生的。
充分利用位置
你應該拿出下次如何處理您的位置的很建議。我的第一個創造性的催化劑就是利用您的經度和緯度更新您的Yahoo!火鷹帳戶。火鷹是從雅虎開的新服務!將你的位置,「廣播」到您的訂閱(在其快速增長的庫應用程式)。其實,這就是這個項目被命名為DeepCast的原因。我們正在廣播的位置給(潛在的)深度的服務和服務提供者。
火鷹
火鷹使用Oauth作為其授權的平臺。我不是那麼熟悉所有Oauth細節。我所知道的是,有一個授權的手工過程可以在任何應用程式進行更新。在Oauth認證的第一步是生成一個請求令牌和秘密對。
然後,您可以瀏覽(通過您的桌面瀏覽器)授權網址。
您必須手動確認應用程式可以更新您的個人資料。
最後,您有您的要求,交換令牌和授權令牌和秘密。這些價值觀您需要保存和使用每次應用程式嘗試更新您的位置。
有很少來形容更新Twitter的條款。它像發送一個HTTP請求那樣簡單。一個困難是增加一個連結到郵件中。 Twitter的使用形式的URL編碼截斷最查詢字符串。我們將生成一個TinyUrl避開這個問題。
下面是我們如何更新Twitter的:
1 internal override void Update(GeoLocation location)
2
3 {
4
5 if (!string.IsNullOrEmpty(Settings.TwitterEmail) &&
6
7 !string.IsNullOrEmpty(Settings.TwitterPassword))
8
9 {
10
11 string googleMapLink = string.Format(Google_Map_Uri,
12
13 location.Latitude, location.Longitude);
14
15 string tilyMapLink = TinyUrlService.GetTinyUrl(googleMapLink);
16
17 string place = string.Format("I'm here: {0} - 'L:{1}, {2}'",
18
19 new object[] { tilyMapLink, location.Latitude, location.Longitude });
20
21 string data = string.Format("status={0}", place);
22
23 HttpWebRequest request = (HttpWebRequest)WebRequest.Create(Twitter_Api_Uri);
24
25 request.Credentials = new NetworkCredential(Settings.TwitterEmail,
26
27 Settings.TwitterPassword);
28
29 request.ContentType = "application/x-www-form-urlencoded";
30
31 request.Method = "POST";
32
33 request.AllowWriteStreamBuffering = true;
34
35 byte[] bytes = Encoding.UTF8.GetBytes(data);
36
37 request.ContentLength = bytes.Length;
38
39 using (Stream requestStream = request.GetRequestStream())
40
41 {
42
43 requestStream.Write(bytes, 0, bytes.Length);
44
45 requestStream.Flush();
46
47 requestStream.Close();
48
49 using (WebResponse response = request.GetResponse())
50
51 {
52
53 using (StreamReader reader =
54
55 new StreamReader(response.GetResponseStream()))
56
57 {
58
59 reader.ReadToEnd();
60
61 }
62
63 }
64
65 }
66
67 }
68
69 }
70
71
請注意,有一個位置的公約,'長:緯度,離子',那就是我們更深層的目的。這種格式可以通過在TwitterMap.com解析的鄉親。同樣,我們試圖使有益的位置深的受眾群。
內部事務
當然,分享您的位置並不是使信息有用的唯一的方式。例如,使同一地點使用面向外部的服務。在主頁上的地圖只是一個瀏覽器的控制,其網址被設置為一個Yahoo!地圖圖像。優雅而簡單,這正是我想要!
你可以享受的服務
我能想到的位置可能的若干用途是:
1、通過Web服務跟蹤你去哪兒。支持創建一個網站,顯示您的軌道地圖-何時何地。畫出你在哪裡旅行的軌跡。
2、跟蹤你的孩子,讓您始終知道他們在哪裡。
3、地理標記您的照片。Flickr支持地理標記圖像,例如。記錄您的車隊。
4、地理博客。
5、更新您的Facebook / MySpace的/社會網站與您的位置。