[xiph-commits] r18438 - trunk/spectrum

xiphmont at svn.xiph.org xiphmont at svn.xiph.org
Mon Jul 2 23:15:11 PDT 2012


Author: xiphmont
Date: 2012-07-02 23:15:11 -0700 (Mon, 02 Jul 2012)
New Revision: 18438

Modified:
   trunk/spectrum/Makefile
   trunk/spectrum/io.c
   trunk/spectrum/io.h
   trunk/spectrum/spec_process.c
   trunk/spectrum/version.h
   trunk/spectrum/wave_panel.c
   trunk/spectrum/wave_plot.c
   trunk/spectrum/wave_plot.h
   trunk/spectrum/wave_process.c
   trunk/spectrum/waveform.h
Log:
Implement a crude but working oversampled trigger in the waveform viewer



Modified: trunk/spectrum/Makefile
===================================================================
--- trunk/spectrum/Makefile	2012-07-02 16:05:53 UTC (rev 18437)
+++ trunk/spectrum/Makefile	2012-07-03 06:15:11 UTC (rev 18438)
@@ -62,7 +62,7 @@
 
 waveform:  $(WAVEFORM_OBJ) 
 	./touch-version
-	$(LD) $(WAVEFORM_OBJ) -o waveform $(LIBS) $(CFLAGS) `pkg-config --libs gtk+-2.0` -lpthread -lm 
+	$(LD) $(WAVEFORM_OBJ) -o waveform $(LIBS) $(CFLAGS) `pkg-config --libs gtk+-2.0` -lpthread -lfftw3f -lm 
 
 target:  waveform spectrum
 

Modified: trunk/spectrum/io.c
===================================================================
--- trunk/spectrum/io.c	2012-07-02 16:05:53 UTC (rev 18437)
+++ trunk/spectrum/io.c	2012-07-03 06:15:11 UTC (rev 18438)
@@ -52,10 +52,21 @@
 
 extern int blocksize; /* set only at startup */
 sig_atomic_t blockslice_frac;
-static int blockslice_count=0;
+int blockslice_count=0;
 static int blockslice_started=0;
 static int blockslices[MAX_FILES]={0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0};
 
+/* used to determine the slight sample timing offsets between the
+   blockbuffer heads of inputs with different, non-interger-ratio
+   sampling rates, or an input that's not a multiple of the requested
+   blockslice fraction.  It lists the sample position within this
+   second of one-past the last sample read */
+int blockslice_cursor[MAX_FILES]={0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0};
+/* used to indicate which inputs have reached eof */
+int blockslice_eof[MAX_FILES]={0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0};
+/* how much the blockbuffer was advanced last read for this input */
+int blockslice_adv[MAX_FILES]={0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0};
+
 static unsigned char readbuffer[MAX_FILES][readsize];
 static int readbufferfill[MAX_FILES]={0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0};
 static int readbufferptr[MAX_FILES]={0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0};
@@ -566,29 +577,44 @@
   }
 }
 
+static void blockslice_init(void){
+
+  /* strict determinism is nice */
+  if(!blockslice_started){
+    int frac = blockslice_frac;
+    int fi;
+
+    blockslice_count=0;
+    memset(blockslice_cursor,0,sizeof(blockslice_cursor));
+    memset(blockslice_eof,0,sizeof(blockslice_eof));
+    blockslice_started = 1;
+
+    for(fi=0;fi<inputs;fi++)
+      blockslices[fi] = rate[fi]/frac;
+  }
+}
+
 /* blockslices are tracked/locked over a one second period */
 static void blockslice_advance(void){
   int fi;
   int frac = blockslice_frac;
-  int count;
+  int count = blockslice_count + (1000000/frac);
 
-  /* strict determinism is nice */
-  if(!blockslice_started)blockslice_count=0;
+  blockslice_count = count;
+  count += (1000000/frac);
 
-  count = blockslice_count + (1000000/frac);
   for(fi=0;fi<inputs;fi++){
-    int prevsample = rint((double)rate[fi]*blockslice_count/1000000);
-    int thissample = rint((double)rate[fi]*count/1000000);
+    int nextsample = rint((double)rate[fi]*count/1000000);
 
-    blockslices[fi] = thissample - prevsample;
-    if(blockslices[fi]<1)blockslices[fi]=1;
-    if(blockslices[fi]>blocksize)blockslices[fi]=blocksize;
+    blockslice_cursor[fi] += blockslice_adv[fi];
+    if(blockslice_adv[fi] < blockslices[fi])
+      blockslice_eof[fi]=1;
+    else
+      blockslices[fi] = nextsample - blockslice_cursor[fi];
+    if(blockslice_cursor[fi] >= rate[fi]) blockslice_cursor[fi]-=rate[fi];
 
   }
-
-  blockslice_count = count;
   if(blockslice_count>=1000000)blockslice_count-=1000000;
-  blockslice_started = 1;
 }
 
 /* input_read returns:
@@ -607,8 +633,7 @@
   int rewound[total_ch];
   memset(rewound,0,sizeof(rewound));
 
-  /* also handles initialization */
-  if(!blockslice_started)blockslice_advance();
+  blockslice_init();
 
   pthread_mutex_lock(&blockbuffer_mutex);
   if(blockbuffer==0){
@@ -710,13 +735,17 @@
         memmove(blockbuffer[i],blockbuffer[i]+
                 (blockbufferfill[fi]-blocksize),
                 blocksize*sizeof(**blockbuffer));
+
+      blockslice_adv[fi] = blockbufferfill[fi]-blocksize;
+
       blockbufferfill[fi]=blocksize;
       blockbuffernew[fi]=1;
       ret=1;
+    }else{
+      blockslice_adv[fi]=0;
     }
   }
   blockslice_advance();
