Code Search for Developers
 
 
  

syncfiles.c from EmStar at Krugle


Show syncfiles.c syntax highlighted


#include <sndfile.h>
#include <string.h>
#include <math.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <ctype.h>
#include <libmisc/misc.h>

#define MAX_FILES 10

struct sync_point {
  int offset;
  int mark;
};

struct skipper {
  int skipfrom;
  int skipignore;
  int zerolen;
};

struct file_info {
  char *name;
  int channels;
  int samples;
  struct sync_point sps[1000];
  int sps_count;
  int offset;
  int offset_avg;
  int offset_count;

  struct skipper skippers[10];
  int next_skipper;
  int skipper_count;
  int zerocount;

  SNDFILE* f;
};

struct file_info files[MAX_FILES];
int file_count = 0;

struct mark_info {
  int offset;
  int present;
  int invalid;

  /* state for the correction code */
  int skipthis;
  int ignore;
  int fillin;
};

struct mark_info marks[256][MAX_FILES];

int mark_valid(int i, int j)
{
  return marks[i][j].present && !marks[i][j].invalid;
}

int main(int argc, char **argv) 
{

#define SYNC_CHANNEL 0
#define THRESH  9000
#define BAUD  19200


  FILE *sync_out = stdout;

  memset(files, 0, sizeof(files));
  memset(marks, 0, sizeof(marks));

  int samplerate = 0;
  int format = 0;
  
  char buf1[128];
  char buf2[128];
  char *outfile_name = "/tmp/output.wav";
  char *sync_filename = "/tmp/output.sync";

  char *outfile_root = misc_parse_out_option(&argc, argv, "outfile", 0);
  if (outfile_root) {
    strcpy(buf1, outfile_root);
    strcat(buf1, ".wav");
    strcpy(buf2, outfile_root);
    strcat(buf2, ".sync");
    outfile_name = buf1;
    sync_filename = buf2;
  }

  int skip=0;
  while (misc_parse_option_as_int(&argc, argv, "skip", 0, &skip) >= 0) {
    int i;
    for (i=0; i<MAX_FILES; i++) 
      marks[skip][i].invalid = 1;
  }

  char *str;
  while ((str = misc_parse_out_option(&argc, argv, "fixchannel", 0))) {
    int channel, mark;
    sscanf(str, "%d,%d", &channel, &mark);
    marks[mark][channel].skipthis = 1;
    printf("# Setting skipthis for channel %d, mark %d\n", channel, mark);
  }

  while ((str = misc_parse_out_option(&argc, argv, "fixup", 0))) {
    int channel, mark, ign, fill;
    sscanf(str, "%d,%d,%d,%d", &channel, &mark, &ign, &fill);
    marks[mark][channel].skipthis = 2;
    marks[mark][channel].ignore = ign;
    marks[mark][channel].fillin = fill;
    printf("# Setting custom skip for channel %d, mark %d, fill %d\n", channel, mark, fill);
  }

 if (misc_parse_out_switch(&argc, argv, "help", 'h')) {
    printf("usage: --outfile <root> [--force] [--skip num] ..\n"
	   "   rcvfile rcvfile feedfile\n"
	   "  --force will force completion when errors flagged\n"
	   "  --skip will invalidate specific marks\n"
	   "  --fixchannel chan,mark will replace the following segment of specified channel with blanks.\n");
    exit(1);
  }

  int force = misc_parse_out_switch(&argc, argv, "force", 0);
  
  /* load each file */
  char **iter = argv + 1;
  while (*iter) {

    files[file_count].name = *iter;
    
    /* load the file */
    SF_INFO info = {};
    files[file_count].f = sf_open(*iter, SFM_READ, &info);
    
    if (files[file_count].f == NULL) {
      fprintf(stderr, "Can't open sound file %s: %m\n", *iter);
      exit(1);
    }

    /* ok, it's open.  now, read in the sync channel */
    fprintf(sync_out, "# File %s, %lld samples, %d data channels\n", 
	    *iter, info.frames, info.channels);
    files[file_count].channels = info.channels;
    files[file_count].samples = info.frames;
    samplerate = info.samplerate;
    format = info.format;

    /* determine the bit rate */
    float samp_per_bit = info.samplerate / (float)BAUD;
    //float samp_per_bit = 44100 / (float)BAUD;

    int16_t *sync_data = malloc(info.frames * sizeof(int16_t));
 
    int i=0;
    while (i<info.frames) {
      int16_t temp[1024][info.channels];
      sf_count_t count = sf_readf_short
	(files[file_count].f, (int16_t *)temp, 1024);
      int j;
      for (j=0; j<count; j++,i++) {
	sync_data[i] = temp[j][SYNC_CHANNEL];
	//printf("%d %d\n", temp[j][0], temp[j][1]);  
      }
    }

    /* ok, we have the sync channel.  now we parse it */
    int k;

    /* first find the threshold */
    int max = 0;
    for (k=0; k<info.frames; k++) {
      if (abs(sync_data[k]) > max)
	max = abs(sync_data[k]);
    }

    int thresh = max/2;

    printf("##thresh = %d\n", thresh);

    for (k=0; k<info.frames; k++) {
      if (abs(sync_data[k]) > (3*max/4)) {
	
	/* ok, maybe got a byte */

	float bits[9];
	int kk;

#ifdef DEBUG
	printf("##sync data: ");
	for (kk=0; kk<22; kk++) 
	  printf("%d ", sync_data[k+kk]);
	printf("\n");
#endif

	/* average the data into bits */
	float fk;
	for (kk=0, fk=0; kk<9; kk++, fk+=samp_per_bit) {
	  bits[kk] = sync_data[k+(int)(fk+0.5)];
	}
	
#ifdef DEBUG  
	printf("##bits: ");
	for (kk=0; kk<9; kk++) 
	  printf("%f ", bits[kk]/thresh);
	printf("\n");
#endif

	int bits2[9];
	for (kk=0; kk<9; kk++) 
	  bits2[kk] = (fabsf(bits[kk]) > thresh) ? 0 : 1;
	
#ifdef DEBUG
	printf("##bits2: ");
	for (kk=0; kk<9; kk++) 
	  printf("%d ", bits2[kk]);
	printf("\n");
#endif

	int value = 0;
	for (kk=8; kk>0; kk--) {
	  value = value << 1;
	  value |= bits2[kk];
	}

	printf("##value: %d (%c) at %d\n", value,
	       isprint(value) ? value : '.', k);
	
	files[file_count].sps[files[file_count].sps_count].mark = value;
	files[file_count].sps[files[file_count].sps_count].offset = k;
	files[file_count].sps_count++;
	
	k += 50;
      }
    }

    file_count++;    
    iter++;
  }

  /* ok, now figure out how to sync up the files */
  
  /* for each mark, find other marks */
  
  int i,j,kk;
  int maybe_stop=0;
  
  for (i=0; i<file_count; i++) {
    for (j=0; j<files[i].sps_count; j++) {
      int mark = files[i].sps[j].mark;

      if (marks[mark][i].present) {
	printf("** warning, duplicate mark %d, file %s\n",
	       mark, files[i].name);
	marks[mark][i].invalid = 1;
      }
      else {
	marks[mark][i].offset = files[i].sps[j].offset;
	marks[mark][i].present = 1;
      }

    }
  }

  /* drop out of order entries */
  for (j=0; j<file_count; j++) {
    for (i=1; i<255; i++) {
      if (mark_valid(i,j) &&
	  mark_valid(i-1,j) &&
	  mark_valid(i+1,j) &&
	  (marks[i][j].offset < marks[i-1][j].offset ||
	   marks[i][j].offset > marks[i+1][j].offset) &&
	  marks[i-1][j].offset < marks[i+1][j].offset) {
	printf("mark '%c' out of order.. marking invalid\n", i);
	marks[i][j].invalid = 1;
      }
    }
  }
 
  /* locate earliest mark. */
  int earliest = -1;
  for (j=0; j<file_count; j++) {
    int earliest_offset = -1;
    int earliest_file = -1;
    for (i=0; i<255; i++) {
      if (mark_valid(i,j)) {
	if (earliest_offset < 0 || 
	    marks[i][j].offset < earliest_offset) {
	  earliest_offset = marks[i][j].offset;
	  earliest_file = i;
	}
      }
    }
    
    if (earliest < 0 || earliest_file < earliest)
      earliest = earliest_file;
  }

  printf("## earliest mark is %d\n", earliest);
 
  /* locate skip requests and patch up offsets after skip */
  for (j=0; j<file_count; j++) {
    for (i=0; i<255; i++) {
      if (marks[i][j].skipthis) {
	printf("## processing skipthis for ch %d, mark %d\n", j,i);
	int k,k2;
	int region;

	if (marks[i][j].skipthis == 2) {
	  k = i+1;

	  files[j].skippers[files[j].skipper_count].skipfrom = marks[i][j].offset;
	  files[j].skippers[files[j].skipper_count].zerolen = marks[i][j].fillin;
	  files[j].skippers[files[j].skipper_count].skipignore = marks[i][j].ignore;	
	  files[j].skipper_count++;
	  
	  region = marks[i][j].fillin - marks[i][j].ignore;

	  goto adjust;
	}

	/* got one.  first, figure out the size of this region. */
	for (k=i+1; k!=i; k = (k+1)%256) {
	  if (mark_valid(k,j)) {
	    for (k2=0; k2<file_count; k2++) {
	      if (k2 != j && mark_valid(k,k2))
		goto found_range;
	    }
	    printf("## no range found, yet, skipping past mark %d\n", k);
	  }
	}
	printf("## ACK .. no range found\n");
	exit(1);

      found_range:
	/* ok, use k2 */
	printf("## using file %d mark %d, offset is %d, %d\n",
	       k2, k, marks[k][k2].offset, marks[i][k2].offset);
	region = marks[k][k2].offset - marks[i][k2].offset;
	int oldregion = marks[k][j].offset - marks[i][j].offset;
	printf("## region change from %d to %d.  %d samples lost, will drop %d unsyncable samples\n", 
	       oldregion, region, region - oldregion, region);
	if (region < oldregion) {
	  printf("## ACK!  old region is bigger???\n");
	  exit(1);
	}

	/* save the amounts to kill and replace */
	marks[i][j].ignore = oldregion;
	marks[i][j].fillin = region;
	
	files[j].skippers[files[j].skipper_count].skipfrom = marks[i][j].offset;
	files[j].skippers[files[j].skipper_count].zerolen = region;
	files[j].skippers[files[j].skipper_count].skipignore = oldregion;	
	files[j].skipper_count++;

	region -= oldregion;
	files[j].samples += region;
	
	/* ok, clear any marks between those two */
	int k3;
	for (k3=i+1; k3 != k; k3 = (k3+1)%256) {
	  if (mark_valid(k3,j)) {
	    printf("## killing mark %d\n", k3);
	    marks[k3][j].invalid = 1;
	  }
	}

      adjust:
	;
	/* ok, we now add the difference to all the rest of the offsets */
	int lastoff = marks[i][j].offset;
	for (k3=k; k3 != i; k3 = (k3+1)%256) {
	  if (mark_valid(k3,j)) {
	    if (marks[k3][j].offset < lastoff) {
	      printf("## done shifting..\n");
	      goto done_shifting;
	    }
	    printf("## shifting mark at %d\n", k3);
	    lastoff = marks[k3][j].offset;
	    marks[k3][j].offset += region;
	  }
	}

      done_shifting:
	;

      }
    }
  }


  /* we assume that there are no errors now.
   * we will check this assumption and fail if it's incorrect */
  
  /* find first full entry */
  int first_full;
  for (i=earliest; i!=earliest-1; i = (i+1)%256) {
    for (j=0; j<file_count; j++) {
      if (!mark_valid(i,j))
	goto next;
    }
    
    first_full=i;
    goto found_full;
    
  next:
    ;
  }
  printf("*** no full sync entry found, aborting.\n");
  exit(1);

 found_full:

  printf("## Using %d as the first full mark.\n", i);
  
  /* dump the errors */
  printf("## dumping errors..\n");
  int last_errors[MAX_FILES] = {};  
  int last_diff[MAX_FILES] = {};
  int last_offset[MAX_FILES] = {};
  int last_mark[MAX_FILES] = {};
  float skew_count[MAX_FILES] = {};
  float sum_skew[MAX_FILES] = {};
  float skew_avg[MAX_FILES] = {};
  float delta = 0;
  for (kk=earliest; kk!=earliest-1; kk = (kk+1)%256) {
    if (mark_valid(kk,0)) {
      int j;
      int the_offset = marks[kk][0].offset;
      printf("## %d  ", kk);
      for (j=0; j<file_count; j++) {
	if (mark_valid(kk,j)) {
	  int new_error = marks[kk][j].offset - the_offset;
	  int new_diff = 0;
	  float skew = 0;
	  if (last_offset[j] != 0) {
	    new_diff = new_error-last_errors[j];
	    skew = new_diff / 
	      (float)(marks[kk][j].offset - last_offset[j]);
	  }
	  last_errors[j] = new_error;
	  if (last_offset[j] != 0) {
	    last_diff[j] = new_diff;
	    sum_skew[j] += skew;
	    skew_count[j]++;
	    if (skew_avg[j] != 0) {
	      float new_delta = fabs(skew - skew_avg[j])*48000.0;
	      if (new_delta > delta)
		delta = new_delta;
	    }
	  }
	  float permark = 0;
	  if (last_offset[j] > 0) {
	    int pastmarks = (kk-last_mark[j]);
	    if (pastmarks < 0) pastmarks += 256;
	    permark = (marks[kk][j].offset - last_offset[j]) / (float)(pastmarks);
	    if ((permark / 468832.0) < 0.99) {
	      printf("\n*** suggest --fixup %d,%d,%d,%d\n",
		     j,last_mark[j],marks[kk][j].offset - last_offset[j], (pastmarks)*468832);
	    }
	  }
	  last_offset[j] = marks[kk][j].offset;
	  skew_avg[j] = skew_count[j] ? sum_skew[j] / skew_count[j] : 0;
	  printf("[%d %d %.2f %.2f %.2f] ", new_error, new_diff, 
		 skew*48000.0, skew_avg[j]*48000.0, permark);
	  last_mark[j] = kk;
	}
	else
	  printf("[] ");
      }
      printf("\n");
    }
  }
  
  if (delta > 1) {
    printf("***WARNING: Deviation of %f from average skew... check for problems!\n",
	   delta);
    maybe_stop = 1;
  }
  
  if (skew_count[0] < 2 || skew_count[1] < 2) {
    printf("***WARNING: Fewer than 2 skew entries\n");
    maybe_stop = 1;
  }

  if (skew_count[2] < 2) {
    printf("***WARNING: Fewer than 2 skew entries on feedback channel\n");
    maybe_stop = 1;
  }
  
  /* $$$ HACKS .. depends on receivers being skew free 
   * and on not needing high quality sync on feedback channel! */ 

  /* check that receiver files are OK */
  if (fabs(skew_avg[1])*48000.0 > 1) {
    printf("***WARNING: Average skew on channel 1 is %f.  This usually indicates a glitch or overrun\n",
	   skew_avg[1]*48000.0);
    maybe_stop = 1;
  }

  /* adjust sender file */
  for (i=0;i<256;i++) {
    if (mark_valid(i,2)) {
      marks[i][2].offset =
	marks[i][2].offset / (1.0+skew_avg[2]);
    }
  }
  int feedback_samples = files[2].samples;
  files[2].samples = files[2].samples / (1.0+skew_avg[2]);
  printf("adjusted feedback sample count from %d to %d\n",
	 feedback_samples, files[2].samples);

  /* dump errors */
  for (kk=earliest; kk!=earliest-1; kk = (kk+1)%256) {
    if (mark_valid(kk,0)) {
      int j;
      int the_offset = marks[kk][0].offset;
      printf("## %d  ", kk);
      for (j=0; j<file_count; j++) {
	if (mark_valid(kk,j)) {
	  int new_error = marks[kk][j].offset - the_offset;
	  printf("%d ", new_error);
	}
      }
      printf("\n");
    }
  }

  if (maybe_stop) {
    if (!force) {
      printf("ABORTING.  use --force to complete\n");
      exit(1);
    }
    printf("WOULD ABORT, --force set\n");
  }

  /* which file has lowest offset */
  int min_off = -1;
  int min_off_file = -1;
  for (j=0; j<file_count; j++) {
    if (min_off_file < 0 ||
	marks[first_full][j].offset < min_off) {
      min_off = marks[first_full][j].offset;
      min_off_file = j;
    }
  }

  printf("## Minimum offset file is %d\n", min_off_file);

  for (j=0; j<file_count; j++) {
    files[j].offset = marks[first_full][j].offset - min_off;
  }

  /* verify the others */
  int max_err = 0;
  int max_mark = -1;
  int max_file = -1;
  int killed_count = 0;
  int OK_count = 0;
  for (kk=earliest; kk!=earliest-1; kk = (kk+1)%256) {
    if (mark_valid(kk,min_off_file)) {
      int local_offset = marks[kk][min_off_file].offset;
      for (j=0; j<file_count; j++) {
	if (mark_valid(kk,j)) {
	  int err = marks[kk][j].offset - local_offset - files[j].offset;
	  if (abs(err) > max_err) {

	    /* if the error is a lot, just mark invalid and keep going */
	    if (abs(err) > 20) {
	      printf("##found large err %d at %d,%d\n", err, kk, j);
	      printf("##killing this mark..\n");
	      killed_count++;
	      goto killit;
	    }

	    max_err = abs(err);
	    max_mark = kk;
	    max_file = j;
	    printf("##found max err %d at %d,%d\n", err, kk, j);
	  }
	}
      }
      OK_count++;
    }
    else {
    killit:
      /* kill this if the low offset file is invalid */
      for (j=0; j<file_count; j++) {
	marks[kk][j].invalid = 1;
      }
    }
  }

  if (killed_count > OK_count*2) {
    printf("***too many bad entries, aborting!\n");
    exit(1);
  }

  if (max_err > 20) {
    printf("***too much error, aborting!\n");
    exit(1);
  }

  /* average the error */
  for (j=0; j<file_count; j++) {
    for (kk=0; kk<256; kk++) {
      if (mark_valid(kk,j)) {
	int local_offset = marks[kk][min_off_file].offset;
	int err = marks[kk][j].offset - local_offset - files[j].offset;
	files[j].offset_avg += err;
	files[j].offset_count++;
      }
    }
    files[j].offset_avg /= files[j].offset_count;
    /* $$ but we arent currently using this info */  
  }

  /* ok, now rewind the input files and offset them */
  int total_samples = -1;
  int total_channels = 0;
  for (j=0; j<file_count; j++) {

    /* rewind and seek the file */
    sf_seek(files[j].f, files[j].offset, SEEK_SET);
    
    printf("## Offset for file %d is %d\n", 
	   j, files[j].offset);

    /* adjust skip location if needed */
    for (i=0; i<files[j].skipper_count; i++) {
      printf("# correcting skipfrom at %d\n", files[j].skippers[i].skipfrom);
      files[j].skippers[i].skipfrom -= files[j].offset;
      printf("# skipfrom is now at %d\n", files[j].skippers[i].skipfrom);
    }
    
    /* compute the length we are copying */
    if (total_samples < 0 ||
	total_samples > (files[j].samples - files[j].offset)) {
      total_samples = (files[j].samples - files[j].offset);
    }

    total_channels += (files[j].channels - 1);
  }

  printf("## Totals: samples %d channels %d\n",
	 total_samples, total_channels);

  /* open output file */
  SF_INFO info2 = {
    samplerate: samplerate,
    format: format,
    frames: total_samples,
    channels: total_channels
  };

  SNDFILE *output_file = sf_open(outfile_name, SFM_WRITE, &info2);
  
  if (output_file == NULL) {
    fprintf(stderr, "Can't open output sound file %s: %m\n", outfile_name);
    exit(1);
  }

  /* $$$ HACK!! */
  float feedback_increment = 1.0 / (1.0 + skew_avg[2]);
  float feedback_counter = 0;

  for (kk=0; kk<total_samples; 
       kk++, feedback_counter += feedback_increment) {

    int16_t frame[total_channels];
    memset(frame, 0, sizeof(frame));
    int index = 0;
    
    for (j=0; j<file_count; j++) {
      int16_t temp[files[j].channels];
      memset(temp, 0, sizeof(temp));
      int drop = 0;
      int insert = 0;

      /* check for major glitch start */
      if (files[j].skippers[files[j].next_skipper].skipfrom && kk == files[j].skippers[files[j].next_skipper].skipfrom) {
	/* seek fwd */
	printf("# seeking past %d samples\n", files[j].skippers[files[j].next_skipper].skipignore);
	sf_seek(files[j].f, files[j].skippers[files[j].next_skipper].skipignore, SEEK_CUR);
	
	/* set zero countdown */
	files[j].zerocount = files[j].skippers[files[j].next_skipper].zerolen;	
	printf("# ignoring the next %d samples on this channel\n", files[j].zerocount);
	files[j].next_skipper++;
      }

      /* skip glitches */
      if (files[j].zerocount > 0) {
	files[j].zerocount--;
	index += (files[j].channels-1);
	continue;
      }

      /* $$$ HACK!!! */
      if (j == 2) {
	/* feedback channel ahead? */
	if (feedback_counter > (kk + 0.5)) {
	  feedback_counter -= 1.0;
	  drop = 1;
	}
	/* feedback channel behind? */
	else if (feedback_counter < kk - 0.5) {
	  feedback_counter += 1.0;
	  insert = 1;
	}
      }
      
      if (!insert) {
	if (sf_readf_short(files[j].f, temp, 1) != 1) {
	  if (j == 2 && kk >= feedback_samples) {
	    printf("**** tried to read off end for feedback.. \n");
	  }
	  else {
	    printf("read failed.. %d %d %d ? %m\n", kk, j, index);
	    exit(1);
	  }
	}
      }
      if (drop) {
	index += (files[j].channels-1);
      }
      else {
	for (i=0; i<files[j].channels; i++) {
	  if (i != SYNC_CHANNEL) {
	    frame[index++] = temp[i];
	  }
	}
      }
    }

    /* write a frame */
    if (sf_writef_short(output_file, frame, 1) != 1) {
      printf("write failed.. %d ? %m\n", kk);
      exit(1);
    }
  }

  /* close */
  for (j=0; j<file_count; j++) {
    printf("closing file %s, offset %d, dropped %d samples\n",
	   files[j].name, files[j].offset, 
	   files[j].samples - total_samples);
    sf_close(files[j].f);
  }

  sf_close(output_file);

  /* dump sync file */
  FILE *sync = fopen(sync_filename, "w");

  /* find earliest entry */
  int min_mark_entry = -1;
  int min_mark_offset = 0;
  for (i=0; i<256; i++) {
    for (j=0; j<file_count; j++) {
      if (mark_valid(i,j) && 
	  (min_mark_entry < 0 || 
	   min_mark_offset > marks[i][0].offset)) {
	min_mark_entry = i;
	min_mark_offset = marks[i][0].offset;
      }
    }
  }

  fprintf(sync, "#h mark offset\n");
  fprintf(sync, "## sync file, channel map:\n");
  int index = 0;
  for (j=0; j<file_count; j++) {
    fprintf(sync, "## %d .. %d from %s\n",
	    index, index + files[j].channels - 2, 
	    files[j].name);
    index += files[j].channels-1;
  }
  
  /* dump the errors */
  fprintf(sync, "## errors..\n");
  for (i=0; i<256; i++) {
    int index = (i+min_mark_entry)%256;
    int j;
    if (mark_valid(index,min_off_file)) {
      fprintf(sync, "## %d  ", index);
      for (j=0; j<file_count; j++) {
	if (mark_valid(index,j)) {
	  fprintf(sync, "%d ", 
		  marks[index][min_off_file].offset -
		  (marks[index][j].offset -
		   files[j].offset));
	}
      }
      fprintf(sync, "\n");
    }
  }

  /* dump entries in order */
  for (i=0; i<256; i++) {
    int index = (i+min_mark_entry)%256;
    if (mark_valid(index,min_off_file)) {
      fprintf(sync, "%d  %d\n", index, marks[index][min_off_file].offset);
    }
  }

  fclose(sync);

  return 0;
}




See more files for this project here

EmStar

EmStar is a software system for developing and deploying wireless sensor networks involving Linux-based platforms. As the wireless sensor network community has attempted to deploy more complex designs---large-scale, long-lived systems that need self-organization and adaptivity---a number of difficult software design issues have arisen. Advances in software design have not kept pace with the capabilities of hardware. This is because designing for an adaptive, efficient, and useful sensor network has turned out to be surprisingly complex and difficult. EmStar is a Linux-based software framework, whose goal is to dramatically reduce this complexity, enabling work to be shared and reused, and simplifying and speeding the design of new sensor network applications.

Project homepage: http://cvs.cens.ucla.edu/emstar/
Programming language(s): C,Shell Script
License: other

  BUILD
  logvehspd.c
  map
  motelog.c
  readfreebob.c
  snippets
  sparkfun_accel.c
  start.run
  syncfiles.c