# Analysis: FPS/Update Frequency of updatePosition & updateArrowRotation
## From backup versions v1.0 through v3.60

## HOW updatePosition AND updateArrowRotation WERE STRUCTURED

### v1.5 (Leaflet) — The ONLY version with explicit FPS control
- **`updatePosition(lat, lng)`** (line 552): sets userLat/Lng, moves L.marker, snaps to route when navigating, centers map
- **No `updateArrowRotation` function** — instead uses **`applyArrowTransform(heading, cameraHeading, rotationOffset)`** (line 385) which applies CSS transform rotation to the arrow DivIcon
- **MAP ENGINE**: Leaflet (L.map, L.marker, L.divIcon)
- **Navigation**: Has full `animationLoop` at lines 750-765

### v1.6 through v3.60 (MapLibre) — GPS-driven, no explicit FPS
- **`updatePosition(lat, lng)`** (varies by version, ~line 82-90):
  ```js
  function updatePosition(lat, lng) {
    userLat = lat; userLng = lng;
    recalcLat = lat; recalcLng = lng;
    if (!markerArrow) {
      // create arrow HTML marker
      markerArrow = new maplibregl.Marker({...}).setLngLat([lng, lat]).addTo(map);
    } else {
      markerArrow.setLngLat([lng, lat]);
    }
    // Rotation handled by updateArrowRotation - don't clobber it here
    hasGps = true;
  }
  ```
  (The rotation comment was added starting in v2.5/v3.0)

- **`updateArrowRotation(heading, cameraHeading, offset)`**:
  ```js
  function updateArrowRotation(heading, cameraHeading, offset) {
    var angle = heading;
    if (cameraHeading) angle = angle - cameraHeading;
    angle += offset || 0;
    if (markerArrow) {
      // v1.6-v3.53: markerArrow.getElement().style.transform = "rotate("+angle+"deg)";
      // v3.60+:       markerArrow.setRotation(angle);
    }
  }
  ```

- **MAP ENGINE**: MapLibre GL JS (maplibregl.Map, maplibregl.Marker)

---

## WHAT FREQUENCY THEY RAN AT

### v1.5 (Leaflet) — 20 Hz animation loop
```
// ANIMATION LOOP (20 Hz)
function animationLoop(timestamp) {
  if (!isNavigating) return;
  var elapsed = timestamp - lastAnimTime;
  // Target ~50ms per frame (20 Hz)
  if (elapsed >= 50) {
    lastAnimTime = timestamp - (elapsed % 50);
    tickNavigation();
  }
  animFrameId = requestAnimationFrame(animationLoop);
}
```
- Uses `requestAnimationFrame` throttled to **20 FPS** (50ms min interval)
- Loop only runs during active navigation (started by `startNavigation()`)
- Calls `tickNavigation()` which does snap-to-route, remaining distance, instruction updates, nav UI updates
- Arrow transforms applied reactively via ObjC → `updatePosition()` / `applyArrowTransform()` calls

### v1.6 through v3.60 — Purely GPS-driven, NO animation loop
- **No `requestAnimationFrame`, no `setInterval` for position/arrow updates**
- **`updatePosition()` is called from iOS CLLocationManager delegate:**
  ```
  locationManager:didUpdateLocations:
  → snap to route (if navigating)
  → evaluateJavaScript("updatePosition(lat, lng)")
  → applyCameraSettings() if userTracking and moving > 2 m/s
  ```

- **`updateArrowRotation()` is called from iOS heading delegate:**
  ```
  locationManager:didUpdateHeading:
  → evaluateJavaScript("updateArrowRotation(heading, cameraHeading, offset)")
  ```

- **iOS CLLocationManager settings (identical across ALL versions v1.6-v3.60):**
  ```
  self.locationManager.desiredAccuracy = kCLLocationAccuracyBestForNavigation;
  self.locationManager.distanceFilter = 1.0;
  self.locationManager.headingFilter = 5.0;
  ```
  - **GPS location update rate**: iOS decides, typically **1Hz** (standard) to **~5-10Hz** (BestForNavigation, when GPS has good signal)
  - **Heading update rate**: Every **5 degrees** change (headingFilter=5.0), which at walking pace might be several seconds, at driving pace could be several times per second

- **Navigation timer (JS side)**: `setInterval(navTick, 1000)` — **1 second**, only for nav calculations, not for position/arrow

- **Navigation timer (ObjC side, v3.24+)** : `[NSTimer scheduledTimerWithTimeInterval:2.0 ...]` — **2 seconds**, polls JS for nav state

---

## COMMENTS ABOUT FPS, RATE, FREQUENCY, SIMULATION TIMING

| Version | Location | Comment |
|---------|----------|---------|
| **v1.5** | line 751 | `// ANIMATION LOOP (20 Hz)` |
| **v1.5** | line 758 | `// Target ~50ms per frame (20 Hz)` |
| **v2.5+** | map.html ~lines 102-103 | `// Rotation is handled by updateArrowRotation - don't clobber it here` / `// Just update position, rotation set separately` |
| **v3.60** | MapView 861-866 | `// BUGFIX: da fermo in navigazione NON aggiornare _cameraHeading — evitava rotazione di 30° ogni 2s!` |
| **v3.60** | MapView 1176 | `_navTimer = [NSTimer scheduledTimerWithTimeInterval:2.0 ...]` — comment: `// Timer 2-secondi — ottiene stato navigazione da JS e aggiorna UI` |
| **All v1.6-v3.60** | MapView ivars | `NSTimer *_arrowTimer;` — **DECLARED but NEVER CREATED/USED**. This dead ivar exists across all versions. |

---

## KEY FINDINGS

1. **The 20 Hz animation loop was ONLY in v1.5 (Leaflet version)**. It was a `requestAnimationFrame` loop throttled to 50ms. This was removed when switching to MapLibre in v1.6.

2. **From v1.6 through v3.60, there is NO throttling or FPS control** for `updatePosition` or `updateArrowRotation`. Updates are purely event-driven by iOS CLLocationManager at whatever rate the hardware delivers GPS data.

3. **The `_arrowTimer` variable is declared but never used** in any version v1.6-v3.60. It's dead code that was never wired up.

4. **All `updatePosition`/`updateArrowRotation` JS functions are identical in structure** from v1.6 through v3.60. The only change in v3.60+ is using `markerArrow.setRotation(angle)` instead of manually setting `style.transform`.

5. **No timing-related experiments were attempted** in the v1.0-v3.60 backup range — the transition from Leaflet→MapLibre also removed the only animation loop. Any FPS throttling experiments must have happened AFTER v3.60 (which is the latest in the backup).

6. **Navigation timer evolution**: 
   - JS side: always `setInterval(navTick, 1000)` (1 second)
   - ObjC side: v3.24+ added a native `_navTimer` at 2-second intervals for polling nav state
