//
// This file is part of Dire Wolf, an amateur radio packet TNC.
//
// Copyright (C) 2011, 2012, 2013, 2014, 2015 John Langner, WB2OSZ
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see .
//
/*-------------------------------------------------------------------
*
* Name: atest.c
*
* Purpose: Test fixture for the AFSK demodulator.
*
* Inputs: Takes audio from a .WAV file insted of the audio device.
*
* Description: This can be used to test the AFSK demodulator under
* controlled and reproducable conditions for tweaking.
*
* For example
*
* (1) Download WA8LMF's TNC Test CD image file from
* http://wa8lmf.net/TNCtest/index.htm
*
* (2) Burn a physical CD.
*
* (3) "Rip" the desired tracks with Windows Media Player.
* Select .WAV file format.
*
* "Track 2" is used for most tests because that is more
* realistic for most people using the speaker output.
*
*
* Without ONE_CHAN defined:
*
* Notice that the number of packets decoded, as reported by
* this test program, will be twice the number expected because
* we are decoding the left and right audio channels separately.
*
*
* With ONE_CHAN defined:
*
* Only process one channel.
*
*--------------------------------------------------------------------*/
// #define X 1
#include
#include
#include
#include
#include
#include
#include
#define ATEST_C 1
#include "audio.h"
#include "demod.h"
#include "multi_modem.h"
#include "textcolor.h"
#include "ax25_pad.h"
#include "hdlc_rec2.h"
#include "dlq.h"
#include "ptt.h"
#if 0 /* Typical but not flexible enough. */
struct wav_header { /* .WAV file header. */
char riff[4]; /* "RIFF" */
int filesize; /* file length - 8 */
char wave[4]; /* "WAVE" */
char fmt[4]; /* "fmt " */
int fmtsize; /* 16. */
short wformattag; /* 1 for PCM. */
short nchannels; /* 1 for mono, 2 for stereo. */
int nsamplespersec; /* sampling freq, Hz. */
int navgbytespersec; /* = nblockalign*nsamplespersec. */
short nblockalign; /* = wbitspersample/8 * nchannels. */
short wbitspersample; /* 16 or 8. */
char data[4]; /* "data" */
int datasize; /* number of bytes following. */
} ;
#endif
/* 8 bit samples are unsigned bytes */
/* in range of 0 .. 255. */
/* 16 bit samples are signed short */
/* in range of -32768 .. +32767. */
static struct {
char riff[4]; /* "RIFF" */
int filesize; /* file length - 8 */
char wave[4]; /* "WAVE" */
} header;
static struct {
char id[4]; /* "LIST" or "fmt " */
int datasize;
} chunk;
static struct {
short wformattag; /* 1 for PCM. */
short nchannels; /* 1 for mono, 2 for stereo. */
int nsamplespersec; /* sampling freq, Hz. */
int navgbytespersec; /* = nblockalign*nsamplespersec. */
short nblockalign; /* = wbitspersample/8 * nchannels. */
short wbitspersample; /* 16 or 8. */
char extras[4];
} format;
static struct {
char data[4]; /* "data" */
int datasize;
} wav_data;
static FILE *fp;
static int e_o_f;
static int packets_decoded = 0;
static int decimate = 0; /* Reduce that sampling rate if set. */
/* 1 = normal, 2 = half, etc. */
static struct audio_s my_audio_config;
static int error_if_less_than = 0; /* Exit with error status if this minimum not reached. */
//#define EXPERIMENT_G 1
//#define EXPERIMENT_H 1
#if defined(EXPERIMENT_G) || defined(EXPERIMENT_H)
static int count[MAX_SUBCHANS];
#if EXPERIMENT_H
extern float space_gain[MAX_SUBCHANS];
#endif
#endif
static void usage (void);
int main (int argc, char *argv[])
{
int err;
int c;
int channel;
time_t start_time;
#if defined(EXPERIMENT_G) || defined(EXPERIMENT_H)
int j;
for (j=0; j 10000) {
text_color_set(DW_COLOR_ERROR);
dw_printf ("Use a more reasonable bit rate in range of 100 - 10000.\n");
exit (EXIT_FAILURE);
}
if (my_audio_config.achan[0].baud < 600) {
my_audio_config.achan[0].modem_type = MODEM_AFSK;
my_audio_config.achan[0].mark_freq = 1600;
my_audio_config.achan[0].space_freq = 1800;
strcpy (my_audio_config.achan[0].profiles, "D"); }
else if (my_audio_config.achan[0].baud > 2400) {
my_audio_config.achan[0].modem_type = MODEM_SCRAMBLE;
my_audio_config.achan[0].mark_freq = 0;
my_audio_config.achan[0].space_freq = 0;
strcpy (my_audio_config.achan[0].profiles, " "); // avoid getting default later.
dw_printf ("Using scrambled baseband signal rather than AFSK.\n");
}
else {
my_audio_config.achan[0].modem_type = MODEM_AFSK;
my_audio_config.achan[0].mark_freq = 1200;
my_audio_config.achan[0].space_freq = 2200;
}
break;
case 'P': /* -P for modem profile. */
dw_printf ("Demodulator profile set to \"%s\"\n", optarg);
strcpy (my_audio_config.achan[0].profiles, optarg);
break;
case 'D': /* -D reduce sampling rate for lower CPU usage. */
decimate = atoi(optarg);
dw_printf ("Divide audio sample rate by %d\n", decimate);
if (decimate < 1 || decimate > 8) {
text_color_set(DW_COLOR_ERROR);
dw_printf ("Unreasonable value for -D.\n");
exit (1);
}
dw_printf ("Divide audio sample rate by %d\n", decimate);
my_audio_config.achan[0].decimate = decimate;
break;
case 'F': /* -D set "fix bits" level. */
my_audio_config.achan[0].fix_bits = atoi(optarg);
if (my_audio_config.achan[0].fix_bits < RETRY_NONE || my_audio_config.achan[0].fix_bits >= RETRY_MAX) {
text_color_set(DW_COLOR_ERROR);
dw_printf ("Invalid Fix Bits level.\n");
exit (1);
}
break;
case 'e': /* -e error if less than this number decoded. */
error_if_less_than = atoi(optarg);
break;
case '?':
/* Unknown option message was already printed. */
usage ();
break;
default:
/* Should not be here. */
text_color_set(DW_COLOR_ERROR);
dw_printf("?? getopt returned character code 0%o ??\n", c);
usage ();
}
}
if (optind >= argc) {
text_color_set(DW_COLOR_ERROR);
dw_printf ("Specify .WAV file name on command line.\n");
usage ();
}
fp = fopen(argv[optind], "rb");
if (fp == NULL) {
text_color_set(DW_COLOR_ERROR);
dw_printf ("Couldn't open file for read: %s\n", argv[optind]);
//perror ("more info?");
exit (1);
}
start_time = time(NULL);
/*
* Read the file header.
* Doesn't handle all possible cases but good enough for our purposes.
*/
err= fread (&header, (size_t)12, (size_t)1, fp);
if (strncmp(header.riff, "RIFF", 4) != 0 || strncmp(header.wave, "WAVE", 4) != 0) {
text_color_set(DW_COLOR_ERROR);
dw_printf ("This is not a .WAV format file.\n");
exit (EXIT_FAILURE);
}
err = fread (&chunk, (size_t)8, (size_t)1, fp);
if (strncmp(chunk.id, "LIST", 4) == 0) {
err = fseek (fp, (long)chunk.datasize, SEEK_CUR);
err = fread (&chunk, (size_t)8, (size_t)1, fp);
}
if (strncmp(chunk.id, "fmt ", 4) != 0) {
text_color_set(DW_COLOR_ERROR);
dw_printf ("WAV file error: Found \"%4.4s\" where \"fmt \" was expected.\n", chunk.id);
exit(1);
}
if (chunk.datasize != 16 && chunk.datasize != 18) {
text_color_set(DW_COLOR_ERROR);
dw_printf ("WAV file error: Need fmt chunk datasize of 16 or 18. Found %d.\n", chunk.datasize);
exit(1);
}
err = fread (&format, (size_t)chunk.datasize, (size_t)1, fp);
err = fread (&wav_data, (size_t)8, (size_t)1, fp);
if (strncmp(wav_data.data, "data", 4) != 0) {
text_color_set(DW_COLOR_ERROR);
dw_printf ("WAV file error: Found \"%4.4s\" where \"data\" was expected.\n", wav_data.data);
exit(1);
}
assert (format.nchannels == 1 || format.nchannels == 2);
assert (format.wbitspersample == 8 || format.wbitspersample == 16);
my_audio_config.adev[0].samples_per_sec = format.nsamplespersec;
my_audio_config.adev[0].bits_per_sample = format.wbitspersample;
my_audio_config.adev[0].num_channels = format.nchannels;
my_audio_config.achan[0].valid = 1;
if (format.nchannels == 2) my_audio_config.achan[1].valid = 1;
text_color_set(DW_COLOR_INFO);
dw_printf ("%d samples per second\n", my_audio_config.adev[0].samples_per_sec);
dw_printf ("%d bits per sample\n", my_audio_config.adev[0].bits_per_sample);
dw_printf ("%d audio channels\n", my_audio_config.adev[0].num_channels);
dw_printf ("%d audio bytes in file\n", (int)(wav_data.datasize));
dw_printf ("Fix Bits level = %d\n", my_audio_config.achan[0].fix_bits);
/*
* Initialize the AFSK demodulator and HDLC decoder.
*/
multi_modem_init (&my_audio_config);
e_o_f = 0;
while ( ! e_o_f)
{
int audio_sample;
int c;
for (c=0; c= 256 * 256)
e_o_f = 1;
#define ONE_CHAN 1 /* only use one audio channel. */
#if ONE_CHAN
if (c != 0) continue;
#endif
multi_modem_process_sample(c,audio_sample);
}
/* When a complete frame is accumulated, */
/* process_rec_frame, below, is called. */
}
text_color_set(DW_COLOR_INFO);
dw_printf ("\n\n");
#if EXPERIMENT_G
for (j=0; j