New AFSK demodulators.

'A' uses mark and space filters but simpler and cleaner
than earlier attempts.
New 'B' uses a different technique where the demodulated
signal is proportional to the frequency.
This commit is contained in:
wb2osz 2020-11-27 21:25:35 -05:00
parent fdf660a7f1
commit 9922f176b2
12 changed files with 769 additions and 599 deletions

View File

@ -236,7 +236,7 @@ int main (int argc, char *argv[])
my_audio_config.achan[channel].space_freq = DEFAULT_SPACE_FREQ;
my_audio_config.achan[channel].baud = DEFAULT_BAUD;
strlcpy (my_audio_config.achan[channel].profiles, "E", sizeof(my_audio_config.achan[channel].profiles));
strlcpy (my_audio_config.achan[channel].profiles, "A", sizeof(my_audio_config.achan[channel].profiles));
my_audio_config.achan[channel].num_freq = 1;
my_audio_config.achan[channel].offset = 0;
@ -430,19 +430,21 @@ int main (int argc, char *argv[])
/* We have similar logic in direwolf.c, config.c, gen_packets.c, and atest.c, */
/* that need to be kept in sync. Maybe it could be a common function someday. */
if (my_audio_config.achan[0].baud == 100) {
if (my_audio_config.achan[0].baud == 100) { // What was this for?
my_audio_config.achan[0].modem_type = MODEM_AFSK;
my_audio_config.achan[0].mark_freq = 1615;
my_audio_config.achan[0].space_freq = 1785;
strlcpy (my_audio_config.achan[0].profiles, "D", sizeof(my_audio_config.achan[0].profiles));
//strlcpy (my_audio_config.achan[0].profiles, "A", sizeof(my_audio_config.achan[0].profiles));
}
else if (my_audio_config.achan[0].baud < 600) {
else if (my_audio_config.achan[0].baud < 600) { // e.g. HF SSB packet
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;
strlcpy (my_audio_config.achan[0].profiles, "D", sizeof(my_audio_config.achan[0].profiles));
// Previously we had a "D" which was fine tuned for 300 bps.
// In v1.7, it's not clear if we should use "B" or just stick with "A".
//strlcpy (my_audio_config.achan[0].profiles, "B", sizeof(my_audio_config.achan[0].profiles));
}
else if (my_audio_config.achan[0].baud < 1800) {
else if (my_audio_config.achan[0].baud < 1800) { // common 1200
my_audio_config.achan[0].modem_type = MODEM_AFSK;
my_audio_config.achan[0].mark_freq = DEFAULT_MARK_FREQ;
my_audio_config.achan[0].space_freq = DEFAULT_SPACE_FREQ;
@ -460,7 +462,7 @@ int main (int argc, char *argv[])
my_audio_config.achan[0].space_freq = 0;
strlcpy (my_audio_config.achan[0].profiles, "", sizeof(my_audio_config.achan[0].profiles));
}
else if (my_audio_config.achan[0].baud == 12345) {
else if (my_audio_config.achan[0].baud == 12345) { // Hack for different use of 9600
my_audio_config.achan[0].modem_type = MODEM_AIS;
my_audio_config.achan[0].baud = 9600;
my_audio_config.achan[0].mark_freq = 0;
@ -473,7 +475,7 @@ int main (int argc, char *argv[])
// Will make more precise in afsk demod init.
my_audio_config.achan[0].mark_freq = 2083; // Actually 2083.3 - logic 1.
my_audio_config.achan[0].space_freq = 1563; // Actually 1562.5 - logic 0.
strlcpy (my_audio_config.achan[0].profiles, "D", sizeof(my_audio_config.achan[0].profiles));
strlcpy (my_audio_config.achan[0].profiles, "A", sizeof(my_audio_config.achan[0].profiles));
}
else {
my_audio_config.achan[0].modem_type = MODEM_SCRAMBLE;

View File

@ -1337,7 +1337,7 @@ void config_init (char *fname, struct audio_s *p_audio_config,
// Will make more precise in afsk demod init.
p_audio_config->achan[channel].mark_freq = 2083; // Actually 2083.3 - logic 1.
p_audio_config->achan[channel].space_freq = 1563; // Actually 1562.5 - logic 0.
// ? strlcpy (p_audio_config->achan[channel].profiles, "D", sizeof(p_audio_config->achan[channel].profiles));
// ? strlcpy (p_audio_config->achan[channel].profiles, "A", sizeof(p_audio_config->achan[channel].profiles));
}
else {
p_audio_config->achan[channel].modem_type = MODEM_SCRAMBLE;

View File

@ -198,46 +198,57 @@ int demod_init (struct audio_s *pa)
assert (num_letters == (int)(strlen(just_letters)));
/*
* Pick a good default demodulator if none specified.
* Pick a good default demodulator if none specified.
* Previously, we had "D" optimized for 300 bps.
* Gone in 1.7 so it is always "A+".
*/
if (num_letters == 0) {
strlcpy (just_letters, "A", sizeof(just_letters));
num_letters = strlen(just_letters);
if (save_audio_config_p->achan[chan].baud < 600) {
/* This has been optimized for 300 baud. */
strlcpy (just_letters, "D", sizeof(just_letters));
}
else {
#if __arm__
/* We probably don't have a lot of CPU power available. */
/* Previously we would use F if possible otherwise fall back to A. */
/* In version 1.2, new default is E+ /3. */
strlcpy (just_letters, "E", sizeof(just_letters)); // version 1.2 now E.
if (have_plus != -1) have_plus = 1; // Add as default for version 1.2
if (have_plus != -1) have_plus = 1; // Add as default for version 1.2
// If not explicitly turned off.
if (save_audio_config_p->achan[chan].decimate == 0) {
if (save_audio_config_p->adev[ACHAN2ADEV(chan)].samples_per_sec > 40000) {
save_audio_config_p->achan[chan].decimate = 3;
}
}
#else
strlcpy (just_letters, "E", sizeof(just_letters)); // version 1.2 changed C to E.
if (have_plus != -1) have_plus = 1; // Add as default for version 1.2
// If not explicitly turned off.
#endif
}
num_letters = 1;
}
/*
* Special case for ARM.
* The higher end ARM chips have loads of power but many people
* are using a single core Pi Zero or similar.
* (I'm still using a model 1 for my digipeater/IGate!)
* Decreasing CPU requirement has a negligible impact on decoding performance.
*
* atest -PA- 01_Track_1.wav --> 1002 packets decoded.
* atest -PA- -D3 01_Track_1.wav --> 997 packets decoded.
*
* Someone concerned about 1/2 of one percent difference can add "-D 1"
*/
#if __arm__
if (save_audio_config_p->achan[chan].decimate == 0) {
if (save_audio_config_p->adev[ACHAN2ADEV(chan)].samples_per_sec > 40000) {
save_audio_config_p->achan[chan].decimate = 3;
}
}
#endif
/*
* Number of filter taps is proportional to number of audio samples in a "symbol" duration.
* These can get extremely large for low speeds, e.g. 300 baud.
* In this case, increase the decimation ration. Crude approximation. Could be improved.
*/
if (save_audio_config_p->achan[chan].decimate == 0 &&
save_audio_config_p->adev[ACHAN2ADEV(chan)].samples_per_sec > 40000 &&
save_audio_config_p->achan[chan].baud < 600) {
// Avoid enormous number of filter taps.
save_audio_config_p->achan[chan].decimate = 3;
}
assert (num_letters == (int)(strlen(just_letters)));
/*
* Put it back together again.
*/
assert (num_letters == (int)(strlen(just_letters)));
/* At this point, have_plus can have 3 values: */
/* 1 = turned on, either explicitly or by applied default */
@ -286,7 +297,7 @@ int demod_init (struct audio_s *pa)
if (save_audio_config_p->achan[chan].decimate == 0) {
save_audio_config_p->achan[chan].decimate = 1;
if (strchr (just_letters, 'D') != NULL && save_audio_config_p->adev[ACHAN2ADEV(chan)].samples_per_sec > 40000) {
if (strchr (just_letters, 'B') != NULL && save_audio_config_p->adev[ACHAN2ADEV(chan)].samples_per_sec > 40000) {
save_audio_config_p->achan[chan].decimate = 3;
}
}

View File

@ -221,7 +221,7 @@ void demod_9600_init (enum modem_t modem_type, int samples_per_sec, int baud, st
//dw_printf ("demod_9600_init: call gen_lowpass(fc=%.2f, , size=%d, )\n", fc, D->lp_filter_size);
(void)gen_lowpass (fc, D->lp_filter, D->lp_filter_size, D->lp_window, 0);
gen_lowpass (fc, D->lp_filter, D->lp_filter_size, D->lp_window);
/* Version 1.2: Experiment with different slicing levels. */

File diff suppressed because it is too large Load Diff

View File

@ -501,7 +501,7 @@ void demod_psk_init (enum modem_t modem_type, enum v26_e v26_alt, int samples_pe
*/
float fc = correct_baud * D->u.psk.lpf_baud / (float)samples_per_sec;
gen_lowpass (fc, D->u.psk.lp_filter, D->u.psk.lp_filter_taps, D->u.psk.lp_window, 0);
gen_lowpass (fc, D->u.psk.lp_filter, D->u.psk.lp_filter_taps, D->u.psk.lp_window);
/*
* No point in having multiple numbers for signal level.

134
src/dsp.c
View File

@ -43,7 +43,6 @@
#include "dsp.h"
//#include "fsk_demod_agc.h" /* for M_FILTER_SIZE, etc. */
#define MIN(a,b) ((a)<(b)?(a):(b))
#define MAX(a,b) ((a)>(b)?(a):(b))
@ -127,7 +126,7 @@ float window (bp_window_t type, int size, int j)
*----------------------------------------------------------------*/
int gen_lowpass (float fc, float *lp_filter, int filter_size, bp_window_t wtype, float lp_delay_fract)
void gen_lowpass (float fc, float *lp_filter, int filter_size, bp_window_t wtype)
{
int j;
float G;
@ -175,54 +174,7 @@ int gen_lowpass (float fc, float *lp_filter, int filter_size, bp_window_t wtype,
lp_filter[j] = lp_filter[j] / G;
}
// Calculate the signal delay.
// If a signal at level 0 steps to level 1, this is the time that it would
// take for the output to reach 0.5.
//
// Examples:
//
// Filter has one tap with value of 1.0.
// Output is immediate so I would call this delay of 0.
//
// Filter coefficients: 0.2, 0.2, 0.2, 0.2, 0.2
// "1" inputs Out
// 1 0.2
// 2 0.4
// 3 0.6
//
// In this case, the output does not change immediately.
// It takes two more samples to reach the half way point
// so it has a delay of 2.
float sum = 0;
int delay = 0;
if (lp_delay_fract == 0) lp_delay_fract = 0.5;
for (j=0; j<filter_size; j++) {
sum += lp_filter[j];
#if DEBUG1
dw_printf ("lp_filter[%d] = %.3f sum = %.3f lp_delay_fract = %.3f\n", j, lp_filter[j], sum, lp_delay_fract);
#endif
if (sum > lp_delay_fract) {
delay = j;
break;
}
}
#if DEBUG1
dw_printf ("Low Pass Delay = %d samples\n", delay) ;
#endif
// Hmmm. This might have been wasted effort. The result is always half the number of taps.
if (delay < 2 || delay > filter_size - 2) {
text_color_set(DW_COLOR_ERROR);
dw_printf ("Internal error, %s %d, delay %d for size %d\n", __func__, __LINE__, delay, filter_size);
}
return (delay);
return;
} /* end gen_lowpass */
@ -369,4 +321,86 @@ void gen_ms (int fc, int sps, float *sin_table, float *cos_table, int filter_siz
} /* end gen_ms */
/*------------------------------------------------------------------
*
* Name: rrc
*
* Purpose: Root Raised Cosine function.
* Why do they call it that?
* It's mostly the sinc function with cos windowing to taper off edges faster.
*
* Inputs: t - Time in units of symbol duration.
* i.e. The centers of two adjacent symbols would differ by 1.
*
* a - Roll off factor, between 0 and 1.
*
* Returns: Basically the sinc (sin(x)/x) function with edges decreasing faster.
* Should be 1 for t = 0 and 0 at all other integer values of t.
*
*----------------------------------------------------------------*/
__attribute__((const))
float rrc (float t, float a)
{
float sinc, window, result;
if (t > -0.001 && t < 0.001) {
sinc = 1;
}
else {
sinc = sinf(M_PI * t) / (M_PI * t);
}
if (fabsf(a * t) > 0.499 && fabsf(a * t) < 0.501) {
window = M_PI / 4;
}
else {
window = cos(M_PI * a * t) / ( 1 - powf(2 * a * t, 2));
// This made nicer looking waveforms for generating signal.
//window = cos(M_PI * a * t);
// Do we want to let it go negative?
// I think this would happen when a > 0.5 / (filter width in symbol times)
if (window < 0) {
//printf ("'a' is too large for range of 't'.\n");
//window = 0;
}
}
result = sinc * window;
#if DEBUGRRC
// t should vary from - to + half of filter size in symbols.
// Result should be 1 at t=0 and 0 at all other integer values of t.
printf ("%.3f, %.3f, %.3f, %.3f\n", t, sinc, window, result);
#endif
return (result);
}
// The Root Raised Cosine (RRC) low pass filter is suppposed to minimize Intersymbol Interference (ISI).
void gen_rrc_lowpass (float *pfilter, int filter_taps, float rolloff, float samples_per_symbol)
{
int k;
float t;
for (k = 0; k < filter_taps; k++) {
t = (k - ((filter_taps - 1.0) / 2.0)) / samples_per_symbol;
pfilter[k] = rrc (t, rolloff);
}
// Scale it for unity gain.
t = 0;
for (k = 0; k < filter_taps; k++) {
t += pfilter[k];
}
for (k = 0; k < filter_taps; k++) {
pfilter[k] = pfilter[k] / t;
}
}
/* end dsp.c */

View File

@ -5,9 +5,13 @@
float window (bp_window_t type, int size, int j);
int gen_lowpass (float fc, float *lp_filter, int filter_size, bp_window_t wtype, float lp_delay_fract);
void gen_lowpass (float fc, float *lp_filter, int filter_size, bp_window_t wtype);
void gen_bandpass (float f1, float f2, float *bp_filter, int filter_size, bp_window_t wtype);
void gen_ms (int fc, int samples_per_sec, float *sin_table, float *cos_table, int filter_size, int wtype);
__attribute__((const)) float rrc (float t, float a);
void gen_rrc_lowpass (float *pfilter, int filter_taps, float rolloff, float samples_per_symbol);

View File

@ -1,2 +0,0 @@
#define TUNE_MS_FILTER_SIZE 140
#define TUNE_PRE_BAUD 1.080

View File

@ -20,6 +20,31 @@ typedef enum bp_window_e { BP_WINDOW_TRUNCATED,
BP_WINDOW_BLACKMAN,
BP_WINDOW_FLATTOP } bp_window_t;
// Experimental low pass filter to detect DC bias or low frequency changes.
// IIR behaves like an analog R-C filter.
// Intuitively, it seems like FIR would be better because it is based on a finite history.
// However, it would require MANY taps and a LOT of computation for a low frequency.
// We can use a little trick here to keep a running average.
// This would be equivalent to convolving with an array of all 1 values.
// That would eliminate the need to multiply.
// We can also eliminate the need to add them all up each time by keeping a running total.
// Add a sample to the total when putting it in our array of recent samples.
// Subtract it from the total when it gets pushed off the end.
// We can also eliminate the need to shift them all down by using a circular buffer.
#define CIC_LEN_MAX 4000
typedef struct cic_s {
int len; // Number of elements used.
// Might want to dynamically allocate.
short in[CIC_LEN_MAX]; // Samples coming in.
int sum; // Running sum.
int inext; // Next position to fill.
} cic_t;
#define MAX_FILTER_SIZE 404 /* 401 is needed for profile A, 300 baud & 44100. Revisit someday. */
struct demodulator_state_s
{
@ -39,30 +64,12 @@ struct demodulator_state_s
// Data is sampled when it overflows.
int ms_filter_size; /* Size of mark & space filters, in audio samples. */
/* Started off as a guess of one bit length */
/* but about 2 bit times turned out to be better. */
/* Currently using same size for any prefilter. */
#define MAX_FILTER_SIZE 320 /* 304 is needed for profile C, 300 baud & 44100. */
/*
* Filter length for Mark & Space in bit times.
* e.g. 1 means 1/1200 second for 1200 baud.
*/
float ms_filter_len_bits;
float lp_delay_fract;
/*
* Window type for the various filters.
*/
bp_window_t pre_window;
bp_window_t ms_window;
bp_window_t lp_window;
/*
* Alternate Low pass filters.
* First is arbitrary number for quick IIR.
@ -78,16 +85,13 @@ struct demodulator_state_s
/* In practice, it turned out a little larger */
/* for profiles B, C, D. */
float lp_filter_len_bits; /* Length in number of bit times. */
float lp_filter_width_sym; /* Length in number of symbol times. */
int lp_filter_size; /* Size of Low Pass filter, in audio samples. */
/* Previously it was always the same as the M/S */
/* filters but in version 1.2 it's now independent. */
#define lp_filter_len_bits lp_filter_width_sym // FIXME: temp hack
int lp_filter_delay; /* Number of samples that the low pass filter */
/* delays the signal. */
/* New in 1.6. */
int lp_filter_taps; /* Size of Low Pass filter, in audio samples. */
#define lp_filter_size lp_filter_taps // FIXME: temp hack
/*
@ -111,6 +115,7 @@ struct demodulator_state_s
/*
* Phase Locked Loop (PLL) inertia.
* Larger number means less influence by signal transitions.
* It is more resistant to change when locked on to a signal.
*/
float pll_locked_inertia;
float pll_searching_inertia;
@ -129,23 +134,17 @@ struct demodulator_state_s
/* lower = min(1600,1800) - 0.5 * 300 = 1450 */
/* upper = max(1600,1800) + 0.5 * 300 = 1950 */
float pre_filter_len_bits; /* Length in number of bit times. */
float pre_filter_len_sym; // Length in number of symbol times.
#define pre_filter_len_bits pre_filter_len_sym // temp until all references changed.
int pre_filter_size; /* Size of pre filter, in audio samples. */
bp_window_t pre_window; // Window type for filter shaping.
int pre_filter_taps; // Calculated number of filter taps.
#define pre_filter_size pre_filter_taps // temp until all references changed.
float pre_filter[MAX_FILTER_SIZE] __attribute__((aligned(16)));
/*
* Kernel for the mark and space detection filters.
*/
float m_sin_table[MAX_FILTER_SIZE] __attribute__((aligned(16)));
float m_cos_table[MAX_FILTER_SIZE] __attribute__((aligned(16)));
float s_sin_table[MAX_FILTER_SIZE] __attribute__((aligned(16)));
float s_cos_table[MAX_FILTER_SIZE] __attribute__((aligned(16)));
float raw_cb[MAX_FILTER_SIZE] __attribute__((aligned(16))); // audio in, need better name.
/*
* The rest are continuously updated.
@ -154,11 +153,6 @@ struct demodulator_state_s
unsigned int lo_phase; /* Local oscillator for PSK. */
/*
* Most recent raw audio samples, before/after prefiltering.
*/
float raw_cb[MAX_FILTER_SIZE] __attribute__((aligned(16)));
/*
* Use half of the AGC code to get a measure of input audio amplitude.
* These use "quick" attack and "sluggish" decay while the
@ -170,24 +164,14 @@ struct demodulator_state_s
float alevel_mark_peak;
float alevel_space_peak;
/*
* Input to the mark/space detector.
* Could be prefiltered or raw audio.
*/
float ms_in_cb[MAX_FILTER_SIZE] __attribute__((aligned(16)));
/*
* Outputs from the mark and space amplitude detection,
* used as inputs to the FIR lowpass filters.
* Kernel for the lowpass filters.
*/
float m_amp_cb[MAX_FILTER_SIZE] __attribute__((aligned(16)));
float s_amp_cb[MAX_FILTER_SIZE] __attribute__((aligned(16)));
float lp_filter[MAX_FILTER_SIZE] __attribute__((aligned(16)));
float m_peak, s_peak;
float m_valley, s_valley;
float m_amp_prev, s_amp_prev;
@ -266,6 +250,113 @@ struct demodulator_state_s
union {
//////////////////////////////////////////////////////////////////////////////////
// //
// AFSK only - new method in 1.7 //
// //
//////////////////////////////////////////////////////////////////////////////////
struct afsk_only_s {
unsigned int m_osc_phase; // Phase for Mark local oscillator.
unsigned int m_osc_delta; // How much to change for each audio sample.
unsigned int s_osc_phase; // Phase for Space local oscillator.
unsigned int s_osc_delta; // How much to change for each audio sample.
unsigned int c_osc_phase; // Phase for Center frequency local oscillator.
unsigned int c_osc_delta; // How much to change for each audio sample.
// Need two mixers for profile "A".
float m_I_raw[MAX_FILTER_SIZE] __attribute__((aligned(16)));
float m_Q_raw[MAX_FILTER_SIZE] __attribute__((aligned(16)));
float s_I_raw[MAX_FILTER_SIZE] __attribute__((aligned(16)));
float s_Q_raw[MAX_FILTER_SIZE] __attribute__((aligned(16)));
// Only need one mixer for profile "B". Reuse the same storage?
//#define c_I_raw m_I_raw
//#define c_Q_raw m_Q_raw
float c_I_raw[MAX_FILTER_SIZE] __attribute__((aligned(16)));
float c_Q_raw[MAX_FILTER_SIZE] __attribute__((aligned(16)));
int use_rrc; // Use RRC rather than generic low pass.
float rrc_width_sym; /* Width of RRC filter in number of symbols. */
float rrc_rolloff; /* Rolloff factor for RRC. Between 0 and 1. */
float prev_phase; // To see phase shift between samples for FM demod.
float normalize_rpsam; // Normalize to -1 to +1 for expected tones.
} afsk;
//////////////////////////////////////////////////////////////////////////////////
// //
// Baseband only, AKA G3RUH //
// //
//////////////////////////////////////////////////////////////////////////////////
struct bb_only_s {
float rrc_width_sym; /* Width of RRC filter in number of symbols. */
float rrc_rolloff; /* Rolloff factor for RRC. Between 0 and 1. */
int rrc_filter_taps; // Number of elements used in the next two.
// FIXME: TODO: reevaluate max size needed.
float audio_in[MAX_FILTER_SIZE] __attribute__((aligned(16))); // Audio samples in.
// FIXME: use lp_filter
float rrc_filter[MAX_FILTER_SIZE] __attribute__((aligned(16))); // RRC Low pass filter.
float lp_1_iir_param; // very low pass filters to get DC offset.
float lp_1_out;
float lp_2_iir_param;
float lp_2_out;
float agc_1_fast_attack; // Signal envelope detection.
float agc_1_slow_decay;
float agc_1_peak;
float agc_1_valley;
float agc_2_fast_attack;
float agc_2_slow_decay;
float agc_2_peak;
float agc_2_valley;
float agc_3_fast_attack;
float agc_3_slow_decay;
float agc_3_peak;
float agc_3_valley;
// CIC low pass filters to detect DC bias or low frequency changes.
// IIR behaves like an analog R-C filter.
// Intuitively, it seems like FIR would be better because it is based on a finite history.
// However, it would require MANY taps and a LOT of computation for a low frequency.
// We can use a little trick here to keep a running average.
// This would be equivalent to convolving with an array of all 1 values.
// That would eliminate the need to multiply.
// We can also eliminate the need to add them all up each time by keeping a running total.
// Add a sample to the total when putting it in our array of recent samples.
// Subtract it from the total when it gets pushed off the end.
// We can also eliminate the need to shift them all down by using a circular buffer.
// This only works with integers because float would have cummulated round off errors.
cic_t cic_center1;
cic_t cic_above;
cic_t cic_below;
} bb;
//////////////////////////////////////////////////////////////////////////////////
// //
// PSK only. //

View File

@ -1,5 +1,7 @@
@CUSTOM_SHELL_SHABANG@
@GEN_PACKETS_BIN@ -n 100 -o test12.wav
@ATEST_BIN@ -F0 -PE -L64 -G72 test12.wav
@ATEST_BIN@ -F1 -PE -L70 -G75 test12.wav
@ATEST_BIN@ -F0 -PA -L66 -G72 test12.wav
@ATEST_BIN@ -F1 -PA -L72 -G78 test12.wav
@ATEST_BIN@ -F0 -PB -L66 -G72 test12.wav
@ATEST_BIN@ -F1 -PB -L70 -G76 test12.wav

View File

@ -1,5 +1,7 @@
@CUSTOM_SHELL_SHABANG@
@GEN_PACKETS_BIN@ -B300 -n 100 -o test3.wav
@ATEST_BIN@ -B300 -F0 -L68 -G69 test3.wav
@ATEST_BIN@ -B300 -F1 -L71 -G75 test3.wav
@ATEST_BIN@ -B300 -PA -F0 -L65 -G71 test3.wav
@ATEST_BIN@ -B300 -PA -F1 -L69 -G75 test3.wav
@ATEST_BIN@ -B300 -PB -F0 -L69 -G75 test3.wav
@ATEST_BIN@ -B300 -PB -F1 -L73 -G79 test3.wav