5276de109a66c241ee07749eb311ce442160db03
[debian/gnuradio] / grc / src / grc_gnuradio / blks2 / packet.py
1 # Copyright 2008 Free Software Foundation, Inc.
2
3 # This file is part of GNU Radio
4
5 # GNU Radio is free software; you can redistribute it and/or modify
6 # it under the terms of the GNU General Public License as published by
7 # the Free Software Foundation; either version 3, or (at your option)
8 # any later version.
9
10 # GNU Radio is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 # GNU General Public License for more details.
14
15 # You should have received a copy of the GNU General Public License
16 # along with GNU Radio; see the file COPYING.  If not, write to
17 # the Free Software Foundation, Inc., 51 Franklin Street,
18 # Boston, MA 02110-1301, USA.
19
20
21 from gnuradio import gr, packet_utils
22 import gnuradio.gr.gr_threading as _threading
23
24 ##payload length in bytes
25 DEFAULT_PAYLOAD_LEN = 512
26
27 ##how many messages in a queue
28 DEFAULT_MSGQ_LIMIT = 2
29
30 ##threshold for unmaking packets
31 DEFAULT_THRESHOLD = 12
32
33 #######################################################################################
34 ##      Packet Encoder
35 #######################################################################################
36
37 class _packet_encoder_thread(_threading.Thread):
38         
39         def __init__(self, msgq, payload_length, send):
40                 self._msgq = msgq
41                 self._payload_length = payload_length
42                 self._send = send
43                 _threading.Thread.__init__(self)
44                 self.setDaemon(1)
45                 self.keep_running = True
46                 self.start()
47                 
48         def run(self):
49                 sample = '' #residual sample
50                 while self.keep_running:
51                         msg = self._msgq.delete_head() #blocking read of message queue
52                         sample = sample + msg.to_string() #get the body of the msg as a string  
53                         while len(sample) >= self._payload_length:              
54                                 payload = sample[0:self._payload_length]
55                                 sample = sample[self._payload_length:]                  
56                                 self._send(payload)     
57
58 class packet_encoder(gr.hier_block2):
59         """
60         Hierarchical block for wrapping packet-based modulators.
61         """
62         
63         def __init__(self, item_size_in, samples_per_symbol, bits_per_symbol, access_code='', pad_for_usrp=True, payload_length=-1):
64                 """!
65                 packet_mod constructor.
66                 @param item_size_in the size of the input data stream in bytes
67                 @param samples_per_symbol number of samples per symbol
68                 @param bits_per_symbol number of bits per symbol
69                 @param access_code AKA sync vector
70                 @param pad_for_usrp If true, packets are padded such that they end up a multiple of 128 samples
71                 @param payload_length number of bytes in a data-stream slice
72                 """
73                 #setup parameters
74                 self._item_size_in = item_size_in
75                 self._samples_per_symbol = samples_per_symbol
76                 self._bits_per_symbol = bits_per_symbol
77                 self._pad_for_usrp = pad_for_usrp
78                 if not access_code: #get access code
79                         access_code = packet_utils.default_access_code
80                 if not packet_utils.is_1_0_string(access_code):
81                         raise ValueError, "Invalid access_code %r. Must be string of 1's and 0's" % (access_code,)
82                 self._access_code = access_code         
83                 self._pad_for_usrp = pad_for_usrp               
84                 if payload_length < 0: #get payload length
85                         payload_length = DEFAULT_PAYLOAD_LEN
86                 if payload_length%self._item_size_in != 0:      #verify that packet length is a multiple of the stream size
87                         raise ValueError, 'The packet length: "%d" is not a mutiple of the stream size: "%d".'%(payload_length, self._item_size_in)
88                 self._payload_length = payload_length
89                 #create blocks
90                 msg_source = gr.message_source(gr.sizeof_char, DEFAULT_MSGQ_LIMIT)
91                 self._msgq_out = msg_source.msgq()              
92                 self._msgq_in = gr.msg_queue(DEFAULT_MSGQ_LIMIT)
93                 msg_sink = gr.message_sink(self._item_size_in, self._msgq_in, False) #False -> blocking         
94                 #initialize hier2
95                 gr.hier_block2.__init__(
96                         self, 
97                         "packet_encoder",
98                         gr.io_signature(1, 1, self._item_size_in), # Input signature
99                         gr.io_signature(1, 1, gr.sizeof_char) # Output signature
100                 )
101                 #connect
102                 self.connect(self, msg_sink)
103                 self.connect(msg_source, self)
104                 #start thread
105                 _packet_encoder_thread(self._msgq_in, self._payload_length, self._send_packet)
106                 
107         def _send_packet(self, payload):
108                 """!
109                 Wrap the payload in a packet and push onto the message queue.
110                 @param payload string, data to send
111                 """     
112                 packet = packet_utils.make_packet(
113                         payload,
114                         self._samples_per_symbol,
115                         self._bits_per_symbol,
116                         self._access_code,
117                         self._pad_for_usrp
118                 )
119                 msg = gr.message_from_string(packet)
120                 self._msgq_out.insert_tail(msg)
121         
122 #######################################################################################
123 ##      Packet Decoder
124 #######################################################################################
125
126 class _packet_decoder_thread(_threading.Thread):
127     
128     def __init__(self, msgq, callback):
129         _threading.Thread.__init__(self)
130         self.setDaemon(1)
131         self._msgq = msgq
132         self.callback = callback
133         self.keep_running = True
134         self.start()
135
136     def run(self):
137         while self.keep_running:
138             msg = self._msgq.delete_head()
139             ok, payload = packet_utils.unmake_packet(msg.to_string(), int(msg.arg1()))
140             if self.callback:
141                 self.callback(ok, payload)
142
143 class packet_decoder(gr.hier_block2):
144         """
145         Hierarchical block for wrapping packet-based demodulators.
146         """
147         
148         def __init__(self, item_size_out, access_code='', threshold=-1):
149                 """!
150                 packet_demod constructor.
151                 @param item_size_out the size of the output data stream in bytes
152                 @param access_code AKA sync vector
153                 @param threshold detect access_code with up to threshold bits wrong (-1 -> use default)
154                 """
155                 #setup
156                 self._item_size_out = item_size_out
157                 #access code
158                 if not access_code: #get access code
159                         access_code = packet_utils.default_access_code
160                 if not packet_utils.is_1_0_string(access_code):
161                         raise ValueError, "Invalid access_code %r. Must be string of 1's and 0's" % (access_code,)
162                 self._access_code = access_code         
163                 #threshold
164                 if threshold < 0: threshold = DEFAULT_THRESHOLD 
165                 self._threshold = threshold  
166                 #blocks
167                 self._msgq_in = gr.msg_queue(DEFAULT_MSGQ_LIMIT) #holds packets from the PHY
168                 correlator = gr.correlate_access_code_bb(self._access_code, self._threshold)
169                 framer_sink = gr.framer_sink_1(self._msgq_in)           
170                 msg_source = gr.message_source(self._item_size_out, DEFAULT_MSGQ_LIMIT)
171                 self._msgq_out = msg_source.msgq()              
172                 #initialize hier2
173                 gr.hier_block2.__init__(
174                         self, 
175                         "packet_decoder",
176                         gr.io_signature(1, 1, gr.sizeof_char), # Input signature
177                         gr.io_signature(1, 1, self._item_size_out) # Output signature
178                 )
179                 #connect
180                 self.connect(self, correlator, framer_sink)
181                 self.connect(msg_source, self)
182                 #start thread
183                 _packet_decoder_thread(self._msgq_in, self._recv_packet)
184
185         def _recv_packet(self, ok, payload):
186                 """!
187                 Extract the payload from the packet and push onto message queue.
188                 @param ok boolean ok
189                 @param payload data received
190                 """
191                 msg = gr.message_from_string(payload, 0, self._item_size_out, len(payload)/self._item_size_out)
192                 if ok: self._msgq_out.insert_tail(msg)                  
193
194