New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Expose stream & packet data through the API #308
Conversation
Here's some sample code that demonstrates how to extract images from the "HERE Images" stream using the API: diff --git a/support/cli.py b/support/cli.py
index ca1322f..17559e9 100755
--- a/support/cli.py
+++ b/support/cli.py
@@ -4,6 +4,7 @@ import argparse
import logging
import os
import queue
+import struct
import sys
import threading
import wave
@@ -23,6 +24,7 @@ class NRSC5CLI:
self.iq_output = None
self.wav_output = None
self.hdc_output = None
+ self.here_image_data = bytes()
def parse_args(self):
parser = argparse.ArgumentParser(description="Receive NRSC-5 signals.")
@@ -177,6 +179,45 @@ class NRSC5CLI:
0xfc
])
+ def parse_here_image(self, data):
+ header = bytes([0xff, 0xf7, 0xff, 0xf7])
+ self.here_image_data = self.here_image_data + data
+ while True:
+ index = self.here_image_data.find(header)
+ if index == -1:
+ break
+ self.here_image_data = self.here_image_data[index:]
+ if len(self.here_image_data) < 6:
+ break
+
+ offset = 4
+
+ payload_length = struct.unpack(">H", self.here_image_data[offset:offset + 2])[0]
+ offset += 2
+ if len(self.here_image_data) < 8 + payload_length:
+ break
+
+ # unknown data
+ offset += 27
+
+ filename_len = self.here_image_data[offset]
+ offset += 1
+
+ filename = self.here_image_data[offset:offset + filename_len].decode()
+ offset += filename_len
+
+ # unknown data
+ offset += 4
+
+ file_length = struct.unpack(">H", self.here_image_data[offset:offset + 2])[0]
+ offset += 2
+
+ with open(filename, "wb") as f:
+ f.write(self.here_image_data[offset:offset + file_length])
+
+ self.here_image_data = self.here_image_data[8 + payload_length:]
+
+
def callback(self, evt_type, evt):
if evt_type == nrsc5.EventType.LOST_DEVICE:
logging.info("Lost device")
@@ -236,6 +277,8 @@ class NRSC5CLI:
elif evt_type == nrsc5.EventType.STREAM:
logging.info("Stream data: port=%04X mime=%s size=%s",
evt.port, evt.mime, len(evt.data))
+ if evt.mime == nrsc5.MIMEType.HERE_IMAGE:
+ self.parse_here_image(evt.data)
elif evt_type == nrsc5.EventType.PACKET:
logging.info("Packet data: port=%04X mime=%s size=%s",
evt.port, evt.mime, len(evt.data)) This particular stream seems to have the same sorts of traffic tiles & weather radar overlays that would be found in the "TTN STM" LOT files on other stations. |
Almost exactly like the iHeart data. This is awesome. |
Yep, it looks very similar, the data is just in a different place. In my collection of recordings, I found seven stations that were doing it this way. |
Some of the "
Yes, I had that idea as well. I might do that once we understand the stream data a bit better. |
Thought about this a bit after looking at Navteq/HERE behaviour. If the common timestamp isn't there, one way to try to ensure the image pieces are correctly synced is to start accumulating parts from 0_0, and ignore anything started in the middle of the cycle. Of course if you miss one piece out of the 9 due to a dropout or some other error, that'd have to be accounted for as well. The whole process takes about 5' to get all 9 pieces so it'd be a shame (and very inefficient) to throw away the whole thing just because one piece errored out. |
Took an initial stab at trying to ID parts of the unknown data sections. The second section is only 4 bytes long, but they are consistent. Traffic tiles are always \x00\x63\x00\x00, and weather overlays are always \x04\x21\x00\x00. Perhaps the first two bytes help identify what it is? I've attached the raw data for each 27-byte and 4-byte section from three sets of traffic images and three weather images. |
I did some analysis as well. Comments below:
It seems like the first nibble is always
For weather, these seem to be counters that go up by one each time the image changes.
Yep, looks like a standard Unix timestamp (seconds since January 1, 1970 UTC).
Yeah, 15-27 appear to be coordinates. I think the bits are split out like so: first bit - sign of latitude And then another set of 52 bits with the second set of coordinates. For weather it does appear to be the upper left and lower right corners that are encoded. For traffic, the coordinates are weird and seem to extend beyond where the tiles actually are. (Luckily, knowing the coordinates of the traffic tiles isn't so important.) |
Strangely, in my old recordings (2016-2017), the coordinates seem to be 26 bits, latitude * 200000 and longitude * 200000, with no sign bit. Maybe it was decided later that there should be a sign bit so that the other three quarters of the earth can be covered. :-) |
Given what we now know, it should be possible to update nrsc5-gui to decode & display the HERE Images stream. |
Interesting. I wondered if the 15th byte played a part, because it would make sense that the tiles would go from 0x0ef7 to 0x0f0c. Guess I was looking for symmetry. |
That's what I've been working with. I modified my v2 of -gui to generate the sample images above. |
A few more observations:
|
Having just worked out the bits, it would appear that the corners in my overlay would be 39.41620N, 77.45359W and 38.59220N, 76.62960W. So yes... makes sense - upper left and lower right. We should have Navteq/HERE's specs reverse engineered in no time.😂 |
Just in case anyone needs it, here is some Python code to pull the timestamp and map coordinates out of the received file:
My original intent was to call these individually within process_traffic_map and process_weather_overlay, but that's rather problematic: These work off the here_image_data buffer, which already may contain the next received traffic or weather image and would provide erratic results. So they really should be called within parse_here_image and have the information parked in a class global, as such:
Then, last_here_timestamp and last_here_bounds can be used within the traffic and weather image processing sections referenced above. |
There are three AAS data types used by HD Radio stations: Stream, Packet, and Large Object Transfer (LOT). Of these, only LOT files are currently exposed through the nrsc5 API. There is a bit of code in output.c aimed at decoding Stream data, but it is not functional.
Since there are various Stream data types (HERE TPEG, HERE Images, TTN TPEG) and Packet data types (HD TMC, NavteqPacketData1, NavteqAdmin) in use, I think it would make sense to expose the raw Stream & Packet payloads through the API, and make the consuming application responsible for decoding the data type(s) it cares about.
Here I've added
NRSC5_EVENT_STREAM
andNRSC5_EVENT_PACKET
event types to the API. Each of these reports the 16-bit port number, payload size, 32-bit MIME hash (taken from the corresponding entry in the SIG table), and payload data.For the moment, the C & Python CLI apps simply report that data is present.
C:
Python: