[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 程式碼
{
// 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 了解運作流程。
{
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/ 介紹。
利用http格式串一個url連線字串回傳一個手機模式的靜態圖,此張圖的尺寸大小為240*320,放大層級到15。
Step4:開啟vs2008新增一個 c# 智慧型裝置專案,在表單上建立webBrowser 元件用來顯示定位圖,在左下方功能表上新增一個map 按鍵用來觸發定位
Step5:在Map按鍵插入下列程式碼
// [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(',', '.') +"¢er=,&size=240x320&zoom=15");
webBrowser1.Navigate(url);
Step7:按一下Map來看一下結果吧
Step8: 程式源碼