TIP - Geolocalización en ordenadores sin adaptador WiFi

Colapsar
X
 
  • Filtrar
  • Tiempo
  • Mostrar
Limpiar Todo
nuevos mensajes
  • jmtella
    Administrator
    • Nov
    • 21508

    TIP - Geolocalización en ordenadores sin adaptador WiFi

    Como sabeis, la geolocalización en windows se basa en varios metodos:

    * Si existe chip de geolocalizacion, usará este.
    * Pero en ordenadores sin el chip, se usan varios metodos basados en la IP.

    En este segundo caso, es donde pueden existir problemas y no lo caliza bien la geolocalizacion, Por ejemplo, en un ordenador de sobremesa, estando en Madrid. a mi me dice:

    Código:
    === Prueba de geolocalización Windows (WinRT) ===
    Modo: adaptive | Presupuesto total: 20s
    Solicitando una posición...
    
    Acceso del sistema: ALLOWED
    Estado inicial del proveedor: NOT_INITIALIZED
    ✔ Posición obtenida:
      Timestamp:       2026-01-14 09:13:38
      Lat, Lon:        53.348700, -6.260700
      Precisión (m):   10000.0
      Altitud (m):     0.0
      Fuente:          IP_ADDRESS
      Estado proveedor:NOT_INITIALIZED
      Modo usado:      HIGH (intentos=2, 0.377s)
    Lo cual es incorrecto, ya que corresponde a una localizacion cercana a Dublin. Cualquier navegador (Google maaps por ejemplo) da la situacion invorrecta.

    Pero si dicho ordenador le ponemos un adaptador WiFi (aunque esté sin conectar a WiFi -esto es lo importante) ya usa otros metodos

    Código:
    === Prueba de geolocalización Windows (WinRT) ===
    Modo: adaptive | Presupuesto total: 20s
    Solicitando una posición...
    
    Acceso del sistema: ALLOWED
    Estado inicial del proveedor: NOT_INITIALIZED
    ✔ Posición obtenida:
      Timestamp:       2026-01-14 09:13:58
      Lat, Lon:        40.435924, -3.701747
      Precisión (m):   81.0
      Altitud (m):     0.0
      Fuente:          WI_FI
      Estado proveedor:NOT_INITIALIZED
      Modo usado:      DEFAULT (intentos=1, 0.134s)
    La cual es la correcta.
    • Antes: IP_ADDRESS, 10000 m, ~0.37s → fallback por IP.
    • Ahora con el dongle Wi-Fi “en el aire”: WI_FI, ~81 m, ~0.13s → triangulación por BSSID (aunque sin asociar a ninguna una red).
    Si se quiere dejarlo “redondo” en sobremesas:
    • Mantener el adaptador Wi-Fi habilitado (aunque no uses Wi-Fi para internet), o
    • Definir una Ubicación predeterminada en Windows como plan B cuando no haya señales (útil si algún día se deshabilita el dongle).

    El programa python para las salidas anteriores es el que dejo debajo en el spoiler.

    Es necesariop tener instalado el winsdk;

    pip install winsdk

    Pero para que lo compiler el pip es necesario igualmente tener instalado el visual code 2022 ya que si no no compila.

    Spoiler: mostrar

    Código:
    #!/usr/bin/env python3
    # geo.py — Test de geolocalización WinRT (Windows) con modo adaptativo opcional.
    # Uso rápido:
    #   python geo.py
    #   python geo.py --help
    
    import sys, os, math, asyncio, datetime, time
    import argparse
    
    # ---- Comprobación winsdk ----------------------------------------------
    try:
        from winsdk.windows.devices.geolocation import (
            Geolocator,
            PositionAccuracy,
            PositionStatus,
            GeolocationAccessStatus,
        )
    except Exception:
        print("Falta el paquete 'winsdk'. Instálalo con: pip install winsdk")
        sys.exit(1)
    
    # ---- Utilidades --------------------------------------------------------
    def status_name(s):
        m = {
            PositionStatus.NOT_INITIALIZED: "NOT_INITIALIZED",
            PositionStatus.NO_DATA: "NO_DATA",
            PositionStatus.DISABLED: "DISABLED",
            PositionStatus.NOT_AVAILABLE: "NOT_AVAILABLE",
            PositionStatus.READY: "READY",
            PositionStatus.INITIALIZING: "INITIALIZING",
        }
        return m.get(s, str(s))
    
    def access_name(a):
        m = {
            GeolocationAccessStatus.ALLOWED: "ALLOWED",
            GeolocationAccessStatus.DENIED: "DENIED",
            GeolocationAccessStatus.UNSPECIFIED: "UNSPECIFIED",
        }
        return m.get(a, str(a))
    
    def _nan_to_none(x):
        if x is None:
            return None
        try:
            return None if (isinstance(x, float) and math.isnan(x)) else x
        except Exception:
            return x
    
    def src_name(c):
        src = getattr(c, "position_source", None)
        return getattr(src, "name", src)
    
    async def watch_status(geo, verbose: bool, seconds=20):
        if not verbose:
            return
        last = None
        for _ in range(int(seconds * 2)):
            s = status_name(geo.location_status)
            if s != last:
                print(f"[{datetime.datetime.now().strftime('%H:%M:%S')}] Estado proveedor: {s}")
                last = s
            await asyncio.sleep(0.5)
    
    # ---- Intentos ----------------------------------------------------------
    async def _try_default(geo: Geolocator, t: int, max_age_s: int, target_acc_m: float):
        start = time.perf_counter()
        geo.desired_accuracy = PositionAccuracy.DEFAULT
        if hasattr(geo, "desired_accuracy_in_meters"):
            try:
                # “Pista” para que se quede en proveedor de red si vale
                geo.desired_accuracy_in_meters = max(int(target_acc_m), 100)
            except Exception:
                pass
        try:
            if max_age_s > 0:
                from datetime import timedelta
                pos = await asyncio.wait_for(
                    geo.get_geoposition_async(timedelta(seconds=max_age_s), timedelta(seconds=t)),
                    timeout=t + 1
                )
            else:
                pos = await asyncio.wait_for(geo.get_geoposition_async(), timeout=t)
            return pos, time.perf_counter() - start, None
        except Exception as e:
            return None, time.perf_counter() - start, e
    
    async def _try_high(geo: Geolocator, t: int):
        start = time.perf_counter()
        geo.desired_accuracy = PositionAccuracy.HIGH
        try:
            pos = await asyncio.wait_for(geo.get_geoposition_async(), timeout=t)
            return pos, time.perf_counter() - start, None
        except Exception as e:
            return None, time.perf_counter() - start, e
    
    def pack_result(geo: Geolocator, pos, mode_used: str, attempts: int, elapsed_s: float):
        c = pos.coordinate
        p = c.point.position
        data = {
            "timestamp": getattr(c, "timestamp", None),
            "latitude": p.latitude,
            "longitude": p.longitude,
            "altitude_m": _nan_to_none(getattr(p, "altitude", None)),
            "accuracy_m": getattr(c, "accuracy", None),
            "altitude_accuracy_m": getattr(c, "altitude_accuracy", None),
            "speed_m_s": getattr(c, "speed", None),
            "heading_deg": getattr(c, "heading", None),
            "source": src_name(c),
            "provider_status": status_name(getattr(geo, "location_status", None)),
            "mode_used": mode_used,
            "attempts": attempts,
            "elapsed_s": round(elapsed_s, 3),
        }
        return data
    
    # ---- Lógica principal adaptativa ---------------------------------------
    async def get_position(mode: str, timeout_total: int, t_default: int, t_high: int,
                           target_acc_m: float, max_age_s: int, warmup: float,
                           verbose: bool, no_escalate: bool):
        # Acceso
        access = await Geolocator.request_access_async()
        print(f"Acceso del sistema: {access_name(access)}")
        if access != GeolocationAccessStatus.ALLOWED:
            return None, 2, "Acceso denegado por Windows (habilita Ubicación para apps de escritorio)."
    
        geo = Geolocator()
        print(f"Estado inicial del proveedor: {status_name(geo.location_status)}")
    
        if warmup > 0:
            # Pequeño warm-up (especialmente útil si hay WWAN/GNSS)
            status_task = asyncio.create_task(watch_status(geo, verbose, seconds=max(warmup, 3)))
            await asyncio.sleep(min(max(warmup, 0.5), 10.0))
            status_task.cancel()
    
        # Modo forzado
        if mode == "high":
            pos, dt, err = await _try_high(geo, max(1, timeout_total))
            if pos:
                return pack_result(geo, pos, "HIGH", 1, dt), 0, None
            return None, 3, f"Timeout/err en HIGH ({timeout_total}s): {repr(err)}"
    
        if mode == "default":
            pos, dt, err = await _try_default(geo, max(1, timeout_total), max_age_s, target_acc_m)
            if pos:
                return pack_result(geo, pos, "DEFAULT", 1, dt), 0, None
            return None, 3, f"Timeout/err en DEFAULT ({timeout_total}s): {repr(err)}"
    
        # Modo adaptativo
        attempts = 0
        t_remain = max(1, timeout_total)
    
        # Reparto de tiempos si no se especificaron
        if t_default <= 0 and t_high <= 0:
            t_default = max(1, min(t_remain - 1, int(t_remain * 0.6)))
            t_high = max(0, t_remain - t_default)
    
        # 1) DEFAULT
        td = min(t_default if t_default > 0 else t_remain, t_remain)
        attempts += 1
        if verbose:
            print(f"[ADAPT] DEFAULT (t={td}s, max_age={max_age_s}s, target_acc={target_acc_m}m)")
        pos, dt1, err1 = await _try_default(geo, td, max_age_s, target_acc_m)
        t_remain -= td
        if pos:
            c = pos.coordinate
            acc = getattr(c, "accuracy", 9999) or 9999
            src = src_name(c)
            if verbose:
                print(f"[ADAPT] DEFAULT → src={src}, acc={acc}m")
            if no_escalate or acc <= target_acc_m or src in ("WIFI", "GNSS") or t_remain <= 0:
                return pack_result(geo, pos, "DEFAULT", attempts, dt1), 0, None
    
        # 2) HIGH (si queda presupuesto)
        if t_remain > 0 and not no_escalate:
            th = min(t_high if t_high > 0 else t_remain, t_remain)
            attempts += 1
            if verbose:
                print(f"[ADAPT] HIGH (t={th}s)")
            pos2, dt2, err2 = await _try_high(geo, th)
            if pos2:
                return pack_result(geo, pos2, "HIGH", attempts, dt1 + dt2), 0, None
            return None, 3, f"Timeout/err: DEFAULT({td}s) y HIGH({th}s). " \
                            f"err_default={repr(err1)} err_high={repr(err2)}"
    
        # Sin presupuesto tras DEFAULT
        return None, 3, f"Timeout/err en DEFAULT ({td}s): {repr(err1)}"
    
    # ---- CLI ----------------------------------------------------------------
    def parse_args():
        p = argparse.ArgumentParser(
            description="Prueba de geolocalización Windows (WinRT) con modo adaptativo."
        )
        p.add_argument("--mode", choices=["adaptive", "default", "high"], default="adaptive",
                       help="Estrategia de obtención. Por defecto: adaptive.")
        p.add_argument("--timeout", type=int, default=20,
                       help="Presupuesto total de tiempo en segundos (por defecto 20).")
        p.add_argument("--t-default", type=int, default=0,
                       help="Tiempo (s) para DEFAULT dentro del modo adaptativo. Si 0, se reparte automáticamente.")
        p.add_argument("--t-high", type=int, default=0,
                       help="Tiempo (s) para HIGH dentro del modo adaptativo. Si 0, se reparte automáticamente.")
        p.add_argument("--target-acc", type=float, default=150.0,
                       help="Precisión objetivo en metros para considerar suficiente DEFAULT (por defecto 150).")
        p.add_argument("--max-age", type=int, default=0,
                       help="Aceptar caché de hasta X segundos en DEFAULT (por defecto 0 = solo posiciones frescas).")
        p.add_argument("--warmup", type=float, default=0.0,
                       help="Warm-up en segundos antes de pedir posición (p.ej. 3–6s si hay WWAN).")
        p.add_argument("--no-escalate", action="store_true",
                       help="No escalar a HIGH nunca (solo DEFAULT).")
        p.add_argument("--json", action="store_true",
                       help="Salida en JSON (una línea).")
        p.add_argument("--verbose", "-v", action="store_true",
                       help="Más trazas (estados de proveedor, etapas adaptativas).")
        # Nuevo: solo <3.14, por si quieres forzar selector legacy
        p.add_argument("--force-selector", action="store_true",
                       help="(Solo <3.14) Forzar WindowsSelectorEventLoopPolicy. No usar salvo necesidad.")
        return p.parse_args()
    
    def _maybe_force_selector(force_flag: bool):
        """Evita deprecations en 3.14+: solo fuerza selector en <3.14 si el usuario lo pide
           (flag --force-selector o env GEO_FORCE_SELECTOR=1)."""
        try:
            if not sys.platform.startswith("win"):
                return
            if sys.version_info >= (3, 14):
                return  # nada que hacer en 3.14+
            want = force_flag or os.getenv("GEO_FORCE_SELECTOR") in ("1","true","TRUE")
            if want and hasattr(asyncio, "WindowsSelectorEventLoopPolicy"):
                asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())  # ok en <3.14
        except Exception:
            pass
    
    def main():
        if not sys.platform.startswith("win"):
            print("Este test solo está soportado en Windows.")
            sys.exit(1)
    
        args = parse_args()
        # En 3.13.x funcionará sin tocar la policy; solo si lo pides explícitamente:
        _maybe_force_selector(args.force_selector)
    
        if not args.json:
            print("=== Prueba de geolocalización Windows (WinRT) ===")
            print(f"Modo: {args.mode} | Presupuesto total: {args.timeout}s\nSolicitando una posición...\n")
    
        data, code, err = asyncio.run(get_position(
            mode=args.mode,
            timeout_total=args.timeout,
            t_default=args.t_default,
            t_high=args.t_high,
            target_acc_m=args.target_acc,
            max_age_s=args.max_age,
            warmup=args.warmup,
            verbose=args.verbose,
            no_escalate=args.no_escalate
        ))
    
        if err:
            if args.json:
                import json
                print(json.dumps({"ok": False, "error": err, "code": code}, default=str))
            else:
                print("No se pudo obtener posición.")
                print("Motivo:", err)
                print("""
    Pistas para habilitarlo:
      1) Configuración → Privacidad y seguridad → Ubicación:
         • Activar "Servicios de ubicación".
         • Activar "Permitir que las aplicaciones de escritorio accedan a tu ubicación".
      2) Servicio de Windows "Geolocalización" (lfsvc) en ejecución.
      3) En Windows Server: comprobar directivas en gpedit.msc
         (Equipo → Plantillas administrativas → Componentes de Windows → 'Ubicación y sensores').
      4) Si el equipo tiene SIM/WWAN y no hay GNSS, prueba con --mode adaptive o --no-escalate,
         o añade --warmup 4 y acepta caché con --max-age 300.
    """)
            sys.exit(code or 2)
    
        # Éxito
        if args.json:
            import json
            print(json.dumps({"ok": True, "data": data}, default=str))
        else:
            print("✔ Posición obtenida:")
            ts = data["timestamp"]
            if isinstance(ts, datetime.datetime):
                ts_str = ts.strftime("%Y-%m-%d %H:%M:%S")
            else:
                ts_str = str(ts)
            print(f"  Timestamp:       {ts_str}")
            print(f"  Lat, Lon:        {data['latitude']:.6f}, {data['longitude']:.6f}")
            print(f"  Precisión (m):   {data['accuracy_m']}")
            print(f"  Altitud (m):     {data['altitude_m']}")
            print(f"  Fuente:          {data['source']}")
            print(f"  Estado proveedor:{data['provider_status']}")
            print(f"  Modo usado:      {data['mode_used']} (intentos={data['attempts']}, {data['elapsed_s']}s)")
    
    if __name__ == "__main__":
        main()
    ​
    ​

  • noSign
    Super Moderator
    • Dec
    • 4915

    #2
    Prefiero no estar geolocalizado, hay mucho loco suelto.
    Suelo cambiar la IP

    Comentario

    Trabajando...
    X