git stults online alsa-signals / master prototype.c
master

Tree @master (Download .tar.gz)

prototype.c @masterraw · history · blame

#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <pthread.h>
#include <math.h>
#include <poll.h>
#include "snd_dev.h"
#include "ring_buffer.h"
#include "sigproc.h"

#define TWOPI (6.283185307179586)
#define SQRTTWO (1.4142135623730951)

double BARKER_DOUBLE[] = {
  1.0, 1.0, 1.0, 1.0, 1.0, -1.0, -1.0, 1.0, 1.0, -1.0, 1.0, -1.0, 1.0
};

void main(int argc, char *argv[])
{
  FILE                *in, *out;
  struct stat          statbf;

  int                  i;
  int                  write;
  unsigned int         idx;
  unsigned int         rem;
  unsigned int         curr;
  unsigned char        mask;

  /* sound card options */
  const char          *device = "default";
  snd_dev_t           *handle;
  unsigned int         channels;
  unsigned int         rate;
  unsigned int         stream;
  unsigned int         access;
  unsigned int         format;
  unsigned int         frames;

  /* wave options */
  double               F;     /* frequency */
  double               P;     /* phase */
  unsigned int         Fs;    /* sample rate */
  double               T;     /* sample duration (1/fs) */
  double               W;     /* f * 2 * pi */
  double               C;     /* cycles per sample */
  double               S;     /* samples per cycle */
  double               Y;     /* samples per symbol */
  
  /* buffer stuff */
  void             *ptr;
  unsigned char    *data;
  unsigned int      datalen;
  int               frleft;
  int               frtran;
  int16_t          *conform;
  unsigned int      conform_len;

  int            rc;

  if (argc != 3)
    {
      fprintf (stdout, "usage: %s <0=listen,1=talk> <file in/out>\n", __func__);
      return;
    }
  
  write    = atoi (argv[1]) ? 1 : 0;

  /* sound buffer is made of periods                */
  /* | pd 0 | pd 1 | ...    ... | pd n-1 |          */
  /* period is made of frames                       */
  /* | fr 0 | fr 1 | ...    ... | fr m-1 |          */
  /* frame is made of samples, one for each channel */
  /* | sm 0 | sm 1 | ...    ... | sm k-1 |          */
  /* samples are made of bytes according to format  */
  /* | byte 0 | ... | byte b-1 |                    */

  /* signal information */
  Fs    = 8000;    /* samples/sec */
  F     = 1000.0;  /* cycles/sec */
  P     = 0.0;
  W     = TWOPI * F;
  C     = F / Fs; /* cycles per sample */
  S     = Fs / F; /* samples per cycle */
  Y     = 16;     /* samples / symbol (want to minimize this) */

  /* sound card information */
  channels = 1; /* number of channels: mono, stereo, etc (?) */
  stream   = write ? SND_DEV_WRITE : SND_DEV_READ;
  access   = SND_DEV_INTER; /* interleave channel samples within a
			       frame? */
  format   = SND_DEV_INT16; /* format of samples (int/uint, etc) */
  frames   = 1024; /* frames per card period (refresh rate on/off card)
		      -- this is the largest number of samples one should
		      realistically try to write to the card at one
		      time */

  /* create sound device handle */
  handle = snd_dev_create (device, stream, access, format, channels,
			   Fs, frames);
  
  /* shovel data */
  /* (samples/sec) * (bytes/samples) * (channels) = bytes/sec */
  /* to send wave at freq f at this data rate, want sin (2 * pi * f * i / rate) */
  /* then cast/scale values to bytes/sample range and plug */
  /* want a ring buffer that holds x seconds of data calculate as bytes via above */
  /* want another buffer to hold chunks of direct output until have
     enough data to push to buffer */

  /* (signal gen) --> (scale to format) --> <accumulator> --> <ring buffer> --> (sound out) */
  /* need an accumulator --> ring buffer write rate, an amount that
     could be dropped without causing too much damage if there is a
     holdup in the ring buffer; 0.5 sec; 1 sec? if there is a backup,
     then have to be able to fast forward signal gen as well */

  /* effective bit rate is symbols/sec * bits/symbol */
  /* sine oscillates (f times) / (sample rate samples = 1 sec) */
  /* 1 oscillation ~ 2 baud -> (2f baud) / (rate samples = 1 sec) -> 1 baud ~ (rate / 2f) samples  */
  /* 1: lower sample rate (maybe to 35k). 2: raise frequency (within human voice band (< 4000 Hz). 3: make sample size as small as possible */

  /* if we are in writing mode, make a signal; else try to catch one */
  if (write)
    {
      double      *tmp;
      double      *pulse_train;
      double      *barker;
      size_t      tmp_len;
      size_t      pulse_train_len;
      size_t      barker_len;
      
      /* read in file and raw create a pulse train bit->pulse */
      if ((in = fopen (argv[2], "rb")) == NULL)
	{
	  fprintf (stderr, "file %s does not exist\n", argv[2]);
	  exit (1);
	}
      fstat (fileno (in), &statbf);
      datalen = statbf.st_size;
      data = calloc (datalen, sizeof(unsigned char));
      fread (data, sizeof(unsigned char), datalen, in);
      fclose (in);

      /* generate barker sequence at samples / symbol */
      if ((barker = make_pulse (BARKER_DOUBLE, 13, Y, &barker_len)) == NULL)
	printf ("error in barker seq\n");

      /* a symbol for each bit at samples / symbol */
      tmp = encode_bpsk (data, datalen, &tmp_len);
      pulse_train = make_pulse (tmp, tmp_len, Y, &pulse_train_len);
      free (tmp);
    
      /* turn the barker-sequence + pulse train into phase offsets of
	 a sinewave */
      printf ("barker:\n");
      for (i = 0; i < barker_len; ++i)
	{
	  barker[i] = sinf ((TWOPI * C * i) + (M_PI * barker[i]));
	  //printf ("%d %1.3f\n", i, barker[i]); 
	}
      printf ("package:\n");
      for (; i < pulse_train_len + barker_len; ++i)
	{
	  pulse_train[i - barker_len] = sinf ((TWOPI * C * i) + (M_PI * pulse_train[i - barker_len]));
	  //	  printf ("%d %1.3f\n", i, pulse_train[i - barker_len]);
	}

      /* pack floating point samples into <16> bit samples (just multiply
	 and trunc), adding barker code at the beginning */
      conform_len = pulse_train_len + barker_len;
      conform     = calloc (pulse_train_len, sizeof(int16_t));
      for (i = 0; i < barker_len; ++i)
	conform[i] = (int16_t)((float)(1 << 14) * barker[i]);
      for (i = barker_len; i < conform_len; ++i)
	conform[i] = (int16_t)((float)(1 << 14) * pulse_train[i]);

      /* poll and transfer; each float sample in the pulse train has to be
	 packed into <sample size> bytes and those bytes assembled into
	 one period's worth of frames */
      /* TODO: have to handle case where data is < frames */
      ptr = (void *)conform;
      /* number of periods in the pulse train */
      frleft = (conform_len + frames - 1) / frames;
      frtran = frames;
      for (;;)
	{
	  rc = snd_dev_poll (handle);
	  /* timeout occurred -- TODO fast forward etc; need to pass our
	     own timeout to handle this (?) */
	  if (rc == 0)
	    {
	      fprintf (stderr, "transfer poll timed out, trying again\n");
	      continue;
	    }
	  /* error occurred */
	  else if (rc < 0)
	    {
	      fprintf (stderr, "polling error, trying again\n");
	      continue;
	    }
	  /* else we're good to write */      
	  snd_dev_transfer (handle, ptr, frtran);
	  ptr    += frames * sizeof(int16_t); /* because ptr is void */
	  if ((frleft -= (int)frames) < 0)
	    break;
	  else if (frleft < (int)frames)
	    frtran = frleft;      
	}

      free (data);
      free (barker);
      free (pulse_train);
      free (conform);
    }
  /* we are waiting for a signal at freq Hz */
  else
    {
      double        *slice;
      double        *corr;
      double        *barker;
      size_t         barker_len;
      ring_buffer_t *rb;
      unsigned int   rb_len;
      void          *tmp;
      unsigned char *dat;
      size_t         corrlen;
      
      /* generate barker sequence at data rate */
      barker = make_pulse (BARKER_DOUBLE, 13, Y, &barker_len);
      for (i = 0; i < barker_len; ++i)
	barker[i] = sinf ((TWOPI * C * i) + (M_PI * barker[i]));

      /* temp buffer between sound card and ring buffer -- holds one
	 period's worth of frames (2 bytes per frame) */
      tmp = calloc (frames * sizeof(int16_t), sizeof(unsigned char));

      /* open file for writing */
      if ((out = fopen (argv[2], "wb")) == NULL)
	{
	  fprintf (stderr, "file %s does not exist\n", argv[2]);
	  exit (1);
	}
      /* arbitrary ring buffer length (some number of frames) */
      rb_len = (1 << 7) * frames * sizeof(int16_t);
      rb = ring_buffer_create (rb_len);
      frleft = rb_len;
      frtran = barker_len * sizeof(int16_t);
      dat = calloc (frtran, sizeof(unsigned char));
      slice = calloc (barker_len, sizeof(double));
      for (;;)
	{
	  rc = snd_dev_poll (handle);
	  /* timeout occurred */
	  if (rc == 0)
	    {
	      fprintf (stderr, "transfer poll timed out, trying again\n");
	      continue;
	    }
	  /* error occurred */
	  else if (rc < 0)
	    {
	      fprintf (stderr, "polling error, trying again\n");
	      continue;
	    }
	  /* else we're good to read */      
	  snd_dev_transfer (handle, tmp, frames);
	  /* if ring buffer is full when we try to write, dump it --
	     more nuanced approach is required here. */
	  if (!ring_buffer_write_avail (rb))
	    {
	      fprintf (stdout, "ring buffer clogged -- dumping\n");
	      ring_buffer_clear (rb);
	    }
	  /* write to ring buffer -- this is a little stupid, I just
	     don't want to keep track of pointers at this very second
	     and the buffer will circle around. */
	  ring_buffer_write (rb, tmp, frames * sizeof(int16_t));       
	  /* when ring buffer has enough data in it, check for what
	     we're looking for */
	  if (ring_buffer_read_avail (rb) < frtran)
	    continue;
	  /* we have enough data, do smth */
	  /* read data into buffer -- correlate with training
	     sequence */
	  
	  /* search energy for a hit */

	  /* if we have a hit then start to demodulate samples from
	     HEAD */
	  
	  /* write them to file */
	  
	  /* read samples from ring buffer */
	  while (ring_buffer_read (rb, dat, frtran))
	    {
	      double curr = 0.0;
	      double val  = 0.0;
	      for (i = 0; i < barker_len; ++i)
		{
		  int16_t d = 0x0000;
		  d ^= dat[2 * i    ] << 8;
		  d ^= dat[2 * i + 1];
		  val = (double)d / (double)(1 << 14);
		  curr += (barker[i] * val) * (barker[i] * val);
		}
	      printf ("score %f\n", sqrt (curr));
	    }
	}
      fclose (out);
      ring_buffer_destroy (rb);
      free (tmp);
      free (dat);
      free (barker);
      free (slice);
    }

  snd_dev_destroy (handle);

  return;
}