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