diff --git a/src/atest.c b/src/atest.c
index 5c19775..7e8f03b 100644
--- a/src/atest.c
+++ b/src/atest.c
@@ -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;
diff --git a/src/config.c b/src/config.c
index 08f71a3..93b92a1 100644
--- a/src/config.c
+++ b/src/config.c
@@ -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;
diff --git a/src/demod.c b/src/demod.c
index 281367b..3f032b4 100644
--- a/src/demod.c
+++ b/src/demod.c
@@ -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;
 		}
 	      }
diff --git a/src/demod_9600.c b/src/demod_9600.c
index 2f98983..ef45e4c 100644
--- a/src/demod_9600.c
+++ b/src/demod_9600.c
@@ -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. */
 
diff --git a/src/demod_afsk.c b/src/demod_afsk.c
index 7a007d1..c34d8bc 100644
--- a/src/demod_afsk.c
+++ b/src/demod_afsk.c
@@ -1,7 +1,7 @@
 //
 //    This file is part of Dire Wolf, an amateur radio packet TNC.
 // 
-//    Copyright (C) 2011, 2012, 2013, 2014, 2015  John Langner, WB2OSZ
+//    Copyright (C) 2011, 2012, 2013, 2014, 2015, 2020  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
@@ -23,8 +23,7 @@
 // #define DEBUG3 1	/* print carrier detect changes. */
 
 // #define DEBUG4 1	/* capture AFSK demodulator output to log files */
-
-// #define DEBUG5 1	/* capture 9600 output to log files */
+			/* Can be used to make nice plots. */
 
 
 /*------------------------------------------------------------------
@@ -62,15 +61,32 @@
 #define MIN(a,b) ((a)<(b)?(a):(b))
 #define MAX(a,b) ((a)>(b)?(a):(b))
 
+#define TUNE(envvar,param,name,fmt) { 				\
+	char *e = getenv(envvar);				\
+	if (e != NULL) {					\
+	  param = atof(e);					\
+	  text_color_set (DW_COLOR_ERROR);			\
+	  dw_printf ("TUNE: " name " = " fmt "\n", param);	\
+	} }
 
-/* Quick approximation to sqrt(x*x+y*y) */
+
+// Cosine table indexed by unsigned byte.
+static float fcos256_table[256];
+
+#define fcos256(x) (fcos256_table[((x)>>24)&0xff])
+#define fsin256(x) (fcos256_table[(((x)>>24)-64)&0xff])
+
+static void nudge_pll (int chan, int subchan, int slice, float demod_out, struct demodulator_state_s *D, float amplitude);
+
+
+/* Quick approximation to sqrt(x*x + y*y) */
 /* No benefit for regular PC. */
-/* Should help with microcomputer platform. */
+/* Might help with microcomputer platform??? */
 
-#if 0	// not using anymore
 __attribute__((hot)) __attribute__((always_inline))
-static inline float z (float x, float y)
+static inline float fast_hypot(float x, float y)
 {
+#if 0
         x = fabsf(x);
         y = fabsf(y);
 
@@ -80,8 +96,11 @@ static inline float z (float x, float y)
         else {
           return (y * .941246f + x * .41f);
         }
-}
+#else
+	return (hypotf(x,y));
 #endif
+}
+
 
 /* Add sample to buffer and shift the rest down. */
 
@@ -96,22 +115,31 @@ static inline void push_sample (float val, float *buff, int size)
 /* FIR filter kernel. */
 
 __attribute__((hot)) __attribute__((always_inline))
-static inline float convolve (const float *__restrict__ data, const float *__restrict__ filter, int filter_size)
+static inline float convolve (const float *__restrict__ data, const float *__restrict__ filter, int filter_taps)
 {
 	float sum = 0.0f;
 	int j;
 
-
 //#pragma GCC ivdep				// ignored until gcc 4.9
-	for (j=0; j<filter_size; j++) {
+	for (j=0; j<filter_taps; j++) {
 	    sum += filter[j] * data[j];
 	}
-
 	return (sum);
 }
 
-/* Automatic gain control. */
-/* Result should settle down to 1 unit peak to peak.  i.e. -0.5 to +0.5 */
+// Automatic Gain control - used when we have a single slicer.
+//
+// The first step is to create an envelope for the peak and valley
+// of the mark or space amplitude.  We need to keep track of the valley
+// because it does not go down to zero when the tone is not present.
+// We want to find the difference between tone present and not.
+//
+// We use an IIR filter with fast attack and slow decay which only considers the past.
+// Perhaps an improvement could be obtained by looking in the future as well.
+//
+
+// Result should settle down to 1 unit peak to peak.  i.e. -0.5 to +0.5 
+
 
 __attribute__((hot)) __attribute__((always_inline))
 static inline float agc (float in, float fast_attack, float slow_decay, float *ppeak, float *pvalley)
@@ -130,13 +158,47 @@ static inline float agc (float in, float fast_attack, float slow_decay, float *p
 	  *pvalley = in * slow_decay + *pvalley * (1.0f - slow_decay);
 	}
 
+#if 1
+	float x = in;
+	if (x > *ppeak) x = *ppeak;		// experiment: clip to envelope?
+	if (x < *pvalley) x = *pvalley;
+#endif
 	if (*ppeak > *pvalley) {
-	  return ((in - 0.5f * (*ppeak + *pvalley)) / (*ppeak - *pvalley));
+
+	  return ((x - 0.5f * (*ppeak + *pvalley)) / (*ppeak - *pvalley)); // my original AGC
+
+	  //return (( x - 0.5f * (*ppeak + *pvalley )) * ( *ppeak - *pvalley )); // see note below.
+	  //return (x - 0.5f * (*ppeak + *pvalley));	// not as good either.
 	}
 	return (0.0f);
 }
 
 
+// K6JQ  pointed me to this wonderful article:
+// Improved Automatic Threshold Correction Methods for FSK by Kok Chen, W7AY.
+// http://www.w7ay.net/site/Technical/ATC/index.html
+//
+// The stated problem is a little different, selective fading for HF RTTY, but the
+// general idea is the similar:  Compensating for imbalance of the two tones.
+//
+// The stronger tone probably has a better S/N ratio so we apply a larger
+// weight to it.  Effectively it is comparing power rather than amplitude.
+// This is the optimal method from the article referenced.
+//
+// Interesting idea but it did not work as well as the original AGC in this case.
+// For VHF FM we are not dealing with rapid deep selective fading of one tone.
+// Instead we have an imbalance which is the same for the whole frame.
+// It might be interesting to try this with HF SSB packet which is much like RTTY.
+//
+// I use the term valley rather than noise floor.
+// After a little algebra, it looks remarkably similar to the function above.
+//
+//	return (( x - valley ) * ( peak - valley ) - 0.5f * ( peak - valley ) * ( peak - valley ));
+//	return (( x - valley ) - 0.5f * ( peak - valley )) * ( peak - valley ));
+//	return (( x - 0.5f * (peak + valley )) * ( peak - valley ));
+
+
+
 /*
  * for multi-slicer experiment.
  */
