前几天无意中看到大觉者的音乐blog中有个Google地图标出了where he is,看了一下用的是Google latitude。于是我也去弄了一个,但是发现Google latitude有以下三大问题:
1、Google latitude需要高版本的Google手机地图,而我一直在用2.4版的;
2、WM的用户都知道,Google地图是没法最小化到后台运行的,我指的是,尽管可以切换回桌面,但背景灯是不会灭的,会亮10分钟左右,10分钟之后它终于到了后台,但是又什么事都不干了;
3、Google latitude向服务器报告的坐标为地球坐标,而不是火星坐标,在网站上贴出来会有很大的偏移(有关天朝火星坐标系偏移的基本知识请自行询问谷歌或度娘)。
故决定自己写。在这儿想提一下火星坐标的郁闷问题。前几天,我发现有充足的迹象表明Google地图软件中有火星坐标系的算法,遂对2.4版的Google Maps进行分析,经过N天的奋斗,终于在茫茫ARM反汇编代码中找到了坐标修正的函数(不论是微软的模拟器还是我的手机都不支持硬件断点(数据断点),造成动态分析几乎成为不可能)。结果悲剧地发现这个算法是个假的,真正的算法在服务器上,客户端只不过是对服务器上数据做一个近似的纠偏算法。。天朝V5。。
所以。。纠偏的活还是用大众的那个。。每0.1度(约11公里)取一个偏移数据,也就是把全中国的版面分成11×11平方公里的小正方形网格,取每个顶点的数据,一共是29.7万个点,做成二进制数据文件是2.26M。然后用个什么数学上的插值算法算,纠偏精确度可达5m左右。php算法如下:
function LocationShift($location) { list($lat, $lng) = explode(',', $location); $xtry = $lng; $ytry = $lat; $fp = fopen('ShiftData.dat', 'rb'); for ($loop = 0; $loop < 10; $loop++) { if ($xtry < 72 || $xtry > 137.9 || $ytry < 10 || $ytry > 54.9) break; $i = (int)(($xtry - 72) * 10.0); $j = (int)(($ytry - 10) * 10.0); fseek($fp, (($i) + 660 * ($j)) * 8); $data = unpack('L*', fread($fp, 8)); $x1 = $data[1] / 100000.0; $y1 = $data[2] / 100000.0; fseek($fp, (($i + 1) + 660 * ($j)) * 8); $data = unpack('L*', fread($fp, 8)); $x2 = $data[1] / 100000.0; $y2 = $data[2] / 100000.0; fseek($fp, (($i + 1) + 660 * ($j + 1)) * 8); $data = unpack('L*', fread($fp, 8)); $x3 = $data[1] / 100000.0; $y3 = $data[2] / 100000.0; fseek($fp, (($i) + 660 * ($j + 1)) * 8); $data = unpack('L*', fread($fp, 8)); $x4 = $data[1] / 100000.0; $y4 = $data[2] / 100000.0; $t = ($xtry - 72.0 - 0.1 * $i) * 10.0; $u = ($ytry - 10.0 - 0.1 * $j) * 10.0; $dx = (1.0 - $t) * (1.0 - $u) * $x1 + $t * (1.0 - $u) * $x2 + $t * $u * $x3 + (1.0 - $t) * $u * $x4 - $xtry; $dy = (1.0 - $t) * (1.0 - $u) * $y1 + $t * (1.0 - $u) * $y2 + $t * $u * $y3 + (1.0 - $t) * $u * $y4 - $ytry; $xtry = ($xtry + $lng + $dx) / 2.0; $ytry = ($ytry + $lat + $dy) / 2.0; } fclose($fp); return sprintf('%.6f,%.6f', $ytry, $xtry); }
然后是手机基站转坐标,参考DeepCast源码即可。php代码如下:
function GetCellLoc($cellInfo) { if ($cellInfo == '') return; list($mobileCountryCode, $mobileNetworkCode, $locationAreaCode, $cellTowerId) = explode(':', $cellInfo); $postData = "\x00\x0E" . pack('x14') . "\x1B"; $postData .= pack('N3x2N6', $mobileNetworkCode, $mobileContryCode, $cellTowerId > 0x10000 ? 5 : 3, $cellTowerId, $locationAreaCode, $mobileNetworkCode, $mobileContryCode, -1, 0); //echo strlen($postData); $fp = fsockopen('www.google.com', 80); fwrite($fp, "POST /glm/mmap HTTP/1.1\r\nHost: www.google.com\r\nContent-Length: " . strlen($postData) . "\r\nContent-Type: application/binary\r\nConnection: Close\r\n\r\n"); fwrite($fp, $postData); while(fgets($fp) != "\r\n"); $responseData = fread($fp, 0x2000); fclose($fp); list($dummy, $status) = unpack('N', substr($responseData, 3)); if ($status != 0) return; list($dummy, $status, $latitude, $longitude, $accuracy) = unpack('N4', substr($responseData, 3)); return sprintf('%0.6f,%0.6f,%d', $latitude / 1000000.0, $longitude / 1000000.0, $accuracy); }
手机方面图简单,直接用.net写了。。核心代码是取手机的cellid,参考DeepCast代码。
Imports System.Runtime.InteropServices Imports System.Threading Public Class CellTower Private Delegate Sub RILRESULTCALLBACK(ByVal dwCode As UInteger, ByVal hrCmdID As IntPtr, ByVal lpData As IntPtr, ByVal cbData As UInteger, ByVal dwParam As UInteger) Private Delegate Sub RILNOTIFYCALLBACK(ByVal dwCode As UInteger, ByVal lpData As IntPtr, ByVal cbData As UInteger, ByVal dwParam As UInteger) <StructLayout(LayoutKind.Explicit)> _ Private Class RILCELLTOWERINFO <FieldOffset(0)> _ Private dwSize As UInteger <FieldOffset(4)> _ Private dwParams As UInteger <FieldOffset(8)> _ Public dwMobileCountryCode As UInteger <FieldOffset(12)> _ Public dwMobileNetworkCode As UInteger <FieldOffset(16)> _ Public dwLocationAreaCode As UInteger <FieldOffset(20)> _ Public dwCellID As UInteger <FieldOffset(24)> _ Private dwBaseStationID As UInteger <FieldOffset(28)> _ Private dwBroadcastControlChannel As UInteger <FieldOffset(32)> _ Private dwRxLevel As UInteger <FieldOffset(36)> _ Private dwRxLevelFull As UInteger <FieldOffset(40)> _ Private dwRxLevelSub As UInteger <FieldOffset(44)> _ Private dwRxQuality As UInteger <FieldOffset(48)> _ Private dwRxQualityFull As UInteger <FieldOffset(52)> _ Private dwRxQualitySub As UInteger End Class Private Declare Function RIL_Initialize Lib "ril.dll" (ByVal dwIndex As UInteger, ByVal pfnResult As RILRESULTCALLBACK, ByVal pfnNotify As RILNOTIFYCALLBACK, ByVal dwNotificationClasses As Integer, ByVal dwParam As Integer, ByRef lphRil As IntPtr) As IntPtr Private Declare Function RIL_GetCellTowerInfo Lib "ril.dll" (ByVal hRil As IntPtr) As IntPtr Private Declare Function RIL_Hangup Lib "ril.dll" (ByVal hRil As IntPtr) As IntPtr Private Declare Function RIL_Deinitialize Lib "ril.dll" (ByVal hRil As IntPtr) As IntPtr Private Shared _towerDetails As RILCELLTOWERINFO Private Shared waithandle As AutoResetEvent = New AutoResetEvent(False) Private Shared WithEvents _timer As New System.Windows.Forms.Timer Private Shared _currentTower As CellTower Public TowerId As Integer Public LocationAreaCode As Integer Public MobileCountryCode As Integer Public MobileNetworkCode As Integer Shared Sub New() _timer.Interval = 5000 _timer.Enabled = True End Sub Public Shared ReadOnly Property CurrentTower() As CellTower Get If _currentTower Is Nothing Then _currentTower = GetCellTowerInfo() Return _currentTower End Get End Property Private Shared Sub _timer_Tick(ByVal sender As Object, ByVal e As EventArgs) Handles _timer.Tick _currentTower = GetCellTowerInfo() End Sub Private Shared Function GetCellTowerInfo() As CellTower Dim radioInterfaceLayerHandle As IntPtr = IntPtr.Zero Dim radioResponseHandle As IntPtr = IntPtr.Zero ' Initialize the radio layer with a result callback parameter. radioResponseHandle = RIL_Initialize(1, New RILRESULTCALLBACK(AddressOf CellDataCallback), Nothing, 0, 0, radioInterfaceLayerHandle) ' The initialize API call will always return 0 if initialization is successful. If radioResponseHandle <> IntPtr.Zero Then Return Nothing End If ' Qusery for the current tower data. radioResponseHandle = RIL_GetCellTowerInfo(radioInterfaceLayerHandle) ' Wait for cell tower info to be returned since RIL_GetCellTowerInfo invokes the ' callback method asynchronously. waithandle.WaitOne() ' Release the RIL handle RIL_Deinitialize(radioInterfaceLayerHandle) ' Convert the raw tower data structre data into a CellTower object Dim cellTower As New CellTower cellTower.TowerId = Convert.ToInt32(_towerDetails.dwCellID) cellTower.LocationAreaCode = Convert.ToInt32(_towerDetails.dwLocationAreaCode) cellTower.MobileCountryCode = Convert.ToInt32(_towerDetails.dwMobileCountryCode) cellTower.MobileNetworkCode = Convert.ToInt32(_towerDetails.dwMobileNetworkCode) Return cellTower End Function Private Shared Sub CellDataCallback(ByVal dwCode As UInteger, ByVal hrCmdID As IntPtr, ByVal lpData As IntPtr, ByVal cbData As UInteger, ByVal dwParam As UInteger) ' Refresh the current tower details _towerDetails = New RILCELLTOWERINFO() ' Copy result returned from RIL into structure Marshal.PtrToStructure(lpData, _towerDetails) ' notify caller function that we have a result waithandle.Set() End Sub Public Overrides Function ToString() As String Return String.Format("{0}:{1}:{2}:{3}", MobileCountryCode, MobileNetworkCode, LocationAreaCode, TowerId) End Function End Class
GPS的以后再写。。。最后效果呢,就在我blog的右边。大家想找我玩或者聊天什么的可以先瞅瞅我在不在家 – – 最后提醒一句:基站有偏差,捉奸需谨慎。。。
是不是说你的google map得一直开着?
Yi,没看懂文章么,我不想一直开着,所以自己写了程序
小心跨省^^