@David Davis yeah this is a pretty common pattern for multi-module builds and honestly the "just stuff it in the payload" approach isn't as janky as it sounds — a lot of field deployments do exactly that. Parse the $GPRMC or $GPZDA sentence (GPZDA is cleaner since it gives you full date+time in one place), extract the UTC time, and pack it as a Unix timestamp or raw epoch seconds into a fixed offset in your BLE packet header. Keep it consistent across all nodes and your base station aggregation becomes trivial.
The thing to watch out for is what happens in the window before your GNSS module gets a fix — at 18Hz you'll get a lock reasonably fast, but if you're powering on cold in a dense canopy situation that could take longer than you want. Worth having the firmware flag packets with a "time valid" bit so you can discard or quarantine anything that went out before the fix was confirmed rather than ending up with garbage timestamps in your dataset.
If you want something tighter than polling NMEA on every packet, you could set up a periodic sync where the firmware captures a GNSS timestamp once per second, stores it locally, and then offsets subsequent BLE packets using the MCU's internal timer ticks against that anchor — way less overhead than parsing NMEA on every transmission cycle.