#!/usr/bin/env python3 """ Capture raw NTRIP stream to binary file - no parsing, just raw bytes. """ import base64 import socket import time from datetime import datetime # NTRIP configuration CASTER_HOST = "truertk.pointonenav.com" CASTER_PORT = 2101 MOUNTPOINT = "AUTO" USERNAME = "9t7fwfbm57" PASSWORD = "96m7bec9g8" LAT = 36.1140884 LON = -97.0880663 ALT = 390.0 def build_gga(lat: float, lon: float, alt: float) -> bytes: """Build NMEA GGA sentence.""" now_utc = datetime.utcnow().strftime("%H%M%S") lat_hemi = "N" if lat >= 0 else "S" lat_abs = abs(lat) lat_deg = int(lat_abs) lat_min = (lat_abs - lat_deg) * 60.0 lat_str = f"{lat_deg:02d}{lat_min:07.4f}" lon_hemi = "E" if lon >= 0 else "W" lon_abs = abs(lon) lon_deg = int(lon_abs) lon_min = (lon_abs - lon_deg) * 60.0 lon_str = f"{lon_deg:03d}{lon_min:07.4f}" fields = ["GPGGA", now_utc, lat_str, lat_hemi, lon_str, lon_hemi, "1", "12", "1.0", f"{alt:.1f}", "M", "", "M", "", ""] core = ",".join(fields) checksum = 0 for char in core: checksum ^= ord(char) return f"${core}*{checksum:02X}\r\n".encode("ascii") def make_ntrip_request(host: str, port: int, mount: str, user: str, password: str) -> bytes: """Create NTRIP v2 HTTP request.""" auth = base64.b64encode(f"{user}:{password}".encode("utf-8")).decode("ascii") req = ( f"GET /{mount} HTTP/1.1\r\n" f"Host: {host}:{port}\r\n" f"Ntrip-Version: Ntrip/2.0\r\n" f"User-Agent: NTRIP-Raw-Capture/1.0\r\n" f"Connection: close\r\n" f"Authorization: Basic {auth}\r\n\r\n" ) return req.encode("ascii") def main(): timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") output_file = f"ntrip_raw_{timestamp}.bin" log_file = f"ntrip_log_{timestamp}.txt" print(f"Raw NTRIP Stream Capture") print(f"=" * 80) print(f"Caster: {CASTER_HOST}:{CASTER_PORT}/{MOUNTPOINT}") print(f"Output: {output_file}") print(f"Log: {log_file}") print(f"Duration: 60 seconds") print(f"=" * 80) print() last_gga_time = 0 start_time = time.monotonic() total_bytes = 0 try: # Connect print("Connecting...") sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.settimeout(30) sock.connect((CASTER_HOST, CASTER_PORT)) sock.sendall(make_ntrip_request(CASTER_HOST, CASTER_PORT, MOUNTPOINT, USERNAME, PASSWORD)) # Read HTTP headers header = b"" while b"\r\n\r\n" not in header: chunk = sock.recv(1) if not chunk: print("ERROR: Connection closed") return header += chunk if "200 OK" not in header.decode("iso-8859-1", errors="replace"): print(f"ERROR: Connection failed") print(header.decode("iso-8859-1", errors="replace")) return print("Connected! Capturing raw data stream...\n") # Open output files with open(output_file, 'wb') as binfile, open(log_file, 'w') as logfile: logfile.write(f"# NTRIP Raw Stream Capture Log\n") logfile.write(f"# Started: {datetime.now().isoformat()}\n") logfile.write(f"# Caster: {CASTER_HOST}:{CASTER_PORT}/{MOUNTPOINT}\n") logfile.write(f"#\n") logfile.write(f"# timestamp, elapsed_sec, bytes_received, total_bytes\n") # Capture loop - just write everything while time.monotonic() - start_time < 60: now = time.monotonic() elapsed = now - start_time # Send GGA every 10 seconds if now - last_gga_time >= 10: gga = build_gga(LAT, LON, ALT) sock.sendall(gga) ts = datetime.now().isoformat() logfile.write(f"{ts},{elapsed:.1f},GGA_SENT,{total_bytes}\n") logfile.flush() print(f"[{int(elapsed):3d}s] Sent GGA | Captured: {total_bytes:,} bytes") last_gga_time = now # Receive raw data data = sock.recv(4096) if not data: print("Connection closed by caster") break # Write raw data to file binfile.write(data) binfile.flush() chunk_len = len(data) total_bytes += chunk_len # Log this chunk ts = datetime.now().isoformat() logfile.write(f"{ts},{elapsed:.3f},{chunk_len},{total_bytes}\n") elapsed_final = time.monotonic() - start_time print(f"\n[{int(elapsed_final):3d}s] Capture complete!") except KeyboardInterrupt: print("\n\nInterrupted by user") except Exception as e: print(f"\nERROR: {e}") import traceback traceback.print_exc() finally: if 'sock' in locals(): try: sock.close() except: pass print(f"\n{'=' * 80}") print(f"CAPTURE COMPLETE") print(f"{'=' * 80}") print(f"Total bytes: {total_bytes:,}") print(f"Output file: {output_file}") print(f"Log file: {log_file}") print() print("To view the binary file:") print(f" hexdump -C {output_file} | head -100") print(f" xxd {output_file} | head -100") print() print("To view just the first 1000 bytes:") print(f" head -c 1000 {output_file} | hexdump -C") print() print("To search for RTCM message 208 (0xD3 header + type 208 = 0x0D0):") print(" Look for pattern: D3 ?? ?? 0D 0x") print(" where ?? ?? is the length field") if __name__ == "__main__": main()