[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