-  pthread_mutex_unlock(&blockbuffer_mutex);
 
   return ret;
 }

Modified: trunk/spectrum/io.h
===================================================================
--- trunk/spectrum/io.h	2012-07-02 16:05:53 UTC (rev 18437)
+++ trunk/spectrum/io.h	2012-07-03 06:15:11 UTC (rev 18438)
@@ -70,6 +70,10 @@
 extern int global_seekable;
 
 extern sig_atomic_t blockslice_frac;
+extern int blockslice_count;
+extern int blockslice_cursor[MAX_FILES];
+extern int blockslice_eof[MAX_FILES];
+extern int blockslice_adv[MAX_FILES];
 extern int blockbufferfill[MAX_FILES];
 extern int blockbuffernew[MAX_FILES];
 extern float **blockbuffer;

Modified: trunk/spectrum/spec_process.c
===================================================================
--- trunk/spectrum/spec_process.c	2012-07-02 16:05:53 UTC (rev 18437)
+++ trunk/spectrum/spec_process.c	2012-07-03 06:15:11 UTC (rev 18438)
@@ -360,6 +360,9 @@
     acc_rewind=0;
 
     ret=input_read(acc_loop,0);
+    /* returns with blockbuffer mutex held */
+    pthread_mutex_unlock(&blockbuffer_mutex);
+
     if(ret==0) break;
     if(ret==-1){
       /* a pipe returned EOF; attempt reopen */
@@ -477,7 +480,6 @@
     if(first==last){
       /* interpolate between two bins */
       float m = (H[i]+L[i])*.5-first;
-      float del = H[i]-L[i];
       firsty=lasty=min=max =
         (todB(in[first])*(1.-m)+todB(in[first+1])*m+dBnorm)*.5;
     }else{
@@ -920,11 +922,9 @@
 }
 
 float norm_ph(float *dataR, float *dataI,int n,int rate){
-  int i,j;
+  int i;
   int x0 = 100.*blocksize/rate;
   int x1 = x0+blocksize/4;
-  int delcount=0, revcount=0;
-  float delsum=0.;
 
   float sumR=0.;
   float sumI=0.;
@@ -1186,7 +1186,6 @@
          channel 0 phase doesn't affect the final answer */
 
       {
-        int any=0;
         float *y = fetch_ret.data[ch];
         float work[blocksize/2+1];
 

Modified: trunk/spectrum/version.h
===================================================================
--- trunk/spectrum/version.h	2012-07-02 16:05:53 UTC (rev 18437)
+++ trunk/spectrum/version.h	2012-07-03 06:15:11 UTC (rev 18438)
@@ -1,2 +1,2 @@
 #define VERSION "$Id$ "
-/* DO NOT EDIT: Automated versioning hack [Sun Jul  1 15:57:37 EDT 2012] */
+/* DO NOT EDIT: Automated versioning hack [Tue Jul  3 02:12:25 EDT 2012] */

Modified: trunk/spectrum/wave_panel.c
===================================================================
--- trunk/spectrum/wave_panel.c	2012-07-02 16:05:53 UTC (rev 18437)
+++ trunk/spectrum/wave_panel.c	2012-07-03 06:15:11 UTC (rev 18438)
@@ -342,8 +342,8 @@
 }
 
 static void triggerchange(GtkWidget *widget,gpointer in){
-  //int choice=gtk_combo_box_get_active(GTK_COMBO_BOX(widget));
-  /* nothing but free-run supported right now */
+  plot_trigger=gtk_combo_box_get_active(GTK_COMBO_BOX(widget));
+  set_trigger(plot_trigger,0,plot_interval,plot_span);
 }
 
 static void runchange(GtkWidget *widget,gpointer in){
@@ -657,7 +657,19 @@
   gtk_combo_box_set_active(GTK_COMBO_BOX(scalemenu),0);
   gtk_combo_box_set_active(GTK_COMBO_BOX(rangemenu),4);
 
+  /* plot type */
   {
+    GtkWidget *menu=gtk_combo_box_new_text();
+    char *entries[]={"zero-hold","interpolated","lollipop",NULL};
+    for(i=0;entries[i];i++)
+      gtk_combo_box_append_text (GTK_COMBO_BOX (menu), entries[i]);
+    gtk_combo_box_set_active(GTK_COMBO_BOX(menu),0);
+    g_signal_connect (G_OBJECT (menu), "changed",
+		      G_CALLBACK (plotchange), NULL);
+    gtk_box_pack_start(GTK_BOX(bbox),menu,0,0,0);
+  }
+
+  {
     GtkWidget *sep=gtk_hseparator_new();
     gtk_box_pack_start(GTK_BOX(bbox),sep,0,0,4);
   }
@@ -690,7 +702,7 @@
   /* trigger */
   {
     GtkWidget *menu=gtk_combo_box_new_text();
-    char *entries[]={"free run",NULL};
+    char *entries[]={"free run","0\xE2\x86\x91 trigger","0\xE2\x86\x93 trigger",NULL};
     for(i=0;entries[i];i++)
       gtk_combo_box_append_text (GTK_COMBO_BOX (menu), entries[i]);
     gtk_combo_box_set_active(GTK_COMBO_BOX(menu),0);
@@ -716,19 +728,7 @@
     gtk_box_pack_start(GTK_BOX(bbox),menu,1,1,0);
   }
 
-  /* plot type */
   {
-    GtkWidget *menu=gtk_combo_box_new_text();
-    char *entries[]={"zero-hold","interpolated","lollipop",NULL};
-    for(i=0;entries[i];i++)
-      gtk_combo_box_append_text (GTK_COMBO_BOX (menu), entries[i]);
-    gtk_combo_box_set_active(GTK_COMBO_BOX(menu),0);
-    g_signal_connect (G_OBJECT (menu), "changed",
-		      G_CALLBACK (plotchange), NULL);
-    gtk_box_pack_start(GTK_BOX(bbox),menu,0,0,0);
-  }
-
-  {
     GtkWidget *sep=gtk_hseparator_new();
     gtk_box_pack_start(GTK_BOX(bbox),sep,0,0,4);
   }

Modified: trunk/spectrum/wave_plot.c
===================================================================
--- trunk/spectrum/wave_plot.c	2012-07-02 16:05:53 UTC (rev 18437)
+++ trunk/spectrum/wave_plot.c	2012-07-03 06:15:11 UTC (rev 18438)
@@ -182,7 +182,6 @@
   /* dark y grid */
   {
     GdkColor rgb={0,0,0,0};
-    int px,py;
 
     gdk_draw_line(p->backing,p->drawgc,padx,rintf(center-1),width,rintf(center-1));
     gdk_draw_line(p->backing,p->drawgc,padx,rintf(center+1),width,rintf(center+1));
@@ -230,16 +229,17 @@
     gdk_gc_set_clip_rectangle (p->twogc, &clip);
 
     for(fi=0;fi<f->groups;fi++){
-      int spann = ceil(f->rate[fi]/1000000.*pp->span)+1;
+      int spann = ceil(f->rate[fi]/1000000.*pp->span)+2;
+      int wp=width-p->padx;
+      int hp=height-p->pady;
+      float ym=hp*-8./36;
+      float spani = 1000000./f->span/f->rate[fi]*wp;
+      float xo = padx + f->offsets[fi]*spani;
 
       for(i=ch;i<ch+f->channels[fi];i++){
 
         if(f->active[i]){
           float *data=f->data[i];
-          int wp=width-p->padx;
-          float spani = 1000000./f->span/f->rate[fi]*wp;
-          int hp=height-p->pady;
-          float ym=hp*-8./36;
           float cp = pp->trace_sep ?
             (height-p->pady)*(16*i+8)/(float)(18*num_active)+
             (height-p->pady)/18. : center;
@@ -251,11 +251,11 @@
           switch(pp->plotchoice){
           case 0: /* zero-hold */
             {
-              int x0=-1;
+              int x0=rintf(xo-1);
               float yH=NAN,yL=NAN,y0=NAN;
               int acc=0;
               for(k=0;k<spann;k++){
-                int x1 = rintf(k*spani);
+                int x1 = rintf(k*spani+xo);
                 float y1 = data[k]*ym;
 
                 /* clamp for shorts in the X protocol; gdk does not guard */
@@ -266,16 +266,16 @@
                   if(acc>1){
                     if(!isnan(yL)&&!isnan(yH))
                       gdk_draw_line(p->backing,p->twogc,
-                                    x0+padx,rintf(yL+cp),x0+padx,
+                                    x0,rintf(yL+cp),x0,
                                     rintf(yH+cp));
                   }
                   if(!isnan(y0)){
                     gdk_draw_line(p->backing,p->twogc,
-                                  x0+padx,rintf(y0+cp),x1+padx,rintf(y0+cp));
+                                  x0,rintf(y0+cp),x1,rintf(y0+cp));
 
                     if(!isnan(y1))
                       gdk_draw_line(p->backing,p->twogc,
-                                    x1+padx,rintf(y0+cp),x1+padx,rintf(y1+cp));
+                                    x1,rintf(y0+cp),x1,rintf(y1+cp));
                   }
 
                   acc=1;
@@ -291,23 +291,23 @@
                 y0=y1;
               }
               {
-                int x1 = rintf(k*spani);
+                int x1 = rintf(k*spani+xo);
 
                 if(x1<=x0 || acc>1){
                   if(!isnan(yL)&&!isnan(yH))
                     gdk_draw_line(p->backing,p->twogc,
-                                  x0+padx,rintf(yL+cp),x0+padx,rintf(yH+cp));
+                                  x0,rintf(yL+cp),x0,rintf(yH+cp));
                 }
               }
             }
             break;
           case 1: /* linear interpolation (first-order hold) */
             {
-              int x0=-1;
+              int x0=rint(xo-1);
               float yH=NAN,yL=NAN,y0=NAN;
               int acc=0;
               for(k=0;k<spann;k++){
-                int x1 = rintf(k*spani);
+                int x1 = rintf(k*spani+xo);
                 float y1 = data[k]*ym;
 
                 /* clamp for shorts in the X protocol; gdk does not guard */
@@ -318,12 +318,12 @@
                   if(acc>1){
                     if(!isnan(yL) && !isnan(yH))
                       gdk_draw_line(p->backing,p->twogc,
-                                    x0+padx,rintf(yL+cp),x0+padx,
+                                    x0,rintf(yL+cp),x0,
                                     rintf(yH+cp));
                   }
                   if(!isnan(y0) && !isnan(y1)){
                     gdk_draw_line(p->backing,p->twogc,
-                                  x0+padx,rintf(y0+cp),x1+padx,rintf(y1+cp));
+                                  x0,rintf(y0+cp),x1,rintf(y1+cp));
                   }
 
                   acc=1;
@@ -339,19 +339,19 @@
                 y0=y1;
               }
               {
-                int x1 = rintf(k*spani);
+                int x1 = rintf(k*spani+xo);
 
                 if(x1<=x0 || acc>1){
                   if(!isnan(yL) && !isnan(yH))
                     gdk_draw_line(p->backing,p->twogc,
-                                  x0+padx,rintf(yL+cp),x0+padx,rintf(yH+cp));
+                                  x0,rintf(yL+cp),x0,rintf(yH+cp));
                 }
               }
             }
             break;
           case 2: /* lollipop */
             {
-              int x0=-1;
+              int x0=rintf(xo-1);
               float yH=NAN,yL=NAN;
 
               rgb.red=0x8000;
@@ -360,7 +360,7 @@
               gdk_gc_set_rgb_fg_color(p->twogc,&rgb);
 
               for(k=0;k<spann;k++){
-                int x1 = rintf(k*spani);
+                int x1 = rintf(k*spani+xo);
                 float y1 = data[k]*ym;
 
                 /* clamp for shorts in the X protocol; gdk does not guard */
@@ -372,7 +372,7 @@
                     if(isnan(yL) || yL>0)yL=pp->bold;
                     if(isnan(yH) || yH<0)yH=0;
                     gdk_draw_line(p->backing,p->twogc,
-                                  x0+padx,rintf(yL+cp),x0+padx,
+                                  x0,rintf(yL+cp),x0,
                                   rintf(yH+cp));
                   }
                   yH=yL=y1;
@@ -398,7 +398,7 @@
               gdk_gc_set_rgb_fg_color(p->twogc,&rgb);
 
               for(k=0;k<spann;k++){
-                int x = rintf(k*spani);
+                int x = rintf(k*spani+xo);
                 float y = data[k]*ym;
 
                 /* clamp for shorts in the X protocol; gdk does not guard */
@@ -407,7 +407,7 @@
 
                 if(!isnan(y)){
                   gdk_draw_arc(p->backing,p->twogc,
-                               0,x+padx-5,rintf(y+cp-5),
+                               0,x-5,rintf(y+cp-5),
                                9,9,0,23040);
                 }
               }
@@ -444,7 +444,7 @@
   Plot *p=PLOT(widget);
   requisition->width = 400;
   requisition->height = 200;
-  int axisy=0,axisx=0,pady=0,padx=0,px,py,i;
+  int axisy=0,axisx=0,pady=0,padx=0;
 
   if(requisition->width<axisx+padx)requisition->width=axisx+padx;
   if(requisition->height<axisy+pady)requisition->height=axisy+pady;
@@ -504,9 +504,6 @@
 
 GtkWidget* plot_new (void){
   GtkWidget *ret= GTK_WIDGET (g_object_new (plot_get_type (), NULL));
-  Plot *p=PLOT(ret);
-  int i,j;
-
   return ret;
 }
 

Modified: trunk/spectrum/wave_plot.h
===================================================================
--- trunk/spectrum/wave_plot.h	2012-07-02 16:05:53 UTC (rev 18437)
+++ trunk/spectrum/wave_plot.h	2012-07-03 06:15:11 UTC (rev 18438)
@@ -44,6 +44,7 @@
 
   float **data;
   int *active;
+  float offsets[MAX_FILES];
 } fetchdata;
 
 typedef struct {

Modified: trunk/spectrum/wave_process.c
===================================================================
--- trunk/spectrum/wave_process.c	2012-07-02 16:05:53 UTC (rev 18437)
+++ trunk/spectrum/wave_process.c	2012-07-03 06:15:11 UTC (rev 18438)
@@ -21,6 +21,8 @@
  *
  */
 
+#include <math.h>
+#include <fftw3.h>
 #include "waveform.h"
 #include "io.h"
 
@@ -30,8 +32,280 @@
 sig_atomic_t acc_rewind=0;
 sig_atomic_t acc_loop=0;
 
+int ch_to_fi(int ch){
+  int fi,ci;
+  for(fi=0,ci=0;fi<inputs;fi++){
+    if(ch>=ci && ch<ci+channels[fi]) return fi;
+    ci+=channels[fi];
+  }
+  return -1;
+}
+
+typedef struct {
+  fftwf_plan fft_f[2];
+  fftwf_plan fft_i[2];
+
+  float *lap[2];
+  int laphead;
+  int lapfill;
+  float *window_f;
+  float *window_i;
+  int rate;
+  int holdoffd;
+  int spansamples;
+
+  int sample_n;
+  int oversample_n;
+  int oversample_factor;
+
+  /* distance back from head of blockbuffer */
+  int lappos; /* head of the lap */
+  int triggersearch; /* point to start next search */
+} triggerstate;
+
 static int metareload = 0;
+static triggerstate *trigger=NULL;
+static int trigger_channel=0;
+static int trigger_type=0;
+static double lasttrigger=-1;
 
+static triggerstate *trigger_create(int rate, int holdoffd, int span){
+  triggerstate *t = calloc(1,sizeof(*t));
+  int i;
+
+  /* hardwired but reasonable */
+  t->sample_n = 512;
+  t->oversample_factor = 16;
+  t->oversample_n = t->sample_n * t->oversample_factor;
+
+  t->rate=rate;
+  t->holdoffd=holdoffd;
+  t->spansamples=rate/1000000.*span;
+
+  for(i=0; i<2; i++){
+    t->lap[i] = fftwf_malloc((t->oversample_n*2+2)*sizeof(**t->lap));
+    t->fft_f[i] = fftwf_plan_dft_r2c_1d(t->sample_n*2,t->lap[i],
+                                        (fftwf_complex *)t->lap[i],
+                                        FFTW_ESTIMATE);
+    t->fft_i[i] = fftwf_plan_dft_c2r_1d(t->oversample_n*2,
+                                        (fftwf_complex *)t->lap[i],t->lap[i],
+                                        FFTW_ESTIMATE);
+  }
+
+  /* highly redundant; make it work first */
+  t->window_f = malloc(t->sample_n*sizeof(*t->window_f));
+  t->window_i = malloc(t->oversample_n*sizeof(*t->window_i));
+  for(i=0;i<t->sample_n;i++)
+    t->window_f[i] = sin(i*M_PI/t->sample_n);
+  for(i=0;i<t->oversample_n;i++)
+    t->window_i[i] = sin(i*M_PI/t->oversample_n);
+
+  return t;
+}
+
+static void trigger_destroy(triggerstate *t){
+  if(t){
+    int i;
+    for(i=0;i<2;i++){
+      if(t->lap[i]) free(t->lap[i]);
+      if(t->fft_f[i]) fftwf_destroy_plan(t->fft_f[i]);
+      if(t->fft_i[i]) fftwf_destroy_plan(t->fft_i[i]);
+    }
+    if(t->window_f)free(t->window_f);
+    if(t->window_i)free(t->window_i);
+    free(t);
+  }
+}
+
+/* returns nonzero if there's enough data to bother trying a trigger search */
+static int trigger_advance(triggerstate *t, int blockslice){
+  t->lappos += blockslice;
+  t->triggersearch += blockslice * t->oversample_factor;
+  if(lasttrigger>=0)
+    lasttrigger += (double)blockslice/t->rate;
+  if(lasttrigger>1) lasttrigger=-1;
+
+  if(t->triggersearch < t->spansamples * t->oversample_factor) return 0;
+  if(t->triggersearch < t->oversample_n/2) return 0;
+  return 1;
+}
+
+static int trigger_try_lap(triggerstate *t, float *blockbuffer, int bn){
+  int i;
+
+  int lap_end = t->lappos * t->oversample_factor;
+  int overlap_end = (t->lappos + t->sample_n/2) * t->oversample_factor;
+
+  /* don't process any oversamples/overlaps that we don't need to */
+  while(t->triggersearch < lap_end-t->sample_n/2){
+    t->lapfill=0;
+
+    if(t->lappos<t->sample_n/2) return -1; /* out of data, off the deep end */
+
+    t->lappos -= t->sample_n/2;
+    lap_end -= t->oversample_n/2;
+    overlap_end -= t->oversample_n/2;
+  }
+
+  if(t->lapfill==0){
+    if(t->lappos<t->sample_n/2) return -1; /* out of data, off the deep end */
+
+    /* advance buffers and oversample */
+    t->lapfill++;
+    t->lappos-=t->sample_n/2;
+
+    if(t->lappos > bn-t->sample_n){
+      /* we got *way* behind. ~restart at head */
+      t->lappos = t->sample_n/2;
+    }
+
+    /* copy/window */
+    float *work = t->lap[t->laphead];
+    float *src = blockbuffer+bn-t->lappos-t->sample_n;
+    memset(work,0, sizeof(**t->lap)*(t->oversample_n*2+2));
+
+    work+=t->sample_n/2;
+    for(i=0;i<t->sample_n;i++)
+      work[i] = src[i]*t->window_f[i];
+
+    /* transform */
+    fftwf_execute(t->fft_f[t->laphead]);
+    fftwf_execute(t->fft_i[t->laphead]);
+
+    /* rewindow */
+    work-=t->sample_n/2;
+    work+=t->oversample_n/2;
+    for(i=0;i<t->oversample_n;i++)
+      work[i] *= t->window_i[i];
+
+    /* switch */
+    t->laphead = ((t->laphead+1)&1);
+
+    /* leave the edges unzeroed; they're not used past this */
+  }
+
+  if(t->lappos<t->sample_n/2) return -1; /* out of data, off the deep end */
+
+  {
+    /* advance buffers and oversample into head */
+    t->lapfill++;
+    t->lappos-=t->sample_n/2;
+
+    /* copy/window */
+    float *work = t->lap[t->laphead];
+    float *src = blockbuffer+bn-t->lappos-t->sample_n;
+    memset(work,0, sizeof(**t->lap)*(t->oversample_n*2+2));
+
+    work+=t->sample_n/2;
+    for(i=0;i<t->sample_n;i++)
+      work[i] = src[i]*t->window_f[i];
+
+    /* transform */
+    fftwf_execute(t->fft_f[t->laphead]);
+    fftwf_execute(t->fft_i[t->laphead]);
+
+    /* rewindow */
+    work-=t->sample_n/2;
+    work+=t->oversample_n/2;
+    for(i=0;i<t->oversample_n;i++)
+      work[i] *= t->window_i[i];
+
+    /* overlap-add */
+    work-=t->oversample_n/2;
+    float *a = t->lap[(t->laphead+1)&1] + t->oversample_n;
+    float *b = work+t->oversample_n/2;
+    for(i=0;i<t->oversample_n/2;i++)
+      work[i] = a[i]+b[i];
+
+    /* switch */
+    t->laphead = ((t->laphead+1)&1);
+  }
+
+  return 0;
+}
+
+/* returns location of trigger as number of seconds behind head of
+   blockbuffer, or <0 if none */
+static float trigger_search(triggerstate *t, float *blockbuffer, int bn, int triggertype){
+  if(t->lappos+t->sample_n>bn || t->lappos<0){
+    /* we got behind */
+    t->lappos=bn-t->sample_n;
+    t->lapfill=0;
+  }
+
+  while(1){
+    if(t->lapfill<2){
+      if(trigger_try_lap(t,blockbuffer, blocksize)){
+        return -1; /* need more blockbuffer data */
+      }
+    }else{
+
+      /* trigger limit can't reach past head + span */
+      int span_begin = t->spansamples * t->oversample_factor;
+
+      if(t->triggersearch <= span_begin){
+        return -1; /* need more blockbuffer data */
+      }else{
+        /* distance back from logical head of the sample buffer to lap tail */
+        int overlap_end = (t->lappos + t->sample_n/2) * t->oversample_factor;
+
+        if(t->triggersearch <= overlap_end){
+          if(trigger_try_lap(t,blockbuffer,blocksize)){
+            return -1; /* need more blockbuffer data */
+          }
+        }else{
+
+          int overlap_begin = (t->lappos + t->sample_n) * t->oversample_factor;
+          if(t->triggersearch > overlap_begin){
+            /* fell off beginning of overlap area-- we got behind processing */
+            t->triggersearch = overlap_begin;
+          }
+
+          float *lap = t->lap[(t->laphead+1)&1];
+          int lapoff = t->oversample_n - t->triggersearch +
+            t->lappos*t->oversample_factor;
+
+          float prev = (lapoff>0 ? lap[lapoff-1] : t->lap[t->laphead][t->oversample_n-1]);
+          lap+=lapoff;
+
+          while(t->triggersearch>overlap_end &&
+                t->triggersearch>span_begin){
+
+            switch(triggertype){
+            case 1: /* +0 */
+              if(prev<=0 && *lap>0){
+                /* linear interpolation can further refine the result in most cases */
+                float x = *lap / (*lap-prev);
+                float ret = (t->triggersearch+x)/
+                  (float)(t->rate*t->oversample_factor);
+                /* apply holdoff */
+                t->triggersearch -= t->rate*t->oversample_factor/t->holdoffd;
+                /* return trigger */
+                return ret;
+              }
+              break;
+            case 2: /* -0 */
+              if(prev>0 && *lap<=0){
+                /* linear interpolation can further refine the result in most cases */
+                float x = *lap / (*lap-prev);
+                float ret = (t->triggersearch+x)/
+                  (float)(t->rate*t->oversample_factor);
+                /* apply holdoff */
+                t->triggersearch -= t->rate*t->oversample_factor/t->holdoffd;
+                /* return trigger */
+                return ret;
+              }
+              break;
+            }
+            t->triggersearch--;
+            prev=*lap++;
+          }
+        }
+      }
+    }
+  }
+}
+
 static void process_init(){
   if(blocksize==0){
     int fi;
@@ -54,10 +328,12 @@
     acc_rewind=0;
 
     ret=input_read(acc_loop,1);
-    if(ret==0) break;
+    if(ret==0){
+      pthread_mutex_unlock(&blockbuffer_mutex);
+      break;
+    }
     if(ret==-1){
       /* a pipe returned EOF; attempt reopen */
-      pthread_mutex_lock(&blockbuffer_mutex);
       if(pipe_reload()){
         blocksize=0;
         metareload=1;
@@ -70,8 +346,17 @@
       }
     }
 
-    write(eventpipe[1],"",1);
-
+    /* advance trigger [if any] by blockslice */
+    if(trigger){
+      int ret;
+      int fi = ch_to_fi(trigger_channel);
+      ret=trigger_advance(trigger,blockslice_adv[fi]);
+      pthread_mutex_unlock(&blockbuffer_mutex);
+      if(ret)write(eventpipe[1],"",1);
+    }else{
+      pthread_mutex_unlock(&blockbuffer_mutex);
+      write(eventpipe[1],"",1);
+    }
   }
 
   /* eof on all inputs */
@@ -80,11 +365,42 @@
   return NULL;
 }
 
+/* everything below called from UI thread only */
+
+void set_trigger(int ttype, int tch, int sliced, int span){
+  pthread_mutex_lock(&blockbuffer_mutex);
+  if(!blockbuffer){
+    pthread_mutex_unlock(&blockbuffer_mutex);
+    return;
+  }
+
+  /* if trigger params have changed, clear out current state */
+  if(trigger_type!=ttype || trigger_channel!=tch){
+    trigger_destroy(trigger);
+    trigger=NULL;
+  }
+
+  /* set up trigger if one if called for */
+  if(ttype && !trigger){
+    int fi = ch_to_fi(tch);
+    trigger_channel = tch;
+    trigger_type = ttype;
+    trigger = trigger_create(rate[fi], sliced, span);
+    blockslice_frac = 200;
+  }else{
+    blockslice_frac = sliced;
+  }
+  lasttrigger=-1;
+  pthread_mutex_unlock(&blockbuffer_mutex);
+}
+
+
 static fetchdata fetch_ret;
 fetchdata *process_fetch(int span, int scale, float range,
                          int *process_in){
   int fi,i,k,ch;
   int process[total_ch];
+  int samppos[total_ch];
 
   pthread_mutex_lock(&blockbuffer_mutex);
   if(!blockbuffer){
@@ -103,6 +419,12 @@
       free(fetch_ret.active);
       fetch_ret.active=NULL;
     }
+    if(trigger){
+      int sliced = trigger->holdoffd;
+      trigger_destroy(trigger);
+      trigger = NULL;
+      set_trigger(trigger_type, trigger_channel, sliced, span);
+    }
   }
 
   if(!fetch_ret.data){
@@ -144,13 +466,63 @@
   fetch_ret.reload=metareload;
   metareload=0;
 
+  {
+    float sec=-1;
+    if(trigger){
+      int fi = ch_to_fi(trigger_channel);
+
+      /* read position determined by trigger */
+      if(process_active || lasttrigger<0){
+        sec = trigger_search(trigger, blockbuffer[trigger_channel], blocksize, trigger_type);
+      }
+      if(sec<0){
+        sec = lasttrigger;
+        /* lasttrigger follows the channel, not the master clock */
+        sec += (blockslice_count/1000000.) - (blockslice_cursor[fi]/(float)rate[fi]);
+      }else{
+        lasttrigger = sec;
+        /* position returned from the trigger search is in terms of the
+           trigger channel, not the blockslice cursor; convert it */
+        sec += (blockslice_count/1000000.) - (blockslice_cursor[fi]/(float)rate[fi]);
+
+      }
+    }
+
+    /* determine sample offsets and sample process position */
+    /* fractional sample offsets come from two sources:
+       1) non-integer ratio of sample clock : sweep period
+       2) non-sampled-aligned trigger
+
+       When a trigger is active, the zero time position on the graph
+       is aligned to the trigger, and all else is adjusted to match.
+       Offsets must be in the range (-2:-1] (we start one sample early
+       when drawing); the read sample position of each channel is
+       massaged to bring this in line if necessary */
+
+    for(fi=0;fi<inputs;fi++){
+      if(blockslice_eof[fi] || sec<0){
+        /* at EOF or when untriggered, we freeze to last sample */
+        float sample_pos = rate[fi]/1000000.*span;
+        samppos[fi] = blocksize - ceilf(sample_pos) ;
+        fetch_ret.offsets[fi] = sample_pos - ceilf(sample_pos);
+      }else{
+        float head_sec = blockslice_cursor[fi]/(float)rate[fi];
+        float read_sec = blockslice_count/1000000.;
+        float head_offset = (head_sec-read_sec)*rate[fi];
+        float sample_pos = sec*rate[fi] + head_offset;
+        samppos[fi] = blocksize - ceilf(sample_pos);
+        fetch_ret.offsets[fi] = sample_pos - ceilf(sample_pos);
+      }
+    }
+  }
+
   /* by channel */
   ch=0;
   for(fi=0;fi<inputs;fi++){
-    int spann = ceil(rate[fi]/1000000.*span)+1;
+    int spann = ceil(rate[fi]/1000000.*span)+2;
     for(i=ch;i<ch+channels[fi];i++){
       if(process[i]){
-        int offset=blocksize-spann;
+        int offset = samppos[fi];
         float *plotdatap=fetch_ret.data[i];
         float *data=blockbuffer[i]+offset;
         if(scale){

Modified: trunk/spectrum/waveform.h
===================================================================
--- trunk/spectrum/waveform.h	2012-07-02 16:05:53 UTC (rev 18437)
+++ trunk/spectrum/waveform.h	2012-07-03 06:15:11 UTC (rev 18438)
@@ -81,6 +81,7 @@
 extern void *process_thread(void *dummy);
 extern fetchdata *process_fetch(int span, int scale, float range,
                                 int *process);
+extern void set_trigger(int ttype, int tch, int sliced, int span);
 
 extern sig_atomic_t acc_rewind;
 extern sig_atomic_t acc_loop;



More information about the commits mailing list