Imported Upstream version 3.2.2
[debian/gnuradio] / gnuradio-core / src / lib / io / gr_oscope_guts.cc
1 /* -*- c++ -*- */
2 /*
3  * Copyright 2003,2005 Free Software Foundation, Inc.
4  * 
5  * This file is part of GNU Radio
6  * 
7  * GNU Radio is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 3, or (at your option)
10  * any later version.
11  * 
12  * GNU Radio is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  * 
17  * You should have received a copy of the GNU General Public License
18  * along with GNU Radio; see the file COPYING.  If not, write to
19  * the Free Software Foundation, Inc., 51 Franklin Street,
20  * Boston, MA 02110-1301, USA.
21  */
22
23 #ifdef HAVE_CONFIG_H
24 #include <config.h>
25 #endif
26 #include <gr_oscope_guts.h>
27 #include <stdexcept>
28 #include <stdio.h>
29 #include <algorithm>
30 #include <unistd.h>
31 #include <math.h>
32 #include <assert.h>
33
34 static const int OUTPUT_RECORD_SIZE = 2048;        // must be power of 2
35
36 static inline int
37 wrap_bi (int buffer_index)                // wrap buffer index
38 {
39   return buffer_index & (OUTPUT_RECORD_SIZE - 1);
40 }
41
42 static inline int
43 incr_bi (int buffer_index)                // increment buffer index
44 {
45   return wrap_bi (buffer_index + 1);
46 }
47
48 static inline int
49 decr_bi (int buffer_index)                // decrement buffer index
50 {
51   return wrap_bi (buffer_index - 1);
52 }
53
54 gr_oscope_guts::gr_oscope_guts (double sample_rate, gr_msg_queue_sptr msgq)
55   : d_nchannels (1),
56     d_msgq (msgq), 
57     d_trigger_mode (gr_TRIG_MODE_AUTO),
58     d_trigger_slope (gr_TRIG_SLOPE_POS),
59     d_trigger_channel (0),
60     d_sample_rate (sample_rate),
61     d_update_rate (20),
62     d_trigger_level (0),
63     d_obi (0),
64     d_state (HOLD_OFF),
65     d_decimator_count (0),
66     d_decimator_count_init (1),
67     d_hold_off_count (0),
68     d_hold_off_count_init (OUTPUT_RECORD_SIZE/2-1),
69     d_pre_trigger_count (0),
70     d_post_trigger_count (0),
71     d_post_trigger_count_init (OUTPUT_RECORD_SIZE/2)
72 {
73   for (int i = 0; i < MAX_CHANNELS; i++)
74     d_buffer[i] = 0;
75
76   for (int i = 0; i < MAX_CHANNELS; i++){
77     d_buffer[i] = new float [OUTPUT_RECORD_SIZE];
78     for (int j = 0; j < OUTPUT_RECORD_SIZE; j++)
79       d_buffer[i][j] = 0.0;
80   }
81
82   // be sure buffer is full before first write
83   enter_hold_off ();
84   update_rate_or_decimation_changed ();
85 }
86
87 gr_oscope_guts::~gr_oscope_guts ()
88 {
89   for (int i = 0; i < MAX_CHANNELS; i++)
90     delete [] d_buffer[i];
91 }
92
93 // MANIPULATORS
94
95 // \p channel_data points to nchannels float values.  These are the values
96 // for each channel at this sample time.
97
98 void
99 gr_oscope_guts::process_sample (const float *channel_data)
100 {
101   d_decimator_count--;
102   if (d_decimator_count > 0)
103     return;
104
105   d_decimator_count = d_decimator_count_init;
106   
107   for (int i = 0; i < d_nchannels; i++)
108     d_buffer[i][d_obi] = channel_data[i];                // copy data into buffer
109
110   switch (d_state){
111   case HOLD_OFF:
112     d_hold_off_count--;
113     if (d_hold_off_count <= 0)
114       enter_look_for_trigger ();
115     break;
116
117   case LOOK_FOR_TRIGGER:
118     if (found_trigger ())
119       enter_post_trigger ();
120     break;
121
122   case POST_TRIGGER:
123     d_post_trigger_count--;
124     if (d_post_trigger_count <= 0){
125       write_output_records ();
126       enter_hold_off ();
127     }
128     break;
129
130   default:
131     assert (0);
132   }
133
134   d_obi = incr_bi (d_obi);
135 }
136
137 /*
138  * Functions called on state entry
139  */
140
141 void
142 gr_oscope_guts::enter_hold_off ()
143 {
144   d_state = HOLD_OFF;
145   d_hold_off_count = d_hold_off_count_init;
146 }
147
148 void
149 gr_oscope_guts::enter_look_for_trigger ()
150 {
151   d_pre_trigger_count = 0;
152   d_state = LOOK_FOR_TRIGGER;
153 }
154
155 void
156 gr_oscope_guts::enter_post_trigger ()
157 {
158   d_state = POST_TRIGGER;
159   d_post_trigger_count = d_post_trigger_count_init;
160   //ensure that the trigger offset is no more than than half a sample
161   if (d_trigger_off > .5) d_trigger_off -= 1;
162   else d_post_trigger_count--;
163 }
164
165 // ----------------------------------------------------------------
166 // returns true if trigger found
167
168 bool
169 gr_oscope_guts::found_trigger ()
170 {
171   float prev_sample = d_buffer[d_trigger_channel][decr_bi(d_obi)];
172   float new_sample = d_buffer[d_trigger_channel][d_obi];
173
174   switch (d_trigger_mode){
175
176   case gr_TRIG_MODE_AUTO: //too many samples without a trigger
177     d_pre_trigger_count++;
178     if (d_pre_trigger_count > OUTPUT_RECORD_SIZE/2) return true;
179
180   case gr_TRIG_MODE_NORM: //look for trigger
181     switch (d_trigger_slope){
182
183     case gr_TRIG_SLOPE_POS: //trigger point in pos slope?
184       if (new_sample < d_trigger_level || prev_sample >= d_trigger_level) return false;
185       break;
186
187     case gr_TRIG_SLOPE_NEG: //trigger point in neg slope?
188       if (new_sample > d_trigger_level || prev_sample <= d_trigger_level) return false;
189       break;
190     }
191
192     //calculate the trigger offset in % sample
193     d_trigger_off = (d_trigger_level - prev_sample)/(new_sample - prev_sample);
194     return true;
195
196   case gr_TRIG_MODE_FREE: //free run mode, always trigger
197     d_trigger_off = 0;
198     return true;
199
200   default:
201     assert (0);
202     return false;
203   }
204 }
205
206 // ----------------------------------------------------------------
207 // write output records (duh!)
208
209 void
210 gr_oscope_guts::write_output_records ()
211 {
212   // if the output queue if full, drop the data like its hot.
213   if (d_msgq->full_p())
214     return;
215     // Build a message to hold the output records
216   gr_message_sptr msg = 
217     gr_make_message(0,                                         // msg type
218             d_nchannels,                                       // arg1 for other side
219             OUTPUT_RECORD_SIZE,                                // arg2 for other side
220             ((d_nchannels * OUTPUT_RECORD_SIZE) + 1) * sizeof(float)); // sizeof payload
221
222   float *out = (float *)msg->msg();        // get pointer to raw message buffer
223
224   for (int ch = 0; ch < d_nchannels; ch++){
225     // note that d_obi + 1 points at the oldest sample in the buffer
226     for (int i = 0; i < OUTPUT_RECORD_SIZE; i++){
227       out[i] = d_buffer[ch][wrap_bi(d_obi + 1 + i)];
228     }
229     out += OUTPUT_RECORD_SIZE;
230   }
231   //Set the last sample as the trigger offset:
232   //  The non gl scope sink will not look at this last sample.
233   //  The gl scope sink will use this last sample as an offset.
234   out[0] = d_trigger_off;
235   d_msgq->handle(msg);                // send the msg
236 }
237
238 // ----------------------------------------------------------------
239
240 bool
241 gr_oscope_guts::set_update_rate (double update_rate)
242 {
243   d_update_rate = std::min (std::max (1./10., update_rate), d_sample_rate);
244   update_rate_or_decimation_changed ();
245   return true;
246 }
247
248 bool
249 gr_oscope_guts::set_decimation_count (int decimator_count)
250 {
251   decimator_count = std::max (1, decimator_count);
252   d_decimator_count_init = decimator_count;
253   update_rate_or_decimation_changed ();
254   return true;
255 }
256
257 bool
258 gr_oscope_guts::set_sample_rate(double sample_rate)
259 {
260   d_sample_rate = sample_rate;
261   return set_update_rate(update_rate());
262 }
263
264
265 void
266 gr_oscope_guts::update_rate_or_decimation_changed ()
267 {
268   d_hold_off_count_init =
269     (int) rint (d_sample_rate / d_update_rate / d_decimator_count_init);
270 }
271
272 bool
273 gr_oscope_guts::set_trigger_channel (int channel)
274 {
275   if (channel >= 0 && channel < d_nchannels){
276     d_trigger_channel = channel;
277     trigger_changed ();
278     return true;
279   }
280
281   return false;
282 }
283
284 bool
285 gr_oscope_guts::set_trigger_mode (gr_trigger_mode mode)
286 {
287   d_trigger_mode = mode;
288   trigger_changed ();
289   return true;
290 }
291
292 bool
293 gr_oscope_guts::set_trigger_slope (gr_trigger_slope slope)
294 {
295   d_trigger_slope = slope;
296   trigger_changed ();
297   return true;
298 }
299
300 bool
301 gr_oscope_guts::set_trigger_level (double trigger_level)
302 {
303   d_trigger_level = trigger_level;
304   trigger_changed ();
305   return true;
306 }
307
308 bool
309 gr_oscope_guts::set_trigger_level_auto ()
310 {
311   // find the level 1/2 way between the min and the max
312
313   float min_v = d_buffer[d_trigger_channel][0];
314   float max_v = d_buffer[d_trigger_channel][0];
315
316   for (int i = 1; i < OUTPUT_RECORD_SIZE; i++){
317     min_v = std::min (min_v, d_buffer[d_trigger_channel][i]);
318     max_v = std::max (max_v, d_buffer[d_trigger_channel][i]);
319   }
320   return set_trigger_level((min_v + max_v) * 0.5);
321 }
322
323 bool
324 gr_oscope_guts::set_num_channels(int nchannels)
325 {
326   if (nchannels > 0 && nchannels <= MAX_CHANNELS){
327     d_nchannels = nchannels;
328     return true;
329   }
330   return false;
331 }
332
333
334 void
335 gr_oscope_guts::trigger_changed ()
336 {
337   enter_look_for_trigger ();
338 }
339
340 // ACCESSORS
341
342 int
343 gr_oscope_guts::num_channels () const
344 {
345   return d_nchannels;
346 }
347
348 double
349 gr_oscope_guts::sample_rate () const
350 {
351   return d_sample_rate;
352 }
353
354 double
355 gr_oscope_guts::update_rate () const
356 {
357   return d_update_rate;
358 }
359
360 int
361 gr_oscope_guts::get_decimation_count () const
362 {
363   return d_decimator_count_init;
364 }
365
366 int
367 gr_oscope_guts::get_trigger_channel () const
368 {
369   return d_trigger_channel;
370 }
371
372 gr_trigger_mode
373 gr_oscope_guts::get_trigger_mode () const
374 {
375   return d_trigger_mode;
376 }
377
378 gr_trigger_slope
379 gr_oscope_guts::get_trigger_slope () const
380 {
381   return d_trigger_slope;
382 }
383
384 double
385 gr_oscope_guts::get_trigger_level () const
386 {
387   return d_trigger_level;
388 }
389
390 int
391 gr_oscope_guts::get_samples_per_output_record () const
392 {
393   return OUTPUT_RECORD_SIZE;
394 }