@@ -162,11 +224,7 @@ static inline float agc (float in, float fast_attack, float slow_decay, float *p
  *	
  *		D		- Pointer to demodulator state for given channel.
  *
- * Outputs:	D->ms_filter_size
- *		D->m_sin_table[] 
- *		D->m_cos_table[]
- *		D->s_sin_table[] 
- *		D->s_cos_table[]
+ * Outputs:	
  *
  * Returns:     None.
  *		
@@ -180,6 +238,10 @@ void demod_afsk_init (int samples_per_sec, int baud, int mark_freq,
 {
 	
 	int j;
+
+	for (j = 0; j < 256; j++) {
+	  fcos256_table[j] = cosf((float)j * 2.0f * (float)M_PI / 256.0f);
+	}
 	
 	memset (D, 0, sizeof(struct demodulator_state_s));
 	D->num_slicers = 1;
@@ -188,122 +250,156 @@ void demod_afsk_init (int samples_per_sec, int baud, int mark_freq,
 	dw_printf ("demod_afsk_init (rate=%d, baud=%d, mark=%d, space=%d, profile=%c\n",
 		samples_per_sec, baud, mark_freq, space_freq, profile);
 #endif
-				
-#ifdef TUNE_PROFILE
-	profile = TUNE_PROFILE;
-#endif
+	D->profile = profile;
 
+	switch (D->profile) {
 
-	D->profile = profile;		// so we know whether to take fast path later.
+	  case 'A':	// Official name
+	  case 'E':	// For compatibility during transition
 
-	switch (profile) {
+	    D->profile = 'A';
 
-	  case 'D':
-
-		/* Prefilter, Cosine window, FIR lowpass. Tweeked for 300 baud. */
+	    /* New in version 1.7 */
+	    /* This is a simpler version of what has been used all along. */
+	    /* Rather than convolving each sample with a pre-computed mark and */
+	    /* space filter, we have two free running local oscillators.  */
+	    /* Also see if we can do better with a Root Raised Cosine filter */
+	    /* which supposedly reduces intersymbol interference. */
 
 	    D->use_prefilter = 1;		/* first, a bandpass filter. */
-	    D->prefilter_baud = 0.87;		
-	    D->pre_filter_len_bits = 1.857;	
-	    D->pre_window = BP_WINDOW_COSINE;
 
-	    D->ms_filter_len_bits = 1.857;		/* 91 @ 44100/3, 300 */
-	    D->ms_window = BP_WINDOW_COSINE;
-		
-	    //D->bp_window = BP_WINDOW_COSINE;
+	    if (baud > 600) {
+	      D->prefilter_baud = 0.155;
+						// Low cutoff below mark, high cutoff above space
+						// as fraction of the symbol rate.
+						// Intuitively you might expect this to be about
+						// half the symbol rate, e.g. 600 Hz outside
+						// the two tones of interest for 1200 baud.
+						// It turns out that narrower is better.
 
-	    D->lpf_use_fir = 1;
-	    D->lpf_baud = 1.10;
-	    D->lp_filter_len_bits = D->ms_filter_len_bits;	
-	    D->lp_window = BP_WINDOW_TRUNCATED;
+	      D->pre_filter_len_sym = 383 * 1200. / 44100.;		// about 8 symbols
+	      D->pre_window = BP_WINDOW_TRUNCATED;
+	    }
+	    else {
+	      D->prefilter_baud = 0.87;			// TOTO: fine tune
+	      D->pre_filter_len_sym = 1.857;
+	      D->pre_window = BP_WINDOW_COSINE;
+	    }
+				    
+	    // Local oscillators for Mark and Space tones.
 
-	    D->agc_fast_attack = 0.495;		
-	    D->agc_slow_decay = 0.00022;
-	    D->hysteresis = 0.027;
+	    D->u.afsk.m_osc_phase = 0;
+	    D->u.afsk.m_osc_delta = round ( pow(2., 32.) * (double)mark_freq / (double)samples_per_sec );
 
-	    D->pll_locked_inertia = 0.620;
-	    D->pll_searching_inertia = 0.350;
-	    break;
+	    D->u.afsk.s_osc_phase = 0;
+	    D->u.afsk.s_osc_delta = round ( pow(2., 32.) * (double)space_freq / (double)samples_per_sec );
 
-	  case 'F':	// removed obsolete. treat as E for now.
-	  case 'E':
+	    D->u.afsk.use_rrc = 1;
+	    TUNE("TUNE_USE_RRC", D->u.afsk.use_rrc, "use_rrc", "%d")
 
-		/* 1200 baud - Started out similar to C but add prefilter. */
-		/* Version 1.2 */
-		/* Enhancements: 					*/
-		/*  + Add prefilter.  Previously used for 300 baud D, but not 1200. */
-		/*  + Prefilter length now independent of M/S filters.	*/
-		/*  + Lowpass filter length now independent of M/S filters.	*/
-		/*  + Allow mixed window types.	*/
+	    if (D->u.afsk.use_rrc) {
+	      D->u.afsk.rrc_width_sym = 2.80;
+	      D->u.afsk.rrc_rolloff = 0.20;
+	    }
+	    else {
+	      D->lpf_baud = 0.14;
+	      D->lp_filter_width_sym = 1.388;	
+	      D->lp_window = BP_WINDOW_TRUNCATED;
+	    }
 
-	    //D->bp_window = BP_WINDOW_COSINE;	/* The name says BP but it is used for all of them. */
-
-	    D->use_prefilter = 1;		/* first, a bandpass filter. */
-	    D->prefilter_baud = 0.23;		
-	    D->pre_filter_len_bits = 156 * 1200. / 44100.;	
-	    D->pre_window = BP_WINDOW_TRUNCATED;
-
-	    D->ms_filter_len_bits = 74 * 1200. / 44100.;		
-	    D->ms_window = BP_WINDOW_COSINE;
-
-	    D->lpf_use_fir = 1;
-	    D->lpf_baud = 1.18;
-	    D->lp_filter_len_bits = 63 * 1200. / 44100.;		
-	    D->lp_window = BP_WINDOW_TRUNCATED;
-
-	    //D->agc_fast_attack = 0.300;		
-	    //D->agc_slow_decay = 0.000185;
 	    D->agc_fast_attack = 0.820;		
 	    D->agc_slow_decay = 0.000214;
-	    D->hysteresis = 0.01;
+	    D->agc_fast_attack = 0.45;		
+	    D->agc_slow_decay = 0.000195;
+	    D->agc_fast_attack = 0.70;		
+	    D->agc_slow_decay = 0.000090;
 
-	    //D->pll_locked_inertia = 0.57;
-	    //D->pll_searching_inertia = 0.33;
 	    D->pll_locked_inertia = 0.74;
 	    D->pll_searching_inertia = 0.50;
 	    break;
 
+	  case 'B':	// official name
+	  case 'D':	// backward compatibility
+
+	    D->profile = 'B';
+
+	    // Experiment for version 1.7.
+	    // Up to this point, I've always used separate mark and space
+	    // filters and compared the amplitudes.
+	    // Another technique for an FM demodulator is to mix with
+	    // the center frequency and look for the rate of change of the phase.
+
+	    D->use_prefilter = 1;		/* first, a bandpass filter. */
+
+	    if (baud > 600) {
+	      D->prefilter_baud = 0.19;
+						// Low cutoff below mark, high cutoff above space
+						// as fraction of the symbol rate.
+						// Intuitively you might expect this to be about
+						// half the symbol rate, e.g. 600 Hz outside
+						// the two tones of interest for 1200 baud.
+						// It turns out that narrower is better.
+	
+	      D->pre_filter_len_sym = 8.163;	// Filter length in symbol times.
+	      D->pre_window = BP_WINDOW_TRUNCATED;
+	    }
+	    else {
+	      D->prefilter_baud = 0.87;			// TOTO: fine tune
+	      D->pre_filter_len_sym = 1.857;
+	      D->pre_window = BP_WINDOW_COSINE;
+	    }
+
+	    // Local oscillator for Center frequency.
+
+	    D->u.afsk.c_osc_phase = 0;
+	    D->u.afsk.c_osc_delta = round ( pow(2., 32.) * 0.5 * (mark_freq + space_freq) / (double)samples_per_sec );
+
+	    D->u.afsk.use_rrc = 1;
+	    TUNE("TUNE_USE_RRC", D->u.afsk.use_rrc, "use_rrc", "%d")
+
+	    if (D->u.afsk.use_rrc) {
+	      D->u.afsk.rrc_width_sym = 2.00;
+	      D->u.afsk.rrc_rolloff = 0.40;
+	    }
+	    else {
+	      D->lpf_baud = 0.5;
+	      D->lp_filter_width_sym = 1.714286;   //  63 * 1200. / 44100.;
+	      D->lp_window = BP_WINDOW_TRUNCATED;
+	    }
+
+	    // For scaling phase shift into normallized -1 to +1 range for mark and space.
+	    D->u.afsk.normalize_rpsam = 1.0 / (0.5 * abs(mark_freq - space_freq) * 2 * M_PI / samples_per_sec);
+
+	    D->pll_locked_inertia = 0.74;
+	    D->pll_searching_inertia = 0.50;
+
+	    D->alevel_mark_peak = -1;		// FIXME:  disable display
+	    D->alevel_space_peak = -1;
+	    break;
+
 	  default:
 
 	    text_color_set(DW_COLOR_ERROR);
-	    dw_printf ("Invalid filter profile = %c\n", profile);
+	    dw_printf ("Invalid AFSK demodulator profile = %c\n", profile);
 	    exit (1);
 	}
 
-#ifdef TUNE_PRE_WINDOW
-	D->pre_window = TUNE_PRE_WINDOW;
-#endif
-#ifdef TUNE_MS_WINDOW
-	D->ms_window = TUNE_MS_WINDOW;
-#endif
-#ifdef TUNE_MS2_WINDOW
-	D->ms2_window = TUNE_MS2_WINDOW;
-#endif
-#ifdef TUNE_LP_WINDOW
-	D->lp_window = TUNE_LP_WINDOW;
-#endif
 
+	TUNE("TUNE_PRE_BAUD", D->prefilter_baud, "prefilter_baud", "%.3f")
+	TUNE("TUNE_PRE_WINDOW", D->pre_window, "pre_window", "%d")
+
+	TUNE("TUNE_LPF_BAUD", D->lpf_baud, "lpf_baud", "%.3f")
+	TUNE("TUNE_LP_WINDOW", D->lp_window, "lp_window", "%d")
+
+	TUNE("TUNE_RRC_ROLLOFF", D->u.afsk.rrc_rolloff, "rrc_rolloff", "%.2f")
+	TUNE("TUNE_RRC_WIDTH_SYM", D->u.afsk.rrc_width_sym, "rrc_width_sym", "%.2f")
+
+	TUNE("TUNE_AGC_FAST", D->agc_fast_attack, "agc_fast_attack", "%.3f")
+	TUNE("TUNE_AGC_SLOW", D->agc_slow_decay, "agc_slow_decay", "%.6f")
+	  
+	TUNE("TUNE_PLL_LOCKED", D->pll_locked_inertia, "pll_locked_inertia", "%.2f")
+	TUNE("TUNE_PLL_SEARCHING", D->pll_searching_inertia, "pll_searching_inertia", "%.2f")
 
-#if defined(TUNE_AGC_FAST) && defined(TUNE_AGC_SLOW)
-	D->agc_fast_attack = TUNE_AGC_FAST;
-	D->agc_slow_decay = TUNE_AGC_SLOW;
-#endif
-#ifdef TUNE_HYST
-	D->hysteresis = TUNE_HYST;
-#endif
-#if defined(TUNE_PLL_LOCKED) && defined(TUNE_PLL_SEARCHING)
-	D->pll_locked_inertia = TUNE_PLL_LOCKED;
-	D->pll_searching_inertia = TUNE_PLL_SEARCHING;
-#endif
-#ifdef TUNE_LPF_BAUD
-	D->lpf_baud = TUNE_LPF_BAUD;
-#endif
-#ifdef TUNE_PRE_BAUD
-	D->prefilter_baud = TUNE_PRE_BAUD;
-#endif
-#ifdef TUNE_LP_DELAY_FRACT
-	D->lp_delay_fract = TUNE_LP_DELAY_FRACT;
-#endif
 
 /*
  * Calculate constants used for timing.
@@ -321,77 +417,28 @@ void demod_afsk_init (int samples_per_sec, int baud, int mark_freq,
 	  D->pll_step_per_sample = (int) round((TICKS_PER_PLL_CYCLE * (double)baud) / ((double)samples_per_sec));
 	}
 
-/*
- * Convert number of bit times to number of taps.
- */
-
-	D->pre_filter_size = (int) round( D->pre_filter_len_bits * (float)samples_per_sec / (float)baud );
-	D->ms_filter_size = (int) round( D->ms_filter_len_bits * (float)samples_per_sec / (float)baud );
-	D->lp_filter_size = (int) round( D->lp_filter_len_bits * (float)samples_per_sec / (float)baud );
-	  	 
-/* Experiment with other sizes. */
-
-#ifdef TUNE_PRE_FILTER_SIZE
-	D->pre_filter_size = TUNE_PRE_FILTER_SIZE;
-#endif
-#ifdef TUNE_MS_FILTER_SIZE
-	D->ms_filter_size = TUNE_MS_FILTER_SIZE;
-#endif
-#ifdef TUNE_LP_FILTER_SIZE
-	D->lp_filter_size = TUNE_LP_FILTER_SIZE;
-#endif
-
-	//assert (D->pre_filter_size >= 4);
-	assert (D->ms_filter_size >= 4);
-	//assert (D->lp_filter_size >= 4);
-
-	if (D->pre_filter_size > MAX_FILTER_SIZE)
-	{
-	  text_color_set (DW_COLOR_ERROR);
-	  dw_printf ("Calculated filter size of %d is too large.\n", D->pre_filter_size);
-	  dw_printf ("Decrease the audio sample rate or increase the baud rate or\n");
-	  dw_printf ("recompile the application with MAX_FILTER_SIZE larger than %d.\n",
-							MAX_FILTER_SIZE);
-	  exit (1);
-	}
-
-	if (D->ms_filter_size > MAX_FILTER_SIZE)
-	{
-	  text_color_set (DW_COLOR_ERROR);
-	  dw_printf ("Calculated filter size of %d is too large.\n", D->ms_filter_size);
-	  dw_printf ("Decrease the audio sample rate or increase the baud rate or\n");
-	  dw_printf ("recompile the application with MAX_FILTER_SIZE larger than %d.\n",
-							MAX_FILTER_SIZE);
-	  exit (1);
-	}
-
-
-
-	if (D->lp_filter_size > MAX_FILTER_SIZE) 
-	{
-	  text_color_set (DW_COLOR_ERROR);
-	  dw_printf ("Calculated filter size of %d is too large.\n", D->pre_filter_size);
-	  dw_printf ("Decrease the audio sample rate or increase the baud rate or\n");
-	  dw_printf ("recompile the application with MAX_FILTER_SIZE larger than %d.\n",
-							MAX_FILTER_SIZE);
-	  exit (1);
-	}
-
 /* 
  * Optionally apply a bandpass ("pre") filter to attenuate
  * frequencies outside the range of interest.
- * This was first used for the "D" profile for 300 baud
- * which uses narrow shift.  We expect it to have significant
- * benefit for a narrow shift.
- * In version 1.2, we will also try it with 1200 baud "E" as
- * an experiment to see how much it actually helps.
  */
 
 	if (D->use_prefilter) {
-	  float f1, f2;
 
-	  f1 = MIN(mark_freq,space_freq) - D->prefilter_baud * baud;
-	  f2 = MAX(mark_freq,space_freq) + D->prefilter_baud * baud;
+	  // odd number is a little better
+	  D->pre_filter_taps = ((int)( D->pre_filter_len_sym * (float)samples_per_sec / (float)baud )) | 1;
+
+	  TUNE("TUNE_PRE_FILTER_TAPS", D->pre_filter_taps, "pre_filter_taps", "%d")
+
+	  if (D->pre_filter_taps > MAX_FILTER_SIZE) {
+	    text_color_set (DW_COLOR_ERROR);
+	    dw_printf ("Calculated pre filter size of %d is too large.\n", D->pre_filter_taps);
+	    dw_printf ("Decrease the audio sample rate or increase the decimation factor or\n");
+	    dw_printf ("recompile the application with MAX_FILTER_SIZE larger than %d.\n", MAX_FILTER_SIZE);
+	    D->pre_filter_taps = (MAX_FILTER_SIZE - 1) | 1;
+	  }
+
+	  float f1 = MIN(mark_freq,space_freq) - D->prefilter_baud * baud;
+	  float f2 = MAX(mark_freq,space_freq) + D->prefilter_baud * baud;
 #if 0
 	  text_color_set(DW_COLOR_DEBUG);
 	  dw_printf ("Generating prefilter %.0f to %.0f Hz.\n", f1, f2);
@@ -399,121 +446,67 @@ void demod_afsk_init (int samples_per_sec, int baud, int mark_freq,
 	  f1 = f1 / (float)samples_per_sec;
 	  f2 = f2 / (float)samples_per_sec;
 	  
-	  gen_bandpass (f1, f2, D->pre_filter, D->pre_filter_size, D->pre_window);
+	  gen_bandpass (f1, f2, D->pre_filter, D->pre_filter_taps, D->pre_window);
 	}
 
-/*
- * Filters for detecting mark and space tones.
- */
-
-#if DEBUG1
-	  text_color_set(DW_COLOR_DEBUG);
-	  dw_printf ("%s:  \n", __FILE__);
-	  dw_printf ("%d baud, %d samples_per_sec\n", baud, samples_per_sec);
-	  dw_printf ("AFSK %d & %d Hz\n", mark_freq, space_freq);
-	  dw_printf ("spll_step_per_sample = %d = 0x%08x\n", D->pll_step_per_sample, D->pll_step_per_sample);
-	  dw_printf ("D->ms_filter_size = %d = 0x%08x\n", D->ms_filter_size, D->ms_filter_size);
-	  dw_printf ("\n");
-	  dw_printf ("Mark\n");
-	  dw_printf ("   j     shape   M sin   M cos \n");
-#endif
-
-
-	  gen_ms (mark_freq, samples_per_sec, D->m_sin_table, D->m_cos_table, D->ms_filter_size, D->ms_window);
-
-#if DEBUG1
-	  text_color_set(DW_COLOR_DEBUG);
-
-	  dw_printf ("Space\n");
-	  dw_printf ("   j     shape   S sin   S cos\n");
-#endif
-
-	  gen_ms (space_freq, samples_per_sec, D->s_sin_table, D->s_cos_table, D->ms_filter_size, D->ms_window);
-
 /*
  * Now the lowpass filter.
- * I thought we'd want a cutoff of about 0.5 the baud rate 
- * but it turns out about 1.1x is better.  Still investigating...
+ * In version 1.7 a Root Raised Cosine filter is added as an alternative
+ * to the generic low pass filter.
+ * In both cases, lp_filter and lp_filter_taps are used but the 
+ * contents will be generated differently.  Later code does not care.
  */
+	if (D->u.afsk.use_rrc) {
 
-	if (D->lpf_use_fir) {
-	  float fc;
-	  fc = baud * D->lpf_baud / (float)samples_per_sec;
-	  D->lp_filter_delay = gen_lowpass (fc, D->lp_filter, D->lp_filter_size, D->lp_window, D->lp_delay_fract);
+	  assert (D->u.afsk.rrc_width_sym >= 1 && D->u.afsk.rrc_width_sym <= 16);
+	  assert (D->u.afsk.rrc_rolloff >= 0. && D->u.afsk.rrc_rolloff <= 1.);
+
+	  D->lp_filter_taps =  ((int) (D->u.afsk.rrc_width_sym * (float)samples_per_sec / baud)) | 1;  // odd works better
+
+	  TUNE("TUNE_LP_FILTER_TAPS", D->lp_filter_taps, "lp_filter_taps (RRC)", "%d")
+
+	  if (D->lp_filter_taps > MAX_FILTER_SIZE) {
+	    text_color_set(DW_COLOR_ERROR);
+	    dw_printf ("Calculated RRC low pass filter size of %d is too large.\n", D->lp_filter_taps);
+	    dw_printf ("Decrease the audio sample rate or increase the decimation factor or\n");
+	    dw_printf ("recompile the application with MAX_FILTER_SIZE larger than %d.\n", MAX_FILTER_SIZE);
+	    D->lp_filter_taps = (MAX_FILTER_SIZE - 1) | 1;
+	  }
+
+	  assert (D->lp_filter_taps > 8 && D->lp_filter_taps <= MAX_FILTER_SIZE);
+	  (void)gen_rrc_lowpass (D->lp_filter, D->lp_filter_taps, D->u.afsk.rrc_rolloff, (float)samples_per_sec / baud);
 	}
 	else {
-	  // D->lp_filter_delay = 
-	  // Only needed for looking back and I don't expect to use IIR in that case.
+	  D->lp_filter_taps = (int) round( D->lp_filter_width_sym * (float)samples_per_sec / (float)baud );
+
+	  TUNE("TUNE_LP_FILTER_TAPS", D->lp_filter_taps, "lp_filter_taps (FIR)", "%d")
+
+	  if (D->lp_filter_taps > MAX_FILTER_SIZE) {
+	    text_color_set (DW_COLOR_ERROR);
+	    dw_printf ("Calculated FIR low pass filter size of %d is too large.\n", D->lp_filter_taps);
+	    dw_printf ("Decrease the audio sample rate or increase the decimation factor or\n");
+	    dw_printf ("recompile the application with MAX_FILTER_SIZE larger than %d.\n", MAX_FILTER_SIZE);
+	    D->lp_filter_taps = (MAX_FILTER_SIZE - 1) | 1;
+	  }
+
+	  assert (D->lp_filter_taps > 8 && D->lp_filter_taps <= MAX_FILTER_SIZE);
+
+	  float fc = baud * D->lpf_baud / (float)samples_per_sec;
+	  gen_lowpass (fc, D->lp_filter, D->lp_filter_taps, D->lp_window);
 	}
 
+
 /*
- * A non-whole number of cycles results in a DC bias. 
- * Let's see if it helps to take it out.
- * Actually makes things worse:  20 fewer decoded.
- * Might want to try again after EXPERIMENTC.
+ * Starting with version 1.2
+ * try using multiple slicing points instead of the traditional AGC.
  */
-
-#if 0
-#ifndef AVOID_FLOATING_POINT
-
-failed experiment
-
-	dc_bias = 0;
-        for (j=0; j<D->ms_filter_size; j++) {
-	  dc_bias += D->m_sin_table[j];
-	}
-        for (j=0; j<D->ms_filter_size; j++) {
-	  D->m_sin_table[j] -= dc_bias / D->ms_filter_size;
-	}
-
-	dc_bias = 0;
-        for (j=0; j<D->ms_filter_size; j++) {
-	  dc_bias += D->m_cos_table[j];
-	}
-        for (j=0; j<D->ms_filter_size; j++) {
-	  D->m_cos_table[j] -= dc_bias / D->ms_filter_size;
-	}
-
-
-	dc_bias = 0;
-        for (j=0; j<D->ms_filter_size; j++) {
-	  dc_bias += D->s_sin_table[j];
-	}
-        for (j=0; j<D->ms_filter_size; j++) {
-	  D->s_sin_table[j] -= dc_bias / D->ms_filter_size;
-	}
-
-	dc_bias = 0;
-        for (j=0; j<D->ms_filter_size; j++) {
-	  dc_bias += D->s_cos_table[j];
-	}
-        for (j=0; j<D->ms_filter_size; j++) {
-	  D->s_cos_table[j] -= dc_bias / D->ms_filter_size;
-	}
-
-#endif
-#endif
-
-/*
- * In version 1.2 we try another experiment.
- * Try using multiple slicing points instead of the traditional AGC.
- */
-
 	space_gain[0] = MIN_G;
 	float step = powf(10.0, log10f(MAX_G/MIN_G) / (MAX_SUBCHANS-1));
 	for (j=1; j<MAX_SUBCHANS; j++) {
 	  space_gain[j] = space_gain[j-1] * step;
 	}
 
-#if 0
-	text_color_set(DW_COLOR_DEBUG);
-	for (j=0; j<MAX_SUBCHANS; j++) {
-	  float db = 20.0 * log10f(space_gain[j]);
-	  dw_printf ("G = %.3f, %+.1f dB\n", space_gain[j], db);
-	}
-#endif
-
-}  /* fsk_gen_filter */
+}  /* demod_afsk_init */
 
 
 
@@ -531,16 +524,10 @@ failed experiment
  *
  * Returns:	None 
  *
- * Descripion:	We start off with two bandpass filters tuned to
- *		the given frequencies.  In the case of VHF packet
- *		radio, this would be 1200 and 2200 Hz.
+ * Descripion:	First demodulate the AFSK signal.
  *
- *		The bandpass filter amplitudes are compared to 
- *		obtain the demodulated signal.
- *
- *		We also have a digital phase locked loop (PLL)
- *		to recover the clock and pick out data bits at
- *		the proper rate.
+ *		A digital phase locked loop (PLL) recovers the symbol
+ *		clock and picks out data bits at the proper rate.
  *
  *		For each recovered data bit, we call:
  *
@@ -552,145 +539,21 @@ failed experiment
  *		of the function to be called for each bit recovered
  *		from the demodulator.  For now, it's simply hard-coded.
  *
+ * Evolution:	The simple version works less well when there is a substantial difference
+ *		in amplitude of the two tones.  e.g. When de-emphasis cuts the
+ *		higher tone down to about half the amplitude. We overcome that
+ *		by boosting the space amplitude by varying amounts before slicing.
+ *
+ *		In version 1.7 an entirely different approach is added, an FM
+ *		discriminator which produces a result proportional to the 
+ *		frequency.
+ *
  *--------------------------------------------------------------------*/
-
-inline static void nudge_pll (int chan, int subchan, int slice, int demod_data, struct demodulator_state_s *D);
-
-__attribute__((hot))
-void demod_afsk_process_sample (int chan, int subchan, int sam, struct demodulator_state_s *D)
-{
-	float fsam;
-	//float abs_fsam;
-	float m_sum1, m_sum2, s_sum1, s_sum2;
-	float m_amp, s_amp;
-	float m_norm, s_norm;
-	float demod_out;
-#if DEBUG4
-	static FILE *demod_log_fp = NULL;
-	static int seq = 0;			/* for log file name */
-#endif
-
-
-	//int j;
-	int demod_data;
-
-
-	assert (chan >= 0 && chan < MAX_CHANS);
-	assert (subchan >= 0 && subchan < MAX_SUBCHANS);
-
-/* 
- * Filters use last 'filter_size' samples.
- *
- * First push the older samples down. 
- *
- * Finally, put the most recent at the beginning.
- *
- * Future project?  Can we do better than shifting each time?
- */
-
-	/* Scale to nice number, TODO: range -1.0 to +1.0, not 2. */
-
-	fsam = sam / 16384.0f;
-
-	//abs_fsam = fsam >= 0.0f ? fsam : -fsam;
-
-
-/*
- * Optional bandpass filter before the mark/space discriminator.
- */
-
-// FIXME:  calculate how much we really need.
-
-	int extra = 0;
-
-	if (D->use_prefilter) {
-	  float cleaner;
-
-	  push_sample (fsam, D->raw_cb, D->pre_filter_size);
-	  cleaner = convolve (D->raw_cb, D->pre_filter, D->pre_filter_size);
-	  push_sample (cleaner, D->ms_in_cb, D->ms_filter_size + extra);
-	}
-	else {
-	  push_sample (fsam, D->ms_in_cb, D->ms_filter_size + extra);
-	}
-
-/*
- * Next we have bandpass filters for the mark and space tones.
- */
-
-/*
- * find amplitude of "Mark" tone.
- */
-	  m_sum1 = convolve (D->ms_in_cb, D->m_sin_table, D->ms_filter_size);
-	  m_sum2 = convolve (D->ms_in_cb, D->m_cos_table, D->ms_filter_size);
-
-	  m_amp = sqrtf(m_sum1 * m_sum1 + m_sum2 * m_sum2);
-
-/*
- * Find amplitude of "Space" tone.
- */
-	  s_sum1 = convolve (D->ms_in_cb, D->s_sin_table, D->ms_filter_size);
-	  s_sum2 = convolve (D->ms_in_cb, D->s_cos_table, D->ms_filter_size);
-
-	  s_amp = sqrtf(s_sum1 * s_sum1 + s_sum2 * s_sum2);
-
-
-/* 
- * Apply some low pass filtering BEFORE the AGC to remove
- * overshoot, ringing, and other bad stuff.
- *
- * A simple IIR filter is faster but FIR produces better results.
- *
- * It is a balancing act between removing high frequency components
- * from the tone dectection while letting the data thru.
- */
-
-	if (D->lpf_use_fir) {
-
-	  push_sample (m_amp, D->m_amp_cb, D->lp_filter_size);
-	  m_amp = convolve (D->m_amp_cb, D->lp_filter, D->lp_filter_size);
-
-	  push_sample (s_amp, D->s_amp_cb, D->lp_filter_size);
-	  s_amp = convolve (D->s_amp_cb, D->lp_filter, D->lp_filter_size);
-	}
-	else {
-	
-	  /* Original, but faster, IIR. */
-
-	  m_amp = D->lpf_iir * m_amp + (1.0f - D->lpf_iir) * D->m_amp_prev;
-	  D->m_amp_prev = m_amp;
-
-	  s_amp = D->lpf_iir * s_amp + (1.0f - D->lpf_iir) * D->s_amp_prev;
-	  D->s_amp_prev = s_amp;
-	}
-
-/*
- * Version 1.2: Try new approach to capturing the amplitude for display.
- * This is same as the AGC above without the normalization step.
- * We want decay to be substantially slower to get a longer
- * range idea of the received audio.
- */
-
-	if (m_amp >= D->alevel_mark_peak) {
-	  D->alevel_mark_peak = m_amp * D->quick_attack + D->alevel_mark_peak * (1.0f - D->quick_attack);
-	}
-	else {
-	  D->alevel_mark_peak = m_amp * D->sluggish_decay + D->alevel_mark_peak * (1.0f - D->sluggish_decay);
-	}
-
-	if (s_amp >= D->alevel_space_peak) {
-	  D->alevel_space_peak = s_amp * D->quick_attack + D->alevel_space_peak * (1.0f - D->quick_attack);
-	}
-	else {
-	  D->alevel_space_peak = s_amp * D->sluggish_decay + D->alevel_space_peak * (1.0f - D->sluggish_decay);
-	}
-
-
 /* 
  * Which tone is stronger?
  *
  * In an ideal world, simply compare.  In my first naive attempt, that
- * worked perfectly with perfect signals. In the real world, we don't
+ * worked well with perfect signals. In the real world, we don't
  * have too many perfect signals.
  *
  * Here is an excellent explanation:
@@ -724,51 +587,207 @@ void demod_afsk_process_sample (int chan, int subchan, int sam, struct demodulat
  * 
  * This is similar to my observations of local signals, from the speaker.
  * The amplitude ratio varies from 1.48 to 3.41 with a median of 2.70. 
- *
- * Rather than only two filters, let's try slicing the data in more places. 
  */
 
-	/* Fast attack and slow decay. */
-	/* Numbers were obtained by trial and error from actual */
-	/* recorded less-than-optimal signals. */
 
-	/* See fsk_demod_agc.h for more information. */
 
-	m_norm = agc (m_amp, D->agc_fast_attack, D->agc_slow_decay, &(D->m_peak), &(D->m_valley));
-	s_norm = agc (s_amp, D->agc_fast_attack, D->agc_slow_decay, &(D->s_peak), &(D->s_valley));
+__attribute__((hot))
+void demod_afsk_process_sample (int chan, int subchan, int sam, struct demodulator_state_s *D)
+{
+#if DEBUG4
+	static FILE *demod_log_fp = NULL;
+	static int seq = 0;			/* for log file name */
+#endif
 
-	if (D->num_slicers <= 1) {
+	assert (chan >= 0 && chan < MAX_CHANS);
+	assert (subchan >= 0 && subchan < MAX_SUBCHANS);
 
-	  /* Normal case of one demodulator to one HDLC decoder. */
-	  /* Demodulator output is difference between response from two filters. */
-	  /* AGC should generally keep this around -1 to +1 range. */
+/* 
+ * Filters use last 'filter_taps' samples.
+ *
+ * First push the older samples down. 
+ *
+ * Finally, put the most recent at the beginning.
+ *
+ * Future project?  Can we do better than shifting each time?
+ */
 
-	  demod_out = m_norm - s_norm;
+	/* Scale to nice number. */
 
-	  /* Try adding some Hysteresis. */
-	  /* (Not to be confused with Hysteria.) */
+	float fsam = (float)sam / 16384.0f;
+
+	switch (D->profile) {
+
+	  case 'E':
+	  default:
+	  case 'A': {
+				/* ========== New in Version 1.7 ========== */
+
+				//	Cleaner & simpler than earlier 'A' thru 'E'
+
+	    if (D->use_prefilter) {
+	      push_sample (fsam, D->raw_cb, D->pre_filter_taps);
+	      fsam = convolve (D->raw_cb, D->pre_filter, D->pre_filter_taps);
+	    }
+
+	    push_sample (fsam * fcos256(D->u.afsk.m_osc_phase), D->u.afsk.m_I_raw, D->lp_filter_taps);
+	    push_sample (fsam * fsin256(D->u.afsk.m_osc_phase), D->u.afsk.m_Q_raw, D->lp_filter_taps);
+	    D->u.afsk.m_osc_phase += D->u.afsk.m_osc_delta;
+
+	    push_sample (fsam * fcos256(D->u.afsk.s_osc_phase), D->u.afsk.s_I_raw, D->lp_filter_taps);
+	    push_sample (fsam * fsin256(D->u.afsk.s_osc_phase), D->u.afsk.s_Q_raw, D->lp_filter_taps);
+	    D->u.afsk.s_osc_phase += D->u.afsk.s_osc_delta;
+
+	    float m_I = convolve (D->u.afsk.m_I_raw, D->lp_filter, D->lp_filter_taps);
+	    float m_Q = convolve (D->u.afsk.m_Q_raw, D->lp_filter, D->lp_filter_taps);
+	    float m_amp = fast_hypot(m_I, m_Q);
+
+	    float s_I = convolve (D->u.afsk.s_I_raw, D->lp_filter, D->lp_filter_taps);
+	    float s_Q = convolve (D->u.afsk.s_Q_raw, D->lp_filter, D->lp_filter_taps);
+	    float s_amp = fast_hypot(s_I, s_Q);
+
+/*
+ * Capture the mark and space peak amplitudes for display.
+ * It uses fast attack and slow decay to get an idea of the
+ * overall amplitude.
+ */
+	    if (m_amp >= D->alevel_mark_peak) {
+	      D->alevel_mark_peak = m_amp * D->quick_attack + D->alevel_mark_peak * (1.0f - D->quick_attack);
+	    }
+	    else {
+	      D->alevel_mark_peak = m_amp * D->sluggish_decay + D->alevel_mark_peak * (1.0f - D->sluggish_decay);
+	    }
+
+	    if (s_amp >= D->alevel_space_peak) {
+	      D->alevel_space_peak = s_amp * D->quick_attack + D->alevel_space_peak * (1.0f - D->quick_attack);
+	    }
+	      else {
+	      D->alevel_space_peak = s_amp * D->sluggish_decay + D->alevel_space_peak * (1.0f - D->sluggish_decay);
+	    }
+
+	    if (D->num_slicers <= 1) {
+
+	      // Which tone is stonger?  That's simple with an ideal signal.
+	      // However, we don't see too many ideal signals.
+	      // Due to mismatching pre-emphasis and de-emphasis, the two
+	      // tones will often have greatly different amplitudes so we use
+	      // automatic gain control (AGC) to scale each to the same range
+	      // before comparing.
+	      // This is probably over complicated and could be combined with
+	      // the signal amplitude measurement, above.
+	      // It works so let's move along to other topics.
+
+	      float m_norm = agc (m_amp, D->agc_fast_attack, D->agc_slow_decay, &(D->m_peak), &(D->m_valley));
+	      float s_norm = agc (s_amp, D->agc_fast_attack, D->agc_slow_decay, &(D->s_peak), &(D->s_valley));
+
+	      // The normalized values should be around -0.5 to +0.5 so the difference
+	      // should work out to be around -1 to +1.
+	      // This is important because nudge_pll uses the demod_out amplitude to assign
+	      // a quality or confidence score to the symbol.
+
+	      float demod_out = m_norm - s_norm;
+
+	      // Tested and it looks good.  Range of about -1 to +1.
+	      //printf ("JWL DEBUG demod A with agc = %6.2f\n", demod_out);
+
+	      nudge_pll (chan, subchan, 0, demod_out, D, 1.0);
+
+	    }
+	    else {
+	      // Multiple slice case.
+	      // Rather than trying to find the best threshold location, use multiple 
+	      // slicer thresholds in parallel.
+	      // The best slicing point will vary from packet to packet but should
+	      // remain abount the same or a given packet.
+
+	      // We are not performing the AGC step here but still want the envelope
+	      // for caluculating the confidence level (or quality) of the sample.
+
+	      (void) agc (m_amp, D->agc_fast_attack, D->agc_slow_decay, &(D->m_peak), &(D->m_valley));
+	      (void) agc (s_amp, D->agc_fast_attack, D->agc_slow_decay, &(D->s_peak), &(D->s_valley));
+
+	      for (int slice=0; slice<D->num_slicers; slice++) {
+	        float demod_out = m_amp - s_amp * space_gain[slice];
+	        float amp = 0.5f * (D->m_peak - D->m_valley + (D->s_peak - D->s_valley) * space_gain[slice]);
+	        if (amp < 0.0000001f) amp = 1;	// avoid divide by zero with no signal.
+
+	        // Tested and it looks good.  Range of about -1 to +1 relative to amp.
+		// Biased one way or the other depending on the space gain.
+	        //printf ("JWL DEBUG demod A with slicer %d: %6.2f / %6.2f = %6.2f\n", slice, demod_out, amp, demod_out/amp);
+
+	        nudge_pll (chan, subchan, slice, demod_out, D, amp);
+	      }
+	    }
+	  }
+	  break;
+
+	case 'D':
+	case 'B': {
+				/* ========== Version 1.7 Experiment ========== */
+
+				// New - Convert frequency to a value proportional to frequency.
+
+	  if (D->use_prefilter) {
+	    push_sample (fsam, D->raw_cb, D->pre_filter_taps);
+	    fsam = convolve (D->raw_cb, D->pre_filter, D->pre_filter_taps);
+	  }
+
+	  push_sample (fsam * fcos256(D->u.afsk.c_osc_phase), D->u.afsk.c_I_raw, D->lp_filter_taps);
+	  push_sample (fsam * fsin256(D->u.afsk.c_osc_phase), D->u.afsk.c_Q_raw, D->lp_filter_taps);
+	  D->u.afsk.c_osc_phase += D->u.afsk.c_osc_delta;
+
+	  float c_I = convolve (D->u.afsk.c_I_raw, D->lp_filter, D->lp_filter_taps);
+	  float c_Q = convolve (D->u.afsk.c_Q_raw, D->lp_filter, D->lp_filter_taps);
+
+	  float phase = atan2f (c_Q, c_I);
+	  float rate = phase - D->u.afsk.prev_phase; 
+	  if (rate > M_PI) rate -= 2 * M_PI;
+	  else if (rate < -M_PI) rate += 2 * M_PI;
+	  D->u.afsk.prev_phase = phase;
+
+	  // Rate is radians per audio sample interval or something like that.
+	  // Scale scale that into -1 to +1 for expected tones.
+
+	  float norm_rate = rate * D->u.afsk.normalize_rpsam;
+
+	  // We really don't have mark and space amplitudes available in this case.
+
+	  if (D->num_slicers <= 1) {
+
+	    float demod_out = norm_rate;
+	    // Tested and it looks good.  Range roughly -1 to +1.
+	    //printf ("JWL DEBUG demod B single = %6.2f\n", demod_out);
+
+	    nudge_pll (chan, subchan, 0, demod_out, D, 1.0);
 
-	  if (demod_out > D->hysteresis) {
-	    demod_data = 1;
 	  }
-	  else if (demod_out < (- (D->hysteresis))) {
-	    demod_data = 0;
-	  } 
 	  else {
-	    demod_data = D->slicer[subchan].prev_demod_data;
+
+	    // This would be useful for HF SSB where a tuning error
+	    // would shift the frequency.  Multiple slicing points would
+	    // then compensate for differences in transmit/receive frequencies.
+	    // 
+	    // Where should we set the thresholds?
+	    // I'm thinking something like:
+	    // 	-.5	-.375	-.25	-.125	0	.125	.25	.375	.5
+	    //
+	    // Assuming a 300 Hz shift, this would put slicing thresholds up
+	    // to +-75 Hz from the center.
+
+	    for (int slice=0; slice<D->num_slicers; slice++) {
+
+	      float offset = -0.5 + slice * (1. / (D->num_slicers - 1));
+	      float demod_out = norm_rate + offset;
+
+	      //printf ("JWL DEBUG demod B slice %d, offset = %6.3f, demod_out = %6.2f\n", slice, offset, demod_out);
+
+	      nudge_pll (chan, subchan, slice, demod_out, D, 1.0);
+	    }
 	  }
-	  nudge_pll (chan, subchan, 0, demod_data, D);
-	}
-	else {
-	  int slice;
-
-	  for (slice=0; slice<D->num_slicers; slice++) {
-	    demod_data = m_amp > s_amp * space_gain[slice];
-	    nudge_pll (chan, subchan, slice, demod_data, D);
 	  }
+	  break;
 	}
-
-
+		
 #if DEBUG4
 
 	if (chan == 0) {
@@ -805,9 +824,6 @@ void demod_afsk_process_sample (int chan, int subchan, int sam, struct demodulat
 } /* end demod_afsk_process_sample */
 
 
-__attribute__((hot))
-inline static void nudge_pll (int chan, int subchan, int slice, int demod_data, struct demodulator_state_s *D)
-{
 
 /*
  * Finally, a PLL is used to sample near the centers of the data bits.
@@ -839,6 +855,9 @@ inline static void nudge_pll (int chan, int subchan, int slice, int demod_data,
  * because this happens for each transition from the demodulator.
  */
 
+__attribute__((hot))
+static void nudge_pll (int chan, int subchan, int slice, float demod_out, struct demodulator_state_s *D, float amplitude)
+{
 	D->slicer[slice].prev_d_c_pll = D->slicer[slice].data_clock_pll;
 
 	// Perform the add as unsigned to avoid signed overflow error.
@@ -850,12 +869,19 @@ inline static void nudge_pll (int chan, int subchan, int slice, int demod_data,
 	if (D->slicer[slice].data_clock_pll < 0 && D->slicer[slice].prev_d_c_pll > 0) {
 
 	  /* Overflow - this is where we sample. */
-	  hdlc_rec_bit (chan, subchan, slice, demod_data, 0, -1);
+	  // Assign it a confidence level or quality, 0 to 100, based on the amplitude.
+	  // Those very close to 0 are suspect.  We'll get back to this later.
+
+	  int quality = fabsf(demod_out) * 100.0f / amplitude;
+	  if (quality > 100) quality = 100;
+
+	  hdlc_rec_bit (chan, subchan, slice, demod_out > 0, 0, quality);
 	  pll_dcd_each_symbol2 (D, chan, subchan, slice);
 	}
 
 	// Transitions nudge the DPLL phase toward the incoming signal.
 
+	int demod_data = demod_out > 0;
         if (demod_data != D->slicer[slice].prev_demod_data) {
 
 	  pll_dcd_signal_transition2 (D, slice, D->slicer[slice].data_clock_pll);
diff --git a/src/demod_psk.c b/src/demod_psk.c
index f01ee21..b953add 100644
--- a/src/demod_psk.c
+++ b/src/demod_psk.c
@@ -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.
diff --git a/src/dsp.c b/src/dsp.c
index 6ba7094..4a5f4a8 100644
--- a/src/dsp.c
+++ b/src/dsp.c
@@ -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 */
diff --git a/src/dsp.h b/src/dsp.h
index 5d5b882..e0dbd24 100644
--- a/src/dsp.h
+++ b/src/dsp.h
@@ -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);
diff --git a/src/fsk_demod_agc.h b/src/fsk_demod_agc.h
deleted file mode 100644
index 95c8079..0000000
--- a/src/fsk_demod_agc.h
+++ /dev/null
@@ -1,2 +0,0 @@
-#define TUNE_MS_FILTER_SIZE 140 
-#define TUNE_PRE_BAUD    1.080 
diff --git a/src/fsk_demod_state.h b/src/fsk_demod_state.h
index f7b5650..33f7901 100644
--- a/src/fsk_demod_state.h
+++ b/src/fsk_demod_state.h
@@ -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.				//
diff --git a/test/scripts/check-modem1200 b/test/scripts/check-modem1200
index 2b97587..0c79af9 100755
--- a/test/scripts/check-modem1200
+++ b/test/scripts/check-modem1200
@@ -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
\ No newline at end of file
diff --git a/test/scripts/check-modem300 b/test/scripts/check-modem300
index da37dd2..6d6e643 100755
--- a/test/scripts/check-modem300
+++ b/test/scripts/check-modem300
@@ -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