Imported Upstream version 3.0
[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 2, 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 (int nchannels, double sample_rate, gr_msg_queue_sptr msgq)
55   : d_nchannels (nchannels),
56     d_msgq (msgq), 
57     d_trigger_mode (gr_TRIG_AUTO),
58     d_trigger_channel (0),
59     d_sample_rate (sample_rate),
60     d_update_rate (20),
61     d_trigger_level (0),
62     d_obi (0),
63     d_state (LOOK_FOR_TRIGGER),
64     d_decimator_count (0),
65     d_decimator_count_init (1),
66     d_hold_off_count (0),
67     d_hold_off_count_init (0),
68     d_post_trigger_count (0),
69     d_post_trigger_count_init (OUTPUT_RECORD_SIZE/2),
70     d_prev_sample (0)
71 {
72   if (d_nchannels > MAX_CHANNELS){
73     fprintf (stderr, "gr_oscope_guts: too many channels.  MAX_CHANNELS = %d\n", MAX_CHANNELS);
74     throw std::runtime_error ("too many channels");
75   }
76
77   for (int i = 0; i < MAX_CHANNELS; i++)
78     d_buffer[i] = 0;
79
80   for (int i = 0; i < d_nchannels; i++)
81     d_buffer[i] = new float [OUTPUT_RECORD_SIZE];
82
83   update_rate_or_decimation_changed ();
84   enter_look_for_trigger ();
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   int trigger = 0;
111   
112   switch (d_state){
113   case HOLD_OFF:
114     d_hold_off_count--;
115     if (d_hold_off_count <= 0)
116       enter_look_for_trigger ();
117     break;
118
119   case LOOK_FOR_TRIGGER:
120     trigger = found_trigger (d_buffer[d_trigger_channel][d_obi]);
121     if (trigger != 0){
122       enter_post_trigger ();
123       if (trigger < 0)                  // previous sample was closer
124         d_post_trigger_count--;
125     }
126     break;
127
128   case POST_TRIGGER:
129     d_post_trigger_count--;
130     if (d_post_trigger_count <= 0){
131       write_output_records ();
132       enter_hold_off ();
133     }
134     break;
135
136   default:
137     assert (0);
138   }
139
140   d_obi = incr_bi (d_obi);
141 }
142
143 /*
144  * Functions called on state entry
145  */
146
147 void
148 gr_oscope_guts::enter_hold_off ()
149 {
150   d_state = HOLD_OFF;
151   d_hold_off_count = d_hold_off_count_init;
152 }
153
154 void
155 gr_oscope_guts::enter_look_for_trigger ()
156 {
157   d_state = LOOK_FOR_TRIGGER;
158   d_prev_sample = d_buffer[d_trigger_channel][d_obi];
159 }
160
161 void
162 gr_oscope_guts::enter_post_trigger ()
163 {
164   d_state = POST_TRIGGER;
165   d_post_trigger_count = d_post_trigger_count_init;
166 }
167
168 // ----------------------------------------------------------------
169 // returns 0 if no trigger found. 
170 // returns +1 if this sample is the trigger point
171 // returns -1 if the previous sample is the trigger point
172
173 int
174 gr_oscope_guts::found_trigger (float new_sample)
175 {
176   float prev_sample = d_prev_sample;
177   d_prev_sample = new_sample;
178   bool trig;
179
180   switch (d_trigger_mode){
181
182   case gr_TRIG_AUTO:            // always trigger
183     return +1;
184     
185   case gr_TRIG_POS_SLOPE:
186     trig = prev_sample < d_trigger_level && new_sample >= d_trigger_level;
187     if (trig){
188       if (fabs (prev_sample - d_trigger_level) < fabs (new_sample - d_trigger_level))
189         return -1;
190       else
191         return +1;
192     }
193     return 0;
194
195   case gr_TRIG_NEG_SLOPE:
196     trig = prev_sample > d_trigger_level && new_sample <= d_trigger_level;
197     if (trig){
198       if (fabs (prev_sample - d_trigger_level) < fabs (new_sample - d_trigger_level))
199         return -1;
200       else
201         return +1;
202     }
203     return 0;
204
205   default:
206     assert (0);
207     return 0;
208   }
209 }
210
211 // ----------------------------------------------------------------
212 // write output records (duh!)
213
214 void
215 gr_oscope_guts::write_output_records ()
216 {
217   // if the output queue if full, drop the data on the ground.
218   if (d_msgq->full_p())
219     return;
220
221   // Build a message to hold the output records
222   gr_message_sptr msg = 
223     gr_make_message(0,                                                 // msg type
224                     d_nchannels,                                       // arg1 for other side
225                     OUTPUT_RECORD_SIZE,                                // arg2 for other side
226                     d_nchannels * OUTPUT_RECORD_SIZE * sizeof(float)); // sizeof payload
227
228   float *out = (float *)msg->msg();     // get pointer to raw message buffer
229
230   for (int ch = 0; ch < d_nchannels; ch++){
231     // note that d_obi points at the oldest sample in the buffer
232     for (int i = 0; i < OUTPUT_RECORD_SIZE; i++)
233       out[i] = d_buffer[ch][wrap_bi(d_obi + i)];
234
235     out += OUTPUT_RECORD_SIZE;
236   }
237
238   d_msgq->handle(msg);          // send the msg
239 }
240
241 // ----------------------------------------------------------------
242
243 bool
244 gr_oscope_guts::set_update_rate (double update_rate)
245 {
246   d_update_rate = std::min (std::max (1./10., update_rate), d_sample_rate);
247   update_rate_or_decimation_changed ();
248   return true;
249 }
250
251 bool
252 gr_oscope_guts::set_decimation_count (int decimator_count)
253 {
254   decimator_count = std::max (1, decimator_count);
255   d_decimator_count_init = decimator_count;
256   update_rate_or_decimation_changed ();
257   return true;
258 }
259
260 bool
261 gr_oscope_guts::set_sample_rate(double sample_rate)
262 {
263   d_sample_rate = sample_rate;
264   return set_update_rate(update_rate());
265 }
266
267
268 void
269 gr_oscope_guts::update_rate_or_decimation_changed ()
270 {
271   d_hold_off_count_init =
272     (int) rint (d_sample_rate / d_update_rate / d_decimator_count_init);
273 }
274
275 bool
276 gr_oscope_guts::set_trigger_channel (int channel)
277 {
278   if (channel >= 0 && channel < d_nchannels){
279     d_trigger_channel = channel;
280     trigger_changed ();
281     return true;
282   }
283
284   return false;
285 }
286
287 bool
288 gr_oscope_guts::set_trigger_mode (gr_trigger_mode mode)
289 {
290   switch (mode){
291   case gr_TRIG_POS_SLOPE:
292   case gr_TRIG_NEG_SLOPE:
293   case gr_TRIG_AUTO:
294     d_trigger_mode = mode;
295     trigger_changed ();
296     return true;
297   }
298   return false;
299 }
300
301 bool
302 gr_oscope_guts::set_trigger_level (double trigger_level)
303 {
304   d_trigger_level = trigger_level;
305   trigger_changed ();
306   return true;
307 }
308
309 bool
310 gr_oscope_guts::set_trigger_level_auto ()
311 {
312   // find the level 1/2 way between the min and the max
313
314   float min_v = d_buffer[d_trigger_channel][0];
315   float max_v = d_buffer[d_trigger_channel][0];
316
317   for (int i = 1; i < OUTPUT_RECORD_SIZE; i++){
318     min_v = std::min (min_v, d_buffer[d_trigger_channel][i]);
319     max_v = std::max (max_v, d_buffer[d_trigger_channel][i]);
320   }
321
322   d_trigger_level = (min_v + max_v) * 0.5;
323   trigger_changed ();
324   return true;
325 }
326
327 void
328 gr_oscope_guts::trigger_changed ()
329 {
330   // d_prev_sample = d_buffer[d_trigger_channel][decr_bi(d_obi)];
331   enter_look_for_trigger ();
332 }
333
334 // ACCESSORS
335
336 int
337 gr_oscope_guts::num_channels () const
338 {
339   return d_nchannels;
340 }
341
342 double
343 gr_oscope_guts::sample_rate () const
344 {
345   return d_sample_rate;
346 }
347
348 double
349 gr_oscope_guts::update_rate () const
350 {
351   return d_update_rate;
352 }
353
354 int
355 gr_oscope_guts::get_decimation_count () const
356 {
357   return d_decimator_count_init;
358 }
359
360 int
361 gr_oscope_guts::get_trigger_channel () const
362 {
363   return d_trigger_channel;
364 }
365
366 gr_trigger_mode
367 gr_oscope_guts::get_trigger_mode () const
368 {
369   return d_trigger_mode;
370 }
371
372 double
373 gr_oscope_guts::get_trigger_level () const
374 {
375   return d_trigger_level;
376 }
377
378 int
379 gr_oscope_guts::get_samples_per_output_record () const
380 {
381   return OUTPUT_RECORD_SIZE;
382 }