Where am I project and sources

前几天无意中看到大觉者的音乐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的右边。大家想找我玩或者聊天什么的可以先瞅瞅我在不在家 – – 最后提醒一句:基站有偏差,捉奸需谨慎。。。

3 Replies to “Where am I project and sources”

    1. Yi,没看懂文章么,我不想一直开着,所以自己写了程序

Leave a Reply

Your email address will not be published. Required fields are marked *