1
1
import math
2
+ import struct
3
+ import sys
2
4
3
5
from os1 .packet import (
4
6
AZIMUTH_BLOCK_COUNT ,
5
7
CHANNEL_BLOCK_COUNT ,
6
8
azimuth_angle ,
7
9
azimuth_block ,
10
+ azimuth_measurement_id ,
11
+ azimuth_timestamp ,
8
12
azimuth_valid ,
9
13
channel_block ,
10
14
channel_range ,
11
15
unpack ,
12
16
)
13
17
18
+ # The OS-16 will still contain 64 channels in the packet, but only
19
+ # every 4th channel starting at the 2nd will contain data .
20
+ OS_16_CHANNELS = (2 , 6 , 10 , 14 , 18 , 22 , 26 , 30 , 34 , 42 , 46 , 50 , 54 , 58 , 62 )
21
+ OS_64_CHANNELS = tuple (i for i in range (CHANNEL_BLOCK_COUNT ))
22
+
14
23
15
24
class UninitializedTrigTable (Exception ):
16
25
def __init__ (self ):
@@ -49,11 +58,21 @@ def xyz_point(channel_n, azimuth_block):
49
58
y = range * table_entry [1 ] * math .sin (adjusted_angle )
50
59
z = range * table_entry [0 ]
51
60
52
- return x , y , z
61
+ return [ x , y , z ]
53
62
54
63
55
- def xyz_points (packet ):
56
- """3D cartesian x, y, z coordinates."""
64
+ def xyz_points (packet , os16 = False ):
65
+ """
66
+ Returns a tuple of x, y, z points where each x, y, z is a list of
67
+ all the x, y or z points in the packet
68
+
69
+ (
70
+ [x1, x2, ...],
71
+ [y1, y2, ...],
72
+ [z1, z2, ...],
73
+ )
74
+ """
75
+ channels = OS_16_CHANNELS if os16 else OS_64_CHANNELS
57
76
if not isinstance (packet , tuple ):
58
77
packet = unpack (packet )
59
78
@@ -67,9 +86,96 @@ def xyz_points(packet):
67
86
if not azimuth_valid (block ):
68
87
continue
69
88
70
- for c in range ( CHANNEL_BLOCK_COUNT ) :
89
+ for c in channels :
71
90
point = xyz_point (c , block )
72
91
x .append (point [0 ])
73
92
y .append (point [1 ])
74
93
z .append (point [2 ])
75
94
return x , y , z
95
+
96
+
97
+ def xyz_columns (packet , os16 = False ):
98
+ """
99
+ Similar to xyz_points except the x, y, z values are ordered by
100
+ column. This is convenient if you only want to render a specific
101
+ column.
102
+
103
+ The structure of it will be be columns containing channels. It
104
+ looks like...
105
+
106
+ [
107
+ # column 1
108
+ [
109
+ [channel1_x, channel2_x, ...],
110
+ [channel1_y, channel2_y, ...],
111
+ [channel1_z, channel2_z, ...],
112
+ ],
113
+ # column 2
114
+ [
115
+ [channel1_x, channel2_x, ...],
116
+ [channel1_y, channel2_y, ...],
117
+ [channel1_z, channel2_z, ...],
118
+ ],
119
+ ]
120
+ """
121
+ channels = OS_16_CHANNELS if os16 else OS_64_CHANNELS
122
+ if not isinstance (packet , tuple ):
123
+ packet = unpack (packet )
124
+
125
+ points = []
126
+ for b in range (AZIMUTH_BLOCK_COUNT ):
127
+ block = azimuth_block (b , packet )
128
+ x = []
129
+ y = []
130
+ z = []
131
+
132
+ for channel in channels :
133
+ point = xyz_point (channel , block )
134
+ x .append (point [0 ])
135
+ y .append (point [1 ])
136
+ z .append (point [2 ])
137
+ points .append ([x , y , z ])
138
+ return points
139
+
140
+
141
+ _unpack = struct .Struct ("<I" ).unpack
142
+
143
+
144
+ def peek_encoder_count (packet ):
145
+ return _unpack (packet [12 :16 ])[0 ]
146
+
147
+
148
+ def frame_handler (queue ):
149
+ """
150
+ Handler that buffers packets until it has a full frame then puts
151
+ them into a queue. Queue must have a put method.
152
+
153
+ The data put into the queue will be a dict that contains a buffer
154
+ of packets making up a frame and the rotation number.
155
+
156
+ {
157
+ 'buffer': [packet1, packet2, ...],
158
+ 'rotation': 1
159
+ }
160
+ """
161
+ buffer = []
162
+ rotation_num = 0
163
+ sentinel = None
164
+ last = None
165
+
166
+ def handler (packet ):
167
+ nonlocal rotation_num , sentinel , last , buffer
168
+
169
+ encoder_count = peek_encoder_count (packet )
170
+ if sentinel is None :
171
+ sentinel = encoder_count
172
+
173
+ if buffer and last and encoder_count >= sentinel and last <= sentinel :
174
+ rotation_num += 1
175
+ queue .put ({"buffer" : buffer , "rotation" : rotation_num })
176
+ buffer = []
177
+
178
+ buffer .append (packet )
179
+ last = encoder_count
180
+
181
+ return handler
0 commit comments