[WM][C#][Google Map之基地台定位]

  • 26659
  • 0
  • 2009-08-10

[WM][C#][Google Map之基地台定位]

如果你手上還有一支Windows Mobile 平台手機可是你想要做地圖定位資訊系統,不過機器裡面沒有GPS模組怎麼辦?沒關係這時我們可以利用基地台的id值來做定位,Cell-ID 是全球使用的識別碼,每個基地台都有一個唯一的ID,地區識別碼Location Area Identity(LAI)以及基地台識別碼Cell Identity(CI),完整的Cell-ID 包含MCC(Mobile Country Code) + MNC(Mobile Network Code) + LAC + CI。通常使用Cell-ID定位的精確度很低,誤差距離大小視基地台的涵蓋半徑而定,大約500 公尺至3 公里,接著我們把取得 Cell ID 值透過 Google Geolocation API 回傳地理經緯度資訊,在把這個這個經緯度到丟到 Google Maps API Services 回傳一張目前的靜態定位圖。

 

Step1:利用Radio Interface Layer (RIL)提供了一個處理CellCore系統軟體以及無線電硬體之間通訊的介面來取得Cell ID,若想要知道更多的應用函式可以連到 http://msdn.microsoft.com/en-us/library/aa921525.aspx 。接著填入我們的 RIL 程式碼

image

 

public class RIL  
       {  
           // string used to store the CellID string  
           private static string celltowerinfo = "";  

           /*  
            * Uses RIL to get CellID from the phone.  
            */
  
           public static string GetCellTowerInfo()  
           {  
               // initialise handles  
               IntPtr hRil = IntPtr.Zero;  
               IntPtr hRes = IntPtr.Zero;  

               // initialise result  
               celltowerinfo = "";  

               // initialise RIL  
               hRes = RIL_Initialize(1,                                        // RIL port 1  
                                     new RILRESULTCALLBACK(rilResultCallback), // function to call with result  
                                     null,                                     // function to call with notify  
                                     0,                                        // classes of notification to enable  
                                     0,                                        // RIL parameters  
                                     out hRil);                                // RIL handle returned  

               if (hRes != IntPtr.Zero)  
               {  
                   return "Failed to initialize RIL";  
               }
  

               // initialised successfully  

               // use RIL to get cell tower info with the RIL handle just created  
               hRes = RIL_GetCellTowerInfo(hRil);  

               // wait for cell tower info to be returned  
               waithandle.WaitOne();  

               // finished - release the RIL handle  
               RIL_Deinitialize(hRil);  

               // return the result from GetCellTowerInfo  
               return celltowerinfo;  
           }
  

           // event used to notify user function that a response has  
           //  been received from RIL  
           private static AutoResetEvent waithandle = new AutoResetEvent(false);  

           public static void rilResultCallback(uint dwCode,  
                                                IntPtr hrCmdID,  
                                                IntPtr lpData,  
                                                uint cbData,  
                                                uint dwParam)  
           {  
               // create empty structure to store cell tower info in  
               RILCELLTOWERINFO rilCellTowerInfo = new RILCELLTOWERINFO();  

               // copy result returned from RIL into structure  
               Marshal.PtrToStructure(lpData, rilCellTowerInfo);  

               // get the bits out of the RIL cell tower response that we want  
               celltowerinfo = rilCellTowerInfo.dwCellID + "-" +  
                               rilCellTowerInfo.dwLocationAreaCode + "-" +  
                               rilCellTowerInfo.dwMobileCountryCode;  

               // notify caller function that we have a result  
               waithandle.Set();  
           }
  

           // -------------------------------------------------------------------  
           //  RIL function definitions  
           // -------------------------------------------------------------------  

           /*  
            * Function definition converted from the definition  
            *  RILRESULTCALLBACK from MSDN:  
            *  
            * http://msdn2.microsoft.com/en-us/library/aa920069.aspx  
            */
  
           public delegate void RILRESULTCALLBACK(uint dwCode,  
                                                  IntPtr hrCmdID,  
                                                  IntPtr lpData,  
                                                  uint cbData,  
                                                  uint dwParam);  

           /*  
            * Function definition converted from the definition  
            *  RILNOTIFYCALLBACK from MSDN:  
            *  
            * http://msdn2.microsoft.com/en-us/library/aa922465.aspx  
            */
  
           public delegate void RILNOTIFYCALLBACK(uint dwCode,  
                                                  IntPtr lpData,  
                                                  uint cbData,  
                                                  uint dwParam);  

           /*  
            * Class definition converted from the struct definition  
            *  RILCELLTOWERINFO from MSDN:  
            *  
            * http://msdn2.microsoft.com/en-us/library/aa921533.aspx  
            */
  
           public class RILCELLTOWERINFO  
           {  
               public uint cbSize;  
               public uint dwParams;  
               public uint dwMobileCountryCode;  
               public uint dwMobileNetworkCode;  
               public uint dwLocationAreaCode;  
               public uint dwCellID;  
               public uint dwBaseStationID;  
               public uint dwBroadcastControlChannel;  
               public uint dwRxLevel;  
               public uint dwRxLevelFull;  
               public uint dwRxLevelSub;  
               public uint dwRxQuality;  
               public uint dwRxQualityFull;  
               public uint dwRxQualitySub;  
               public uint dwIdleTimeSlot;  
               public uint dwTimingAdvance;  
               public uint dwGPRSCellID;  
               public uint dwGPRSBaseStationID;  
               public uint dwNumBCCH;  
           }
  

           // -------------------------------------------------------------------  
           //  RIL DLL functions  
           // -------------------------------------------------------------------  

           /* Definition from: http://msdn2.microsoft.com/en-us/library/aa919106.aspx */  
           [DllImport("ril.dll")]  
           private static extern IntPtr RIL_Initialize(uint dwIndex,  
                                                       RILRESULTCALLBACK pfnResult,  
                                                       RILNOTIFYCALLBACK pfnNotify,  
                                                       uint dwNotificationClasses,  
                                                       uint dwParam,  
                                                       out IntPtr lphRil);  

           /* Definition from: http://msdn2.microsoft.com/en-us/library/aa923065.aspx */  
           [DllImport("ril.dll")]  
           private static extern IntPtr RIL_GetCellTowerInfo(IntPtr hRil);  

           /* Definition from: http://msdn2.microsoft.com/en-us/library/aa919624.aspx */  
           [DllImport("ril.dll")]  
           private static extern IntPtr RIL_Deinitialize(IntPtr hRil);  
       }


 

Step2: 將得到 Cell ID 利用 http post 方式傳到 Google 網站轉換成經緯度資訊,可以連到 http://code.google.com/intl/zh-TW/apis/gears/api_geolocation.html 了解運作流程。

image

 

class GMM  
    {  
        static byte[] PostData(int MCC, int MNC, int LAC, int CID,  
                               bool shortCID)  
        {  
            /* The shortCID parameter follows heuristic experiences:  
             * Sometimes UMTS CIDs are build up from the original GSM CID (lower 4 hex digits)  
             * and the RNC-ID left shifted into the upper 4 digits.  
             */
  
            byte[] pd = new byte[] {  
                0x00, 0x0e,  
                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,  
                0x00, 0x00,  
                0x00, 0x00,  
                0x00, 0x00,  

                0x1b,  
                0x00, 0x00, 0x00, 0x00, // Offset 0x11  
                0x00, 0x00, 0x00, 0x00, // Offset 0x15  
                0x00, 0x00, 0x00, 0x00, // Offset 0x19  
                0x00, 0x00,  
                0x00, 0x00, 0x00, 0x00, // Offset 0x1f  
                0x00, 0x00, 0x00, 0x00, // Offset 0x23  
                0x00, 0x00, 0x00, 0x00, // Offset 0x27  
                0x00, 0x00, 0x00, 0x00, // Offset 0x2b  
                0xff, 0xff, 0xff, 0xff,  
                0x00, 0x00, 0x00, 0x00  
            }
;  

            bool isUMTSCell = ((Int64)CID > 65535);  

            if (isUMTSCell)  
                Console.WriteLine("UMTS CID. {0}", shortCID ?  
class GMM  
    {  
        static byte[] PostData(int MCC, int MNC, int LAC, int CID,  
                               bool shortCID)  
        {  
            /* The shortCID parameter follows heuristic experiences:  
             * Sometimes UMTS CIDs are build up from the original GSM CID (lower 4 hex digits)  
             * and the RNC-ID left shifted into the upper 4 digits.  
             */
  
            byte[] pd = new byte[] {  
                0x00, 0x0e,  
                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,  
                0x00, 0x00,  
                0x00, 0x00,  
                0x00, 0x00,  

                0x1b,  
                0x00, 0x00, 0x00, 0x00, // Offset 0x11  
                0x00, 0x00, 0x00, 0x00, // Offset 0x15  
                0x00, 0x00, 0x00, 0x00, // Offset 0x19  
                0x00, 0x00,  
                0x00, 0x00, 0x00, 0x00, // Offset 0x1f  
                0x00, 0x00, 0x00, 0x00, // Offset 0x23  
                0x00, 0x00, 0x00, 0x00, // Offset 0x27  
                0x00, 0x00, 0x00, 0x00, // Offset 0x2b  
                0xff, 0xff, 0xff, 0xff,  
                0x00, 0x00, 0x00, 0x00  
            }
;  

            bool isUMTSCell = ((Int64)CID > 65535);  

            if (isUMTSCell)  
                Console.WriteLine("UMTS CID. {0}", shortCID ?  
Using short CID to resolve." : "");  
            else  
                Console.WriteLine("GSM CID given.");  

            if (shortCID)  
                CID &= 0xFFFF;      /* Attempt to resolve the cell using the    

            if ((Int64)CID > 65536) /* GSM: 4 hex digits, UTMS: 6 hex  
                                    digits */
  
                pd[0x1c] = 5;  
            else  
                pd[0x1c] = 3;  

            pd[0x11] = (byte)((MNC >> 24) & 0xFF);  
            pd[0x12] = (byte)((MNC >> 16) & 0xFF);  
            pd[0x13] = (byte)((MNC >> 8) & 0xFF);  
            pd[0x14] = (byte)((MNC >> 0) & 0xFF);  

            pd[0x15] = (byte)((MCC >> 24) & 0xFF);  
            pd[0x16] = (byte)((MCC >> 16) & 0xFF);  
            pd[0x17] = (byte)((MCC >> 8) & 0xFF);  
            pd[0x18] = (byte)((MCC >> 0) & 0xFF);  

            pd[0x27] = (byte)((MNC >> 24) & 0xFF);  
            pd[0x28] = (byte)((MNC >> 16) & 0xFF);  
            pd[0x29] = (byte)((MNC >> 8) & 0xFF);  
            pd[0x2a] = (byte)((MNC >> 0) & 0xFF);  

            pd[0x2b] = (byte)((MCC >> 24) & 0xFF);  
            pd[0x2c] = (byte)((MCC >> 16) & 0xFF);  
            pd[0x2d] = (byte)((MCC >> 8) & 0xFF);  
            pd[0x2e] = (byte)((MCC >> 0) & 0xFF);  

            pd[0x1f] = (byte)((CID >> 24) & 0xFF);  
            pd[0x20] = (byte)((CID >> 16) & 0xFF);  
            pd[0x21] = (byte)((CID >> 8) & 0xFF);  
            pd[0x22] = (byte)((CID >> 0) & 0xFF);  

            pd[0x23] = (byte)((LAC >> 24) & 0xFF);  
            pd[0x24] = (byte)((LAC >> 16) & 0xFF);  
            pd[0x25] = (byte)((LAC >> 8) & 0xFF);  
            pd[0x26] = (byte)((LAC >> 0) & 0xFF);  

            return pd;  
        }

                                    GSM CID part */
  

        static public string GetLatLng(string[] args)  
        {  
            if (args.Length < 4)  
            {  
                return string.Empty;  
            }
  
            string shortCID = "";   /* Default, no change at all */  
            if (args.Length == 5)  
                shortCID = args[4].ToLower();  
            try  
            {  
                String url = "http://www.google.com/glm/mmap";  
                HttpWebRequest req = (HttpWebRequest)WebRequest.Create(  
                    new Uri(url));  
                req.Method = "POST";  

                int MCC = Convert.ToInt32(args[0]);  
                int MNC = Convert.ToInt32(args[1]);  
                int LAC = Convert.ToInt32(args[2]);  
                int CID = Convert.ToInt32(args[3]);  
                byte[] pd = PostData(MCC, MNC, LAC, CID,  
                    shortCID == "shortcid");  

                req.ContentLength = pd.Length;  
                req.ContentType = "application/binary";  
                Stream outputStream = req.GetRequestStream();  
                outputStream.Write(pd, 0, pd.Length);  
                outputStream.Close();  

                HttpWebResponse res = (HttpWebResponse)req.GetResponse();  
                byte[] ps = new byte[res.ContentLength];  
                int totalBytesRead = 0;  
                while (totalBytesRead < ps.Length)  
                {  
                    totalBytesRead += res.GetResponseStream().Read(  
                        ps, totalBytesRead, ps.Length - totalBytesRead);  
                }
  

                if (res.StatusCode == HttpStatusCode.OK)  
                {  
                    short opcode1 = (short)(ps[0] << 8 | ps[1]);  
                    byte opcode2 = ps[2];  
                    int ret_code = (int)((ps[3] << 24) | (ps[4] << 16) |  
                                   (ps[5] << 8) | (ps[6]));  
                    if (ret_code == 0)  
                    {  
                        double lat = ((double)((ps[7] << 24) | (ps[8] << 16)  
                                     | (ps[9] << 8) | (ps[10]))) / 1000000;  
                        double lon = ((double)((ps[11] << 24) | (ps[12] <<  
                                     16) | (ps[13] << 8) | (ps[14]))) /  
                                     1000000;  
                        return lat + "|" + lon;  
                    }
  
                    else  
                        return string.Empty;  
                }
  
                else  
                    return string.Empty;  
            }
  
            catch (Exception ex)  
            {  
                MessageBox.Show(ex.ToString());  
                return string.Empty;  
            }
  
        }
  
    }
 

 

Step4:把 Google 回傳的經緯度資訊,在次使用 http 方式傳到 Google 回傳一張靜態定位圖回來,使用方法可連到 http://code.google.com/intl/zh-TW/apis/maps/documentation/staticmaps/ 介紹。

image

範例: http://maps.google.com/staticmap?&maptype=mobile&key=xxx&markers=25.033875,121.56445&center=,&size=240x320&zoom=15

利用http格式串一個url連線字串回傳一個手機模式的靜態圖,此張圖的尺寸大小為240*320,放大層級到15。

 

Step4:開啟vs2008新增一個 c# 智慧型裝置專案,在表單上建立webBrowser 元件用來顯示定位圖,在左下方功能表上新增一個map 按鍵用來觸發定位

image

Step5:在Map按鍵插入下列程式碼

string[] cellidFields =RIL.GetCellTowerInfo().ToString().Split('-');  
            // [0] - CID  
            // [1] - LAC  
            // [2] – MCC  
            //---Arguments for GetLatLng(MCC MNC LAC CID)---  
            string[] args = {  
                cellidFields[2], // MCC  
                "0",             // MNC – don’t need it here  
                cellidFields[1], // LAC  
                cellidFields[0]  // CID  
            }
;  

            string[] latlng = GMM.GetLatLng(args).Split('|');  
            Uri url = new Uri("http://maps.google.com/staticmap?&maptype=mobile&key=xxx&markers="+latlng[0].ToString().Replace(',', '.') +","+ latlng[1].ToString().Replace(',', '.') +"&center=,&size=240x320&zoom=15");  
            webBrowser1.Navigate(url);
 
Step6:在vs2008編輯器上功能表按下偵錯\開始偵錯部署你的應用程式

 Step7:按一下Map來看一下結果吧

image

Step8: 程式源碼