From cdde3076401d92ebb7dbb3888c4da856fc83e7e2 Mon Sep 17 00:00:00 2001 From: Kamal Mostafa Date: Tue, 24 May 2011 21:36:49 -0700 Subject: [PATCH] minimodem, miniscope: initial implementation --- configure | 27 ++-- configure.ac | 3 +- src/Makefile.am | 7 +- src/Makefile.in | 22 ++- src/junkprog.c | 11 -- src/minimodem.c | 407 ++++++++++++++++++++++++++++++++++++++++++++++++ src/miniscope.c | 206 ++++++++++++++++++++++++ 7 files changed, 652 insertions(+), 31 deletions(-) delete mode 100644 src/junkprog.c create mode 100644 src/minimodem.c create mode 100644 src/miniscope.c diff --git a/configure b/configure index 0ab12df..5bf57d5 100755 --- a/configure +++ b/configure @@ -3395,16 +3395,19 @@ if test -n "$DEPS_CFLAGS"; then elif test -n "$PKG_CONFIG"; then if test -n "$PKG_CONFIG" && \ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \" - glib-2.0 + libpulse-simple + fftw3f \""; } >&5 ($PKG_CONFIG --exists --print-errors " - glib-2.0 + libpulse-simple + fftw3f ") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then pkg_cv_DEPS_CFLAGS=`$PKG_CONFIG --cflags " - glib-2.0 + libpulse-simple + fftw3f " 2>/dev/null` else pkg_failed=yes @@ -3417,16 +3420,19 @@ if test -n "$DEPS_LIBS"; then elif test -n "$PKG_CONFIG"; then if test -n "$PKG_CONFIG" && \ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \" - glib-2.0 + libpulse-simple + fftw3f \""; } >&5 ($PKG_CONFIG --exists --print-errors " - glib-2.0 + libpulse-simple + fftw3f ") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then pkg_cv_DEPS_LIBS=`$PKG_CONFIG --libs " - glib-2.0 + libpulse-simple + fftw3f " 2>/dev/null` else pkg_failed=yes @@ -3448,18 +3454,21 @@ else fi if test $_pkg_short_errors_supported = yes; then DEPS_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors " - glib-2.0 + libpulse-simple + fftw3f " 2>&1` else DEPS_PKG_ERRORS=`$PKG_CONFIG --print-errors " - glib-2.0 + libpulse-simple + fftw3f " 2>&1` fi # Put the nasty error message in config.log where it belongs echo "$DEPS_PKG_ERRORS" >&5 as_fn_error $? "Package requirements ( - glib-2.0 + libpulse-simple + fftw3f ) were not met: $DEPS_PKG_ERRORS diff --git a/configure.ac b/configure.ac index 631d232..3d2f0d4 100644 --- a/configure.ac +++ b/configure.ac @@ -29,7 +29,8 @@ AC_PROG_CC # Checks for libraries. PKG_CHECK_MODULES(DEPS, [ - glib-2.0 + libpulse-simple + fftw3f ]) AC_SUBST(DEPS_CFLAGS) AC_SUBST(DEPS_LIBS) diff --git a/src/Makefile.am b/src/Makefile.am index 473d775..3388390 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -3,10 +3,11 @@ AM_CFLAGS = -Wall -Werror INCLUDES = $(DEPS_CFLAGS) -bin_PROGRAMS = minimodem +bin_PROGRAMS = minimodem miniscope minimodem_LDADD = $(DEPS_LIBS) -minimodem_VERSION = 0.1.1 -minimodem_SOURCES = junkprog.c +minimodem_SOURCES = minimodem.c +miniscope_LDADD = $(DEPS_LIBS) +miniscope_SOURCES = miniscope.c diff --git a/src/Makefile.in b/src/Makefile.in index 9841d5b..a8a386d 100644 --- a/src/Makefile.in +++ b/src/Makefile.in @@ -32,7 +32,7 @@ POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : -bin_PROGRAMS = minimodem$(EXEEXT) +bin_PROGRAMS = minimodem$(EXEEXT) miniscope$(EXEEXT) subdir = src DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 @@ -45,10 +45,13 @@ CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = am__installdirs = "$(DESTDIR)$(bindir)" PROGRAMS = $(bin_PROGRAMS) -am_minimodem_OBJECTS = junkprog.$(OBJEXT) +am_minimodem_OBJECTS = minimodem.$(OBJEXT) minimodem_OBJECTS = $(am_minimodem_OBJECTS) am__DEPENDENCIES_1 = minimodem_DEPENDENCIES = $(am__DEPENDENCIES_1) +am_miniscope_OBJECTS = miniscope.$(OBJEXT) +miniscope_OBJECTS = $(am_miniscope_OBJECTS) +miniscope_DEPENDENCIES = $(am__DEPENDENCIES_1) DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) depcomp = $(SHELL) $(top_srcdir)/depcomp am__depfiles_maybe = depfiles @@ -57,8 +60,8 @@ COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) CCLD = $(CC) LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ -SOURCES = $(minimodem_SOURCES) -DIST_SOURCES = $(minimodem_SOURCES) +SOURCES = $(minimodem_SOURCES) $(miniscope_SOURCES) +DIST_SOURCES = $(minimodem_SOURCES) $(miniscope_SOURCES) ETAGS = etags CTAGS = ctags DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) @@ -153,8 +156,9 @@ top_srcdir = @top_srcdir@ AM_CFLAGS = -Wall -Werror INCLUDES = $(DEPS_CFLAGS) minimodem_LDADD = $(DEPS_LIBS) -minimodem_VERSION = 0.1.1 -minimodem_SOURCES = junkprog.c +minimodem_SOURCES = minimodem.c +miniscope_LDADD = $(DEPS_LIBS) +miniscope_SOURCES = miniscope.c all: all-am .SUFFIXES: @@ -229,6 +233,9 @@ clean-binPROGRAMS: minimodem$(EXEEXT): $(minimodem_OBJECTS) $(minimodem_DEPENDENCIES) @rm -f minimodem$(EXEEXT) $(LINK) $(minimodem_OBJECTS) $(minimodem_LDADD) $(LIBS) +miniscope$(EXEEXT): $(miniscope_OBJECTS) $(miniscope_DEPENDENCIES) + @rm -f miniscope$(EXEEXT) + $(LINK) $(miniscope_OBJECTS) $(miniscope_LDADD) $(LIBS) mostlyclean-compile: -rm -f *.$(OBJEXT) @@ -236,7 +243,8 @@ mostlyclean-compile: distclean-compile: -rm -f *.tab.c -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/junkprog.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/minimodem.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/miniscope.Po@am__quote@ .c.o: @am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< diff --git a/src/junkprog.c b/src/junkprog.c deleted file mode 100644 index 2b1aac9..0000000 --- a/src/junkprog.c +++ /dev/null @@ -1,11 +0,0 @@ -#define G_LOG_DOMAIN "minimodem" - -#include - -int -main() -{ - g_debug("hello world from junkprog"); - return 0; -} - diff --git a/src/minimodem.c b/src/minimodem.c new file mode 100644 index 0000000..46d1c47 --- /dev/null +++ b/src/minimodem.c @@ -0,0 +1,407 @@ +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + + +static inline +float +band_mag( fftwf_complex * const cplx, unsigned int band, float scalar ) +{ + float re = cplx[band][0]; + float im = cplx[band][1]; + float mag = hypot(re, im) * scalar; + return mag; +} + +int main(int argc, char*argv[]) { + /* The sample type to use */ + static const pa_sample_spec ss = { + .format = PA_SAMPLE_FLOAT32, + .rate = 48000, // pulseaudio will resample its configured audio rate + + // .channels = 2 // 2 channel stereo + .channels = 1 // pulseaudio will downmix (additively) to 1 channel + // .channels = 3 // 2 channel stereo + 1 mixed channel + }; + int ret = 1; + int error; + + + + /* + * Bell 103: mark=1270 space=1070 + * ITU-T V.21: mark=1280 space=1080 + */ + unsigned int bfsk_mark_f = 1270; + unsigned int bfsk_space_f = 1070; + + if ( argc < 2 ) { + fprintf(stderr, "usage: minimodem baud_rate [ mark_hz space_hz ]\n"); + return 1; + } + + unsigned char textscope = 0; + int argi = 1; + if ( argi < argc && strcmp(argv[argi],"-s") == 0 ) { + textscope = 1; + argi++; + } + + unsigned int decode_rate = atoi(argv[argi++]); + + if ( argi < argc ) { + assert(argc-argi == 2); + bfsk_mark_f = atoi(argv[argi++]); + bfsk_space_f = atoi(argv[argi++]); + } + + unsigned int sample_rate = ss.rate; + + unsigned int band_width = decode_rate / 2; + + unsigned int bfsk_mark_band = (bfsk_mark_f +(float)band_width/2) / band_width; + unsigned int bfsk_space_band = (bfsk_space_f +(float)band_width/2) / band_width; + + + if ( bfsk_mark_band == 0 || bfsk_space_band == 0 ) { + fprintf(stderr, __FILE__": mark or space band is at dsp DC\n"); +// return 1; + } + if ( bfsk_mark_band == bfsk_space_band ) { + fprintf(stderr, __FILE__": inadequate mark/space separation\n"); + return 1; + } + + + /* Create the recording stream */ + pa_simple *s; + s = pa_simple_new(NULL, argv[0], PA_STREAM_RECORD, NULL, "record", &ss, NULL, NULL, &error); + if ( !s ) { + fprintf(stderr, __FILE__": pa_simple_new() failed: %s\n", pa_strerror(error)); + return 1; + } + + int pa_samplesize = pa_sample_size(&ss); + int pa_framesize = pa_frame_size(&ss); + int pa_nchannels = ss.channels; + + assert( pa_framesize == pa_samplesize * pa_nchannels ); + + + /* Create the FFT plan */ + fftwf_plan fftplan; + + int fftsize = sample_rate / band_width; + + if ( fftsize & 1 ) + fprintf(stderr, __FILE__": WARNING: fftsize %u is not even\n", fftsize); + + unsigned int nbands = fftsize / 2 + 1; + + float *fftin = fftwf_malloc(fftsize * sizeof(float) * pa_nchannels); + + fftwf_complex *fftout = fftwf_malloc(nbands * sizeof(fftwf_complex) * pa_nchannels); + + /* + * works only for 1 channel: + fftplan = fftwf_plan_dft_r2c_1d(fftsize, fftin, fftout, FFTW_ESTIMATE); + */ + /* + * works for N channels: + */ + fftplan = fftwf_plan_many_dft_r2c( + /*rank*/1, &fftsize, /*howmany*/pa_nchannels, + fftin, NULL, /*istride*/pa_nchannels, /*idist*/1, + fftout, NULL, /*ostride*/1, /*odist*/nbands, + FFTW_ESTIMATE | FFTW_PRESERVE_INPUT ); + /* Nb. FFTW_PRESERVE_INPUT is needed for the "shift the input window" trick */ + + if ( !fftplan ) { + fprintf(stderr, __FILE__": fftwf_plan_dft_r2c_1d() failed\n"); + return 1; + } + + + void *pa_samples_in = fftin; // read samples directly into fftin + assert( pa_samplesize == sizeof(float) ); + + + /* + * Prepare the input sample chunk rate + */ + int nsamples = sample_rate / decode_rate; + nsamples -= 1; // BLACK MAGIC! + + // float magscalar = 1.0 / (fftsize/2.0); /* normalize fftw output */ + float magscalar = 1.0 / (nsamples/2.0); /* normalize fftw output */ + + + float actual_decode_rate = (float)sample_rate / nsamples; + fprintf(stderr, "### baud=%.2f mark=%u space=%u ###\n", + actual_decode_rate, + bfsk_mark_band * band_width, + bfsk_space_band * band_width + ); + + + /* + * Run the main loop + */ + + unsigned int bfsk_bits = 0xFFFFFFFF; + unsigned char carrier_detected = 0; + + ret = 0; + + while ( 1 ) { + + size_t nframes = nsamples; + size_t nbytes = nframes * pa_framesize; + + bzero(fftin, (fftsize * sizeof(float) * pa_nchannels)); + if (pa_simple_read(s, pa_samples_in, nbytes, &error) < 0) { + fprintf(stderr, __FILE__": pa_simple_read() failed: %s\n", + pa_strerror(error)); + ret = 1; + break; + } + +#define TRICK + +#ifdef TRICK +reprocess_audio: + { + } +#endif +#ifdef USE_PA_FORMAT_S16LE + { // convert S16LE samples to float [-1.0:+1.0] + int j; + for ( j=0; j 1.0 || fftin[i] < -1.0 ) +// fprintf(stderr, __FILE__": WARNING input datum %.3f\n", fftin[i]); + if ( inmin > fftin[i] ) + inmin = fftin[i]; + if ( inmax < fftin[i] ) + inmax = fftin[i]; + } + + + fftwf_execute(fftplan); + + /* examine channel 0 only */ + float mag_mark = band_mag(fftout, bfsk_mark_band, magscalar); + float mag_space = band_mag(fftout, bfsk_space_band, magscalar); + + + static unsigned char lastbit; + +#if 0 // TEST -- doesn't help?, sometimes gets it wrong + // "clarify" mag_mark and mag_space according to whether lastbit + // was a mark or a space ... If lastbit was a mark, then enhance + // space and vice-versa + float clarify_factor = 2.0; + if ( lastbit ) { + mag_space *= clarify_factor; + } else { + mag_mark *= clarify_factor; + } +#endif + + float msdelta = mag_mark - mag_space; + + + // Detect carrier + float mag_detect = 0.01; + /* pulseaudio *adds* when downmixing 2 channels to 1; if we're using only + * one channel here, we blindly assume that pulseaudio downmixed from 2. */ + if ( pa_nchannels == 1 ) + mag_detect *= 2.0; + unsigned char carrier_detect = mag_mark + mag_space > mag_detect ? 1 : 0; + + +#ifdef TRICK + +// #define TRICK_DETECT 0.0 +// #define TRICK_DETECT 0.4 +// #define TRICK_DETECT 0.1 + +// #define TRICK_DETECT mag_detect +#define TRICK_DETECT ( 0.1 * (float)decode_rate/300 ) + + // EXCELLENT trick -- fixes 300 baud perfectly + // shift the input window if the msdelta is small + static unsigned int skipped_frames = 0; + if ( carrier_detected && fabs(msdelta) < TRICK_DETECT ) + { +# if 0 + skipped_frames++; + if ( skipped_frames >= nsamples ) // maybe nsamples/2 ?? + nframes = 0; + else + nframes = 1; + printf( "*" ); +# else + + if ( nframes == nsamples ) { + // nframes = nsamples / 2; + // nframes = nsamples / 16; // shift by 1/4 the bit width + nframes = 1; + nframes = nframes ? nframes : 1; + } + skipped_frames += nframes; + if ( skipped_frames >= nsamples ) // maybe nsamples/2 ?? + nframes = 0; +# endif + if ( nframes ) { + size_t nbytes = nframes * pa_framesize; + size_t reuse_bytes = nsamples*pa_framesize - nbytes; + memmove(pa_samples_in, pa_samples_in+nbytes, reuse_bytes); + void *in = pa_samples_in + reuse_bytes; + if (pa_simple_read(s, in, nbytes, &error) < 0) { + fprintf(stderr, __FILE__": pa_simple_read() failed: %s\n", + pa_strerror(error)); + ret = 1; + break; + } + goto reprocess_audio; + } + } + + if ( textscope ) { + if ( skipped_frames ) + printf( "\n", + skipped_frames, nsamples); + } + + skipped_frames = 0; +#endif + + unsigned char bit; + + if ( carrier_detect ) { + bit = signbit(msdelta) ? 0 : 1; + +#if 0 + static unsigned char lastbit_strong = 0; + if ( fabs(msdelta) < 0.5 ) { // TEST + if ( lastbit_strong ) + bit = !lastbit; + lastbit_strong = 0; + } else { + lastbit_strong = 1; + } +#endif + + } + else + bit = 1; + + lastbit = bit; + + // save 11 bits: + // stop--- v v--- start bit + // v v--- prev stop bit + // 1dddddddd01 + bfsk_bits = (bfsk_bits>>1) | (bit << 10); + + if ( ! carrier_detect ) { + if ( carrier_detected ) { + printf( "###NOCARRIER###\n"); + carrier_detected = 0; + } + continue; + } + + if ( ! carrier_detected ) + printf( "###CARRIER###\n"); + carrier_detected = carrier_detect; + + if ( textscope ) { + printf("%s %c ", + carrier_detected ? "CD" : " ", + carrier_detected ? ( bit ? '1' : '0' ) : ' '); + + float magmax = 0; + + for ( i=0; i magmax ) + magmax = mag; + char *magchars = " .-=^"; + if ( i%nbands == bfsk_mark_band ) + magchars = " mMM^"; + if ( i%nbands == bfsk_space_band ) + magchars = " sSS^"; + char c = magchars[0]; + if ( mag > 0.10 ) c = magchars[1]; + if ( mag > 0.25 ) c = magchars[2]; + if ( mag > 0.50 ) c = magchars[3]; + if ( mag > 1.00 ) c = magchars[4]; + printf("%c", c); + + if ( i > 30 ) + break; + } + printf("| in[%+4.2f %+4.2f] >mag %+.2f", inmin, inmax, magmax); + + printf(" "); + for ( i=15; i>=0; i-- ) + printf("%c", bfsk_bits & (1<> 2) & 0xFF; + if ( textscope ) + printf("+"); + printf("%c", isspace(byte)||isprint(byte) ? byte : '.'); + fflush(stdout); + bfsk_bits = 1 << 10; + } + if ( textscope ) + printf("\n"); + + + } + + pa_simple_free(s); + + fftwf_free(fftin); + fftwf_free(fftout); + fftwf_destroy_plan(fftplan); + + return ret; +} diff --git a/src/miniscope.c b/src/miniscope.c new file mode 100644 index 0000000..ca180f5 --- /dev/null +++ b/src/miniscope.c @@ -0,0 +1,206 @@ +/* + * miniscope.c + * + * Author: Kamal Mostafa + * + * Unpublished work, not licensed for any purpose. + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + + +static inline +float +band_mag( fftwf_complex * const cplx, unsigned int band, float scalar ) +{ + float re = cplx[band][0]; + float im = cplx[band][1]; + float mag = sqrtf(re*re + im*im) * scalar; + return mag; +} + +int main(int argc, char*argv[]) { + /* The sample type to use */ + static const pa_sample_spec ss = { + .format = PA_SAMPLE_FLOAT32, + .rate = 9600, // pulseaudio will resample its configured audio rate + + .channels = 2 // 2 channel stereo + // .channels = 1 // pulseaudio will downmix (additively) to 1 channel + // .channels = 3 // 2 channel stereo + 1 mixed channel + }; + int ret = 1; + int error; + + + if ( argc < 2 ) { + fprintf(stderr, "usage: miniscope baud_rate [ band_width ]\n"); + return 1; + } + + + + int argi = 1; + unsigned int decode_rate = atoi(argv[argi++]); + unsigned int band_width = decode_rate; + if ( argi < argc ) + band_width = atoi(argv[argi++]); + assert( band_width <= decode_rate ); + + unsigned int sample_rate = ss.rate; + + + + /* Create the recording stream */ + pa_simple *s; + s = pa_simple_new(NULL, argv[0], PA_STREAM_RECORD, NULL, "record", &ss, NULL, NULL, &error); + if ( !s ) { + fprintf(stderr, __FILE__": pa_simple_new() failed: %s\n", pa_strerror(error)); + return 1; + } + + int pa_samplesize = pa_sample_size(&ss); + int pa_framesize = pa_frame_size(&ss); + int pa_nchannels = ss.channels; + + assert( pa_framesize == pa_samplesize * pa_nchannels ); + + + /* Create the FFT plan */ + fftwf_plan fftplan; + + int fftsize = sample_rate / band_width; + + if ( fftsize & 1 ) + fprintf(stderr, __FILE__": WARNING: fftsize %u is not even\n", fftsize); + + unsigned int nbands = fftsize / 2 + 1; + + float *fftin = fftwf_malloc(fftsize * sizeof(float) * pa_nchannels); + + fftwf_complex *fftout = fftwf_malloc(nbands * sizeof(fftwf_complex) * pa_nchannels); + + /* + * works only for 1 channel: + fftplan = fftwf_plan_dft_r2c_1d(fftsize, fftin, fftout, FFTW_ESTIMATE); + */ + /* + * works for N channels: + */ + fftplan = fftwf_plan_many_dft_r2c( + /*rank*/1, &fftsize, /*howmany*/pa_nchannels, + fftin, NULL, /*istride*/pa_nchannels, /*idist*/1, + fftout, NULL, /*ostride*/1, /*odist*/nbands, + FFTW_ESTIMATE | FFTW_PRESERVE_INPUT ); + /* Nb. FFTW_PRESERVE_INPUT is needed for the "shift the input window" trick */ + + if ( !fftplan ) { + fprintf(stderr, __FILE__": fftwf_plan_dft_r2c_1d() failed\n"); + return 1; + } + + + void *pa_samples_in = fftin; // read samples directly into fftin + assert( pa_samplesize == sizeof(float) ); + + + /* + * Prepare the input sample chunk rate + */ + int nsamples = sample_rate / decode_rate; + + // float magscalar = 1.0 / (fftsize/2.0); /* normalize fftw output */ + float magscalar = 1.0 / (nsamples/2.0); /* normalize fftw output */ + + + float actual_decode_rate = (float)sample_rate / nsamples; + fprintf(stderr, "### baud=%.2f ###\n", actual_decode_rate); + + + /* + * Run the main loop + */ + + ret = 0; + + while ( 1 ) { + + size_t nframes = nsamples; + size_t nbytes = nframes * pa_framesize; + + bzero(fftin, (fftsize * sizeof(float) * pa_nchannels)); + if (pa_simple_read(s, pa_samples_in, nbytes, &error) < 0) { + fprintf(stderr, __FILE__": pa_simple_read() failed: %s\n", + pa_strerror(error)); + ret = 1; + break; + } + + float inmax=0, inmin=0; + int i; + for ( i=0; i 1.0 || fftin[i] < -1.0 ) +// fprintf(stderr, __FILE__": WARNING input datum %.3f\n", fftin[i]); + if ( inmin > fftin[i] ) + inmin = fftin[i]; + if ( inmax < fftin[i] ) + inmax = fftin[i]; + } + + + fftwf_execute(fftplan); + + { + float magmax = 0; + + for ( i=0; i magmax ) + magmax = mag; + char *magchars = " .-=^"; + char c = magchars[0]; + if ( mag > 0.10 ) c = magchars[1]; + if ( mag > 0.25 ) c = magchars[2]; + if ( mag > 0.50 ) c = magchars[3]; + if ( mag > 1.00 ) c = magchars[4]; + printf("%c", c); + + // if ( i > 70 ) + // break; + } + printf("|"); + // printf("in %+4.2f %+4.2f", inmin, inmax); + printf(">mag %.2f", magmax); + printf("\n"); + } + + } + + pa_simple_free(s); + + fftwf_free(fftin); + fftwf_free(fftout); + fftwf_destroy_plan(fftplan); + + return ret; +}