From 38fea4e39084aa09ee5d8e367e99b1b751c0c764 Mon Sep 17 00:00:00 2001 From: drowe67 Date: Thu, 5 Jul 2012 22:14:37 +0000 Subject: [PATCH] first attempt at portaudio integration but decided to step back and do some unit tests git-svn-id: https://svn.code.sf.net/p/freetel/code@579 01035d8c-6547-0410-b346-abe4f91aad63 --- codec2-dev/fltk/Makefile | 6 +- codec2-dev/fltk/fl_fdmdv.cxx | 352 +++++++++++++++++++++++++++++------ 2 files changed, 303 insertions(+), 55 deletions(-) diff --git a/codec2-dev/fltk/Makefile b/codec2-dev/fltk/Makefile index 7a3824d4..004ee46b 100644 --- a/codec2-dev/fltk/Makefile +++ b/codec2-dev/fltk/Makefile @@ -1,14 +1,14 @@ -# Requires FLTK 1.3 +# Requires FLTK 1.3 & Portaudio V19 FLTK_CFLAGS += $(shell fltk-config --ldstaticflags) CFLAGS = -O3 -g - +LIBS = ../src/.libs/libcodec2.a -lm -lrt -lportaudio -pthread LC2POC_C = fl_fdmdv.cxx all: fl_fdmdv fl_fdmdv: Makefile $(LC2POC_C) - g++ $(LC2POC_C) -I../src/ -o fl_fdmdv $(CFLAGS) $(FLTK_CFLAGS) ../src/.libs/libcodec2.a + g++ $(LC2POC_C) -I../src/ -o fl_fdmdv $(CFLAGS) $(FLTK_CFLAGS) $(LIBS) clean: rm -f fl_fdmdv diff --git a/codec2-dev/fltk/fl_fdmdv.cxx b/codec2-dev/fltk/fl_fdmdv.cxx index 7ef14bcd..39f88cfc 100644 --- a/codec2-dev/fltk/fl_fdmdv.cxx +++ b/codec2-dev/fltk/fl_fdmdv.cxx @@ -7,60 +7,110 @@ FDMDV GUI displays. */ +#include +#include +#include +#include + #include #include #include #include #include -#include -#include -#include +#include + +#include "portaudio.h" #include "fdmdv.h" -#define MIN_DB -40.0 +#define MIN_DB -40.0 #define MAX_DB 0.0 -#define BETA 0.1 +#define BETA 0.1 // constant for time averageing spectrum data #define MIN_HZ 0 #define MAX_HZ 4000 #define WATERFALL_SECS_Y 5 // number of seconds respresented by y axis of waterfall -#define DT 0.02 // time between samples -#define FS 8000 +#define DT 0.02 // time between samples +#define FS 8000 // FDMDV modem sample rate #define SCATTER_MEM (FDMDV_NSYM)*50 #define SCATTER_X_MAX 3.0 #define SCATTER_Y_MAX 3.0 +// main window params + #define W 1200 #define W3 (W/3) #define H 600 #define H2 (H/2) #define SP 20 +#define SOUND_CARD_FS 48000 + +// forward class declarations class Spectrum; class Waterfall; class Scatter; class Scalar; +// Globals -------------------------------------- + char *fin_name = NULL; +char *sound_dev_name = NULL; FILE *fin = NULL; struct FDMDV *fdmdv; +float av_mag[FDMDV_NSPEC]; // shared between a few classes + +// GUI variables -------------------------------- + Fl_Group *agroup; Fl_Window *window; +Fl_Window *zoomSpectrumWindow = NULL; +Fl_Window *zoomWaterfallWindow = NULL; Spectrum *aSpectrum; +Spectrum *aZoomedSpectrum; Waterfall *aWaterfall; +Waterfall *aZoomedWaterfall; Scatter *aScatter; Scalar *aTimingEst; Scalar *aFreqEst; Scalar *aSNR; +int zoom_spectrum = 0; -float av_mag[FDMDV_NSPEC]; // shared between a few classes +// Main processing loop states ------------------ float Ts = 0.0; +int nbuf = 0; +short rx_fdm_scaled[7*FDMDV_NOM_SAMPLES_PER_FRAME]; +int nin = FDMDV_NOM_SAMPLES_PER_FRAME; + +// Portaudio states ----------------------------- + +PaStreamParameters inputParameters; +PaStream *stream = NULL; +PaError err; + +// Class for each window type ------------------ class Spectrum: public Fl_Box { protected: + int handle(int event) { + + // detect a left mouse down if inside the spectrum window + + if ((event == FL_NO_EVENT) && (Fl::event_button() == 1)) { + if ((Fl::event_x() > x()) && (Fl::event_x() < (x() + w())) && + (Fl::event_y() > y()) && (Fl::event_y() < (y() + h()))) { + + // show zoomed spectrum window + + zoomSpectrumWindow->show(); + } + + } + return 0; + } + void draw() { float x_px_per_point = 0.0; float y_px_per_dB = 0.0; @@ -165,6 +215,23 @@ protected: pixel_buf[i] = 0; } + int handle(int event) { + + // detect a left mouse down if inside the window + + if ((event == FL_NO_EVENT) && (Fl::event_button() == 1)) { + if ((Fl::event_x() > x()) && (Fl::event_x() < (x() + w())) && + (Fl::event_y() > y()) && (Fl::event_y() < (y() + h()))) { + + // show zoomed spectrum window + + zoomWaterfallWindow->show(); + } + + } + return 0; + } + // map val to a rgb colour // from http://eddiema.ca/2011/01/21/c-sharp-heatmaps/ @@ -283,7 +350,7 @@ class Scatter: public Fl_Box { protected: COMP mem[SCATTER_MEM]; COMP new_samples[FDMDV_NSYM]; - int prev_w, prev_h; + int prev_w, prev_h, prev_x, prev_y; void draw() { float x_scale; @@ -294,10 +361,10 @@ protected: /* detect resizing of window */ - if ((h() != prev_h) || (w() != prev_w)) { + if ((h() != prev_h) || (w() != prev_w) || (x() != prev_x) || (y() != prev_y)) { fl_color(FL_BLACK); fl_rectf(x(),y(),w(),h()); - prev_h = h(); prev_w = w(); + prev_h = h(); prev_w = w(); prev_x = x(); prev_y = y(); } fl_push_clip(x(),y(),w(),h()); @@ -346,7 +413,7 @@ public: mem[i].imag = 0.0; } - prev_w = 0; prev_h = 0; + prev_w = 0; prev_h = 0; prev_x = 0; prev_y = 0; }; void add_new_samples(COMP samples[]) { @@ -367,22 +434,31 @@ protected: int x_max, y_max; float *mem; /* array of x_max samples */ float new_sample; - int index; - int prev_w, prev_h; + int index, step; + int prev_w, prev_h, prev_x, prev_y; + + int clip(int y1) { + if (y1 > (h()/2 - 10)) + y1 = h()/2 - 10; + if (y1 < -(h()/2 - 10)) + y1 = -(h()/2 - 10); + return y1; + } void draw() { float x_scale; float y_scale; - int i, j, x1, y1; + int i, j, x1, y1, x2, y2; + char label[100]; Fl_Box::draw(); /* detect resizing of window */ - if ((h() != prev_h) || (w() != prev_w)) { + if ((h() != prev_h) || (w() != prev_w) || (x() != prev_x) || (y() != prev_y)) { fl_color(FL_BLACK); fl_rectf(x(),y(),w(),h()); - prev_h = h(); prev_w = w(); + prev_h = h(); prev_w = w(); prev_x = x(); prev_y = y(); } fl_push_clip(x(),y(),w(),h()); @@ -394,25 +470,60 @@ protected: fl_color(FL_BLACK); x1 = x_scale * index + x(); - y1 = -y_scale * mem[index] + y() + h()/2; + y1 = y_scale * mem[index]; + y1 = clip(y1); + y1 = y() + h()/2 - y1; fl_point(x1, y1); // draw new sample fl_color(FL_GREEN); x1 = x_scale * index + x(); - y1 = -y_scale * new_sample + y() + h()/2; + y1 = y_scale * new_sample; + y1 = clip(y1); + y1 = y() + h()/2 - y1; fl_point(x1, y1); mem[index] = new_sample; - fl_pop_clip(); index++; if (index >= x_max) index = 0; + + // y axis graticule + + step = 10; + + while ((2.0*y_max/step) > 10) + step *= 2.0; + while ((2.0*y_max/step) < 4) + step /= 2.0; + + fl_color(FL_DARK_GREEN); + fl_line_style(FL_DOT); + for(i=-y_max; i= nin) { - if (fread(rx_fdm_scaled, sizeof(short), nin, fin) == nin) { Ts += (float)nin/FS; // demod per frame processing @@ -471,12 +635,14 @@ void idle(void*) { for(i=0; i= 0); - // get stats and spectrum and update + // get spectrum, stats and update windows - fdmdv_get_demod_stats(fdmdv, &stats); fdmdv_get_rx_spectrum(fdmdv, rx_spec, rx_fdm, nin_prev); + fdmdv_get_demod_stats(fdmdv, &stats); new_data(rx_spec); aScatter->add_new_samples(stats.rx_symbols); aTimingEst->add_new_sample(stats.rx_timing); @@ -487,15 +653,26 @@ void idle(void*) { if (Ts >= DT) { Ts -= DT; - aSpectrum->redraw(); - aWaterfall->redraw(); - aScatter->redraw(); - aTimingEst->redraw(); - aFreqEst->redraw(); - aSNR->redraw(); + if (!zoomSpectrumWindow->shown() && !zoomWaterfallWindow->shown()) { + aSpectrum->redraw(); + aWaterfall->redraw(); + aScatter->redraw(); + aTimingEst->redraw(); + aFreqEst->redraw(); + aSNR->redraw(); + } + if (zoomSpectrumWindow->shown()) + aZoomedSpectrum->redraw(); + if (zoomWaterfallWindow->shown()) + aZoomedWaterfall->redraw(); } + + // shift buffer + + for(i=0; i= argc) + return 0; + sound_dev_name = argv[i+1]; + i += 2; + return 2; + } return 0; } @@ -515,46 +699,110 @@ int main(int argc, char **argv) { i = 1; Fl::args(argc,argv,i,arg_callback); - - if (argc != 3) { - printf("usage: %s -i inputFdmdvRawFile\n", argv[0]); + + if (argc == 1) { + printf("usage: %s [-i inputFdmdvRawFile] [-s inputSoundDevice]\n", argv[0]); exit(0); } - fin = fopen(fin_name,"rb"); - if (fin == NULL) { - fprintf(stderr, "Error opening input fdmdv raw file %s\n", argv[1]); - exit(1); + if (fin_name != NULL) { + fin = fopen(fin_name,"rb"); + if (fin == NULL) { + fprintf(stderr, "Error opening input fdmdv raw file %s\n", fin_name); + exit(1); + } + } + + if (sound_dev_name != NULL) { + err = Pa_Initialize(); + if( err != paNoError ) goto pa_error; + inputParameters.device = Pa_GetDefaultInputDevice(); /* default input device */ + printf( "Input device # %d.\n", inputParameters.device ); + printf( "Input LL: %g s\n", Pa_GetDeviceInfo( inputParameters.device )->defaultLowInputLatency ); + printf( "Input HL: %g s\n", Pa_GetDeviceInfo( inputParameters.device )->defaultHighInputLatency ); + inputParameters.channelCount = 1; + inputParameters.sampleFormat = paInt16; + inputParameters.suggestedLatency = Pa_GetDeviceInfo( inputParameters.device )->defaultHighInputLatency ; + inputParameters.hostApiSpecificStreamInfo = NULL; + + err = Pa_OpenStream( + &stream, + &inputParameters, + NULL, // no output + /*SOUND_CARD_FS*/8000, + FDMDV_NOM_SAMPLES_PER_FRAME, + paClipOff, + NULL, // no callback, use blocking API + NULL ); // no callback, so no callback userData + if( err != paNoError ) goto pa_error; + err = Pa_StartStream( stream ); + if( err != paNoError ) goto pa_error; } - for(i=0; isize_range(100, 100); - window->resizable(); + //window->size_range(100, 100); + //window->resizable(); aSpectrum = new Spectrum(SP, SP, W3-2*SP, H2); aWaterfall = new Waterfall(SP, SP+H2+SP+SP, W3-2*SP, H2); aScatter = new Scatter(W3+SP, SP, W3-2*SP, H2); aTimingEst = new Scalar(W3+SP, SP+H2+SP+SP, W3-2*SP, H2, 100, 80, "Timing Est"); aFreqEst = new Scalar(2*W3+SP, SP, W3-2*SP, H2, 100, 100, "Frequency Est"); - aSNR = new Scalar(2*W3+SP, SP+H2+SP+SP, W3-2*SP, H2, 100, 40, "SNR"); + aSNR = new Scalar(2*W3+SP, SP+H2+SP+SP, W3-2*SP, H2, 100, 10, "SNR"); fdmdv = fdmdv_create(); Fl::add_idle(idle); window->end(); + // set up zoomed spectrum window + + zoomSpectrumWindow = new Fl_Window(W, H, "Spectrum"); + aZoomedSpectrum = new Spectrum(SP, SP, W-2*SP, H-2*SP); + zoomSpectrumWindow->end(); + + // set up zoomed waterfall window + + zoomWaterfallWindow = new Fl_Window(W, H, "Waterfall"); + aZoomedWaterfall = new Waterfall(SP, SP, W-2*SP, H-2*SP); + zoomWaterfallWindow->end(); + + // show the main window and start running + window->show(argc, argv); - ret = Fl::run(); + Fl::run(); + + if (sound_dev_name != NULL) { + err = Pa_StopStream( stream ); + if( err != paNoError ) goto pa_error; + Pa_CloseStream( stream ); + Pa_Terminate(); + } fdmdv_destroy(fdmdv); - fclose(fin); + if (fin_name != NULL) + fclose(fin); return ret; + + // Portaudio error handling + +pa_error: + if( stream ) { + Pa_AbortStream( stream ); + Pa_CloseStream( stream ); + } + Pa_Terminate(); + fprintf( stderr, "An error occured while using the portaudio stream\n" ); + fprintf( stderr, "Error number: %d\n", err ); + fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) ); + return -1; } -- 2.25.1