Android基于蓝牙iBeacon的室内定位App和算法
一、简介
室内部署蓝牙信标,移动端app通过接收广播信号,
通过定位算法实现位置定位,并在三维系统实时更新显示位置。
二、代码
package ibeaconlocate; import android.Manifest; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; import android.os.Build; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.support.v7.widget.DefaultItemAnimator; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.widget.TextView; import android.widget.Toast; import com.anthonycr.grant.PermissionsManager; import com.anthonycr.grant.PermissionsResultAction; import java.util.ArrayList; import java.util.LinkedHashMap; import java.util.List; import CommonUtils.DeviceIDUitl; import CommonUtils.TimeUtils; import DbLibrary.DbManager; import HttpUtils.ReqCallBack; import HttpUtils.RequestManager; import XyItemsRecycleradatper; import IbeaconLibrary.BeaconType; import IbeaconLibrary.BeaconUtils; import IbeaconLibrary.BluetoothLeDevice; import IbeaconLibrary.BluetoothLeDeviceStore; import IbeaconLibrary.BluetoothLeScanner; import IbeaconLibrary.BluetoothUtils; import IbeaconLibrary.IbeaconLocateUtil; import IbeaconLibrary.Point; public class MainActivity extends AppCompatActivity{ private BluetoothUtils mBluetoothUtils; private BluetoothLeScanner mScanner; private BluetoothLeDeviceStore mDeviceStore; private TextView txtViewX; private TextView txtViewY; private List<Point> mDatas;//扫描到的记录 private XyItemsRecycleradatper recycleAdapter;//列表数据适配器 private AndriodDevice mAndriodDevice;//巡检设备 private RequestManager requestManager; //主入口点,初始化 @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); txtViewX =(TextView)findViewById(R.id.textViewX); txtViewY=(TextView)findViewById(R.id.textViewY); mDeviceStore = new BluetoothLeDeviceStore(); mBluetoothUtils = new BluetoothUtils(this); mScanner = new BluetoothLeScanner(mLeScanCallback, mBluetoothUtils); startScanPrepare(); } /* 菜单项初始化 */ @Override public boolean onCreateOptionsMenu(final Menu menu) { getMenuInflater().inflate(R.menu.ypontmain, menu); if (!mScanner.isScanning()) { menu.findItem(R.id.menu_stop).setVisible(false); menu.findItem(R.id.menu_scan).setVisible(true); } else { menu.findItem(R.id.menu_stop).setVisible(true); menu.findItem(R.id.menu_scan).setVisible(false); } if (mScanner.isCheck()) { menu.findItem(R.id.menu_check).setVisible(false); menu.findItem(R.id.menu_noCheck).setVisible(true); } else { menu.findItem(R.id.menu_check).setVisible(true); menu.findItem(R.id.menu_noCheck).setVisible(false); } if (mScanner.isUpLocateData()) { menu.findItem(R.id.menu_updata).setVisible(false); menu.findItem(R.id.menu_noupdata).setVisible(true); } else { menu.findItem(R.id.menu_updata).setVisible(true); menu.findItem(R.id.menu_noupdata).setVisible(false); } return true; } /* 选择菜单项 */ @Override public boolean onOptionsItemSelected(final MenuItem item) { switch (item.getItemId()) { case R.id.menu_scan: startScanPrepare(); break; case R.id.menu_stop: mScanner.scanLeDevice(-1, false); invalidateOptionsMenu(); break; case R.id.menu_check: mScanner.setCheck(mScanner.isScanning()?true:false); invalidateOptionsMenu(); break; case R.id.menu_noCheck: mScanner.setCheck(false); invalidateOptionsMenu(); break; case R.id.menu_clear: mDatas.clear(); recycleAdapter.notifyDataSetChanged(); break; case R.id.menu_updata: mScanner.setIsUpLocateData(true); invalidateOptionsMenu(); break; case R.id.menu_noupdata: mScanner.setIsUpLocateData(false); invalidateOptionsMenu(); break; } return true; } /* 尝试开启蓝牙扫描 */ private void startScanPrepare() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { PermissionsManager.getInstance().requestPermissionsIfNecessaryForResult(this, new String[]{Manifest.permission.ACCESS_COARSE_LOCATION}, new PermissionsResultAction() { @Override public void onGranted() { startScan(); } @Override public void onDenied(String permission) { Toast.makeText(MainActivity.this, "访问蓝牙扫描结果需要访问权限!", Toast.LENGTH_SHORT) .show(); } }); } else { startScan(); } } /* 开始蓝牙扫描 */ private void startScan() { final boolean isBluetoothOn = mBluetoothUtils.isBluetoothOn(); final boolean isBluetoothLePresent = mBluetoothUtils.isBluetoothLeSupported(); mDeviceStore.clear(); mBluetoothUtils.askUserToEnableBluetoothIfNeeded(); InitRecyclerView(); if (isBluetoothOn && isBluetoothLePresent) { mScanner.scanLeDevice(-1, true); invalidateOptionsMenu(); mAndriodDevice =new AndriodDevice(); requestManager=RequestManager.getInstance(MainActivity.this); } } /* 初始化列表控件 */ private void InitRecyclerView() { mDatas = new ArrayList<Point>(); recycleAdapter=new XyItemsRecycleradatper(this,mDatas);//条目适配器 RecyclerView recyclerView=(RecyclerView) findViewById(R.id.recy_list);//列表控件 LinearLayoutManager layoutManager = new LinearLayoutManager(this ); recyclerView.setLayoutManager(layoutManager); recyclerView.setHasFixedSize(true); //创建并设置Adapter recyclerView.setAdapter(recycleAdapter); recyclerView.setItemAnimator(new DefaultItemAnimator()); } //蓝牙扫描回调函数 private final BluetoothAdapter.LeScanCallback mLeScanCallback = new BluetoothAdapter.LeScanCallback() { @Override public void onLeScan(final BluetoothDevice device, final int rssi, final byte[] scanRecord) { final BluetoothLeDevice deviceLe = new BluetoothLeDevice(device, rssi, scanRecord, System.currentTimeMillis()); if (BeaconUtils.getBeaconType(deviceLe) == BeaconType.IBEACON) { if (deviceLe.getName()!=null) { deviceLe.IbeaconLocateData= DbManager.onResume(MainActivity.this,deviceLe.getName()); if (deviceLe.IbeaconLocateData!= null) { mDeviceStore.addDevice(deviceLe); } } } else { return; } List<BluetoothLeDevice> sortedDevices = IbeaconLocateUtil.GetSortedDevices(mDeviceStore); Point curLocatePoint=IbeaconLocateUtil.getLocateXY2(sortedDevices); if (IbeaconLocateUtil.isSameLocationValue(curLocatePoint)) { return; } IbeaconLocateUtil.curLocatePoint=curLocatePoint; if (sortedDevices!=null&&!sortedDevices.isEmpty()&&mScanner.isCheck()) { if (mScanner.isUpLocateData()) { reportLocateData(new Point(sortedDevices.get(0).IbeaconLocateData.getX(), sortedDevices.get(0).IbeaconLocateData.getY())); } double xCheck=sortedDevices.get(0).IbeaconLocateData.getX(); double yCheck=sortedDevices.get(0).IbeaconLocateData.getY(); if (mDatas.size()>50) { mDatas.remove(mDatas.size()-1); } mDatas.add(0,new Point(xCheck,yCheck)); recycleAdapter.notifyDataSetChanged(); } else { if (curLocatePoint != null) { if (mScanner.isUpLocateData()) { reportLocateData(curLocatePoint);//提交定位数据到服务器 } txtViewX.setText(String.valueOf(curLocatePoint.X)); txtViewY.setText(String.valueOf(curLocatePoint.Y)); if (mDatas.size() > 50) { mDatas.remove(mDatas.size() - 1); } mDatas.add(0, new Point(curLocatePoint.X, curLocatePoint.Y)); recycleAdapter.notifyDataSetChanged(); } } } }; /* 提交定位数据到服务器 */ private void reportLocateData(Point curLocatePoint) { mAndriodDevice.DeviceID= DeviceIDUitl.getDeviceId(MainActivity.this); mAndriodDevice.Devicename= DeviceIDUitl.getDeviceId(MainActivity.this); mAndriodDevice.DateTimeNow= TimeUtils.getNowDateTime("yyyy-MM-dd HH:mm:ss"); mAndriodDevice.X=curLocatePoint.X; mAndriodDevice.Y=curLocatePoint.Y; mAndriodDevice.Xsbh="xs"+TimeUtils.getNowDateTime("yyyyMMddHHmmssSSS")+mAndriodDevice.DeviceID; LinkedHashMap<String,String> map=new LinkedHashMap<>(); map.put("deviceid".toLowerCase(),mAndriodDevice.DeviceID); map.put("devicename".toLowerCase(),mAndriodDevice.Devicename); map.put("time".toLowerCase(),mAndriodDevice.DateTimeNow); map.put("x",String.valueOf(curLocatePoint.X)); map.put("y",String.valueOf(curLocatePoint.Y)); map.put("time".toLowerCase(),mAndriodDevice.DateTimeNow); map.put("xsbh",mAndriodDevice.Xsbh); requestManager.requestAsyn(RequestManager.BASE_URL, 0, map, new ReqCallBack<Object>() { @Override public void onReqSuccess(Object result) { //String resultMsg=String.valueOf(result); } @Override public void onReqFailed(String failMsg) { //String resultMsg=String.valueOf(failMsg); } }); } }
package IbeaconLibrary; import java.util.ArrayList; import java.util.List; public class IbeaconLocateUtil { public static Point curLocatePoint; public static boolean isSameLocationValue(Point p) { if (p==null) { return false; } if (curLocatePoint==null) { return false; } if (p.X==curLocatePoint.X&&p.Y==curLocatePoint.Y) { return true; } else { return false; } } public static Point getLocateXY(List<BluetoothLeDevice> curScanedDevices) { if (curScanedDevices==null||curScanedDevices.size()==0) { return null; } else if (curScanedDevices.size()<=2) { int rssi=curScanedDevices.get(0).getRssi(); return new Point(curScanedDevices.get(0).IbeaconLocateData.getX(),curScanedDevices.get(0).IbeaconLocateData.getY()); } else { int rssi1=curScanedDevices.get(0).getRssi(); double distance1= RSSIDistanceUitl.calculateAccuracy2(rssi1); int rssi2=curScanedDevices.get(1).getRssi(); double distance2= RSSIDistanceUitl.calculateAccuracy2(rssi2); int rssi3=curScanedDevices.get(2).getRssi(); double distance3= RSSIDistanceUitl.calculateAccuracy2(rssi3); Circle c1=new Circle(curScanedDevices.get(0).IbeaconLocateData.getX(),curScanedDevices.get(0).IbeaconLocateData.getY(), distance1); Circle c2=new Circle(curScanedDevices.get(1).IbeaconLocateData.getX(),curScanedDevices.get(1).IbeaconLocateData.getY(), distance2); Circle c3=new Circle(curScanedDevices.get(2).IbeaconLocateData.getX(),curScanedDevices.get(2).IbeaconLocateData.getY(), distance3); ThreeCircleIntersect threeCircleIntersect = new ThreeCircleIntersect(c1, c2, c3); Point point=threeCircleIntersect.GetThreeCircleIntersectPoint2(threeCircleIntersect.threeCirclePoints()); return point; } } //获取定位坐标 public static Point getLocateXY2(List<BluetoothLeDevice> curScanedDevices) { List<Circle> lstCircles=getLocateCircles(curScanedDevices);//获取用于定位的信标节点 Point resultLocatePoint=getLocateXY1(lstCircles,curScanedDevices);//定位坐标 if (resultLocatePoint==null&&lstCircles!=null&&lstCircles.size()>=3) { resultLocatePoint=getLocateXY1(lstCircles.subList(0,2),curScanedDevices.subList(0,2));//定位坐标 } if (lstCircles!=null&&lstCircles.size()>=3&&resultLocatePoint!=null)//修正坐标 { Triangle triangle = new Triangle(new Point(lstCircles.get(0).getX(),lstCircles.get(0).getY()) ,new Point (lstCircles.get(1).getX(),lstCircles.get(1).getY()),new Point(lstCircles.get(2).getX(),lstCircles.get(2).getY())); boolean isInOuterCircle=triangle.isPointInOuterCircle(resultLocatePoint);//定位坐标是否在信标节点的外接圆内 if (!isInOuterCircle) { Point near2SidePoint=getNearst2Side2IbeaconsMiddlePoint(curScanedDevices); if (near2SidePoint!=null) { return new Point(near2SidePoint.X,near2SidePoint.Y); } int rssi=curScanedDevices.get(0).getRssi(); double distance = RSSIDistanceUitl.calculateAccuracy2(rssi); Circle c1=new Circle(curScanedDevices.get(0).IbeaconLocateData.getX(),curScanedDevices.get(0).IbeaconLocateData.getY(), distance); return new Point(c1.getX(),c1.getY()); } } return resultLocatePoint; } private static Point getNearst2Side2IbeaconsMiddlePoint(List<BluetoothLeDevice> curScanedDevices) { if (curScanedDevices==null||curScanedDevices.isEmpty()||curScanedDevices.size()<2) { return null; } List<BluetoothLeDevice> aSideDevices=new ArrayList<>(); List<BluetoothLeDevice> bSideDevices=new ArrayList<>(); for (BluetoothLeDevice leDevice:curScanedDevices) { if (leDevice.IbeaconLocateData!=null) { if (leDevice.IbeaconLocateData.getR()==0) { aSideDevices.add(leDevice); } else if (leDevice.IbeaconLocateData.getR()==1) { bSideDevices.add(leDevice); } } } if (aSideDevices.isEmpty()||bSideDevices.isEmpty()) { return null; } else { class Brother2SideIbeacons { public BluetoothLeDevice aSideIbeacon; public BluetoothLeDevice bSideIbeacon; public double distancePower; public Brother2SideIbeacons(BluetoothLeDevice l,BluetoothLeDevice ll,double disPower) { this.aSideIbeacon=l; this.bSideIbeacon=ll; this.distancePower=disPower; } } List<Brother2SideIbeacons> brother2SideIbeacons=new ArrayList<>(); for (BluetoothLeDevice l:aSideDevices) { for (BluetoothLeDevice ll:bSideDevices) { double disPower=(l.IbeaconLocateData.getX()-ll.IbeaconLocateData.getX())*(l.IbeaconLocateData.getX()-ll.IbeaconLocateData.getX()) +(l.IbeaconLocateData.getY()-ll.IbeaconLocateData.getY())*(l.IbeaconLocateData.getY()-ll.IbeaconLocateData.getY()); Brother2SideIbeacons brother2SideIbeacons1=new Brother2SideIbeacons(l,ll,disPower); brother2SideIbeacons.add(brother2SideIbeacons1); } } if (!brother2SideIbeacons.isEmpty()) { Brother2SideIbeacons nearst2SideIbeacon=brother2SideIbeacons.get(0); int rssi2sideadded=brother2SideIbeacons.get(0).aSideIbeacon.getRssi()+brother2SideIbeacons.get(0).bSideIbeacon.getRssi(); for (Brother2SideIbeacons bro:brother2SideIbeacons ) { int rssi=bro.aSideIbeacon.getRssi()+bro.bSideIbeacon.getRssi(); if (rssi>rssi2sideadded) { nearst2SideIbeacon=bro; } } if (nearst2SideIbeacon!=null) { double middleX=(nearst2SideIbeacon.aSideIbeacon.IbeaconLocateData.getX()+nearst2SideIbeacon.bSideIbeacon.IbeaconLocateData.getX())/2; double middleY=(nearst2SideIbeacon.aSideIbeacon.IbeaconLocateData.getY()+nearst2SideIbeacon.bSideIbeacon.IbeaconLocateData.getY())/2; return new Point(middleX,middleY); } } } return null; } private static Point getLocateXY1(List<Circle> circles,List<BluetoothLeDevice> curScanedDevices) { if (circles==null||circles.size()==0) { return null; } else if (circles.size()<=2||curScanedDevices.size()<=2) { if (circles.size()==2||curScanedDevices.size()==2) { Point near2SidePoint=getNearst2Side2IbeaconsMiddlePoint(curScanedDevices); if (near2SidePoint!=null) { return new Point(near2SidePoint.X,near2SidePoint.Y); } } return new Point(circles.get(0).getX(),circles.get(0).getY()); } else { ThreeCircleIntersect threeCircleIntersect = new ThreeCircleIntersect(circles.get(0), circles.get(1), circles.get(2)); Circle bothIntersectC = CirIntersect.getBothIntersectCircles(circles.get(0),circles.get(1),circles.get(2)); if (bothIntersectC!=null&&!CirIntersect.siAllCirclesIntersect(circles.get(0),circles.get(1),circles.get(2))) { Point point = threeCircleIntersect.GetThreeCircleIntersectPoint2(threeCircleIntersect.oneCircleIntersecttwoCircles()); return point; } if (CirIntersect.siAllCirclesIntersect(circles.get(0),circles.get(1),circles.get(2))) { Point point = threeCircleIntersect.GetThreeCircleIntersectPoint2(threeCircleIntersect.threeCirclePoints()); return point; } return null; } } private static List<Circle> getLocateCircles(List<BluetoothLeDevice> curScanedDevices) { List<Circle> resultC=new ArrayList<>(); if (curScanedDevices==null||curScanedDevices.size()==0) { return null; } else if (curScanedDevices.size()==1) { int rssi=curScanedDevices.get(0).getRssi(); double distance = RSSIDistanceUitl.calculateAccuracy2(rssi); Circle c1=new Circle(curScanedDevices.get(0).IbeaconLocateData.getX(),curScanedDevices.get(0).IbeaconLocateData.getY(), distance); resultC.add(c1); return resultC; } else { for (BluetoothLeDevice l : curScanedDevices) { int rssi=l.getRssi(); double distance = RSSIDistanceUitl.calculateAccuracy2(rssi); Circle c1=new Circle(l.IbeaconLocateData.getX(),l.IbeaconLocateData.getY(), distance); resultC.add(c1); } return CirIntersect.getLocateCircles(resultC); } } public static List<BluetoothLeDevice> GetSortedDevices(BluetoothLeDeviceStore leDeviceStore) { List<BluetoothLeDevice> bluetoothLeDevices=new ArrayList<>(); List<BluetoothLeDevice> beforeSortDevices = leDeviceStore.getDeviceList(); BluetoothLeDevice tempSortDevice; if (beforeSortDevices.size()>1) { for (int i = 0; i < beforeSortDevices.size() - 1; i++) { for (int j = i+1; j < beforeSortDevices.size(); j++) { if (beforeSortDevices.get(i).getRssi()<beforeSortDevices.get(j).getRssi()) { tempSortDevice=beforeSortDevices.get(i); beforeSortDevices.set(i,beforeSortDevices.get(j)); beforeSortDevices.set(j,tempSortDevice); } } } } for (int i=0;i<beforeSortDevices.size();i++) { bluetoothLeDevices.add(beforeSortDevices.get(i)); } return bluetoothLeDevices; } }