mirror of https://github.com/wb2osz/direwolf.git
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:
parent
fdf660a7f1
commit
9922f176b2
18
src/atest.c
18
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].space_freq = DEFAULT_SPACE_FREQ;
|
||||||
my_audio_config.achan[channel].baud = DEFAULT_BAUD;
|
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].num_freq = 1;
|
||||||
my_audio_config.achan[channel].offset = 0;
|
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, */
|
/* 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. */
|
/* 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].modem_type = MODEM_AFSK;
|
||||||
my_audio_config.achan[0].mark_freq = 1615;
|
my_audio_config.achan[0].mark_freq = 1615;
|
||||||
my_audio_config.achan[0].space_freq = 1785;
|
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].modem_type = MODEM_AFSK;
|
||||||
my_audio_config.achan[0].mark_freq = 1600;
|
my_audio_config.achan[0].mark_freq = 1600;
|
||||||
my_audio_config.achan[0].space_freq = 1800;
|
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].modem_type = MODEM_AFSK;
|
||||||
my_audio_config.achan[0].mark_freq = DEFAULT_MARK_FREQ;
|
my_audio_config.achan[0].mark_freq = DEFAULT_MARK_FREQ;
|
||||||
my_audio_config.achan[0].space_freq = DEFAULT_SPACE_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;
|
my_audio_config.achan[0].space_freq = 0;
|
||||||
strlcpy (my_audio_config.achan[0].profiles, "", sizeof(my_audio_config.achan[0].profiles));
|
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].modem_type = MODEM_AIS;
|
||||||
my_audio_config.achan[0].baud = 9600;
|
my_audio_config.achan[0].baud = 9600;
|
||||||
my_audio_config.achan[0].mark_freq = 0;
|
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.
|
// 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].mark_freq = 2083; // Actually 2083.3 - logic 1.
|
||||||
my_audio_config.achan[0].space_freq = 1563; // Actually 1562.5 - logic 0.
|
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 {
|
else {
|
||||||
my_audio_config.achan[0].modem_type = MODEM_SCRAMBLE;
|
my_audio_config.achan[0].modem_type = MODEM_SCRAMBLE;
|
||||||
|
|
|
@ -1337,7 +1337,7 @@ void config_init (char *fname, struct audio_s *p_audio_config,
|
||||||
// Will make more precise in afsk demod init.
|
// 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].mark_freq = 2083; // Actually 2083.3 - logic 1.
|
||||||
p_audio_config->achan[channel].space_freq = 1563; // Actually 1562.5 - logic 0.
|
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 {
|
else {
|
||||||
p_audio_config->achan[channel].modem_type = MODEM_SCRAMBLE;
|
p_audio_config->achan[channel].modem_type = MODEM_SCRAMBLE;
|
||||||
|
|
69
src/demod.c
69
src/demod.c
|
@ -199,45 +199,56 @@ int demod_init (struct audio_s *pa)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* 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) {
|
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) {
|
if (have_plus != -1) have_plus = 1; // Add as default for version 1.2
|
||||||
|
|
||||||
/* 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 not explicitly turned off.
|
// 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.
|
* Put it back together again.
|
||||||
*/
|
*/
|
||||||
|
assert (num_letters == (int)(strlen(just_letters)));
|
||||||
|
|
||||||
/* At this point, have_plus can have 3 values: */
|
/* At this point, have_plus can have 3 values: */
|
||||||
/* 1 = turned on, either explicitly or by applied default */
|
/* 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) {
|
if (save_audio_config_p->achan[chan].decimate == 0) {
|
||||||
save_audio_config_p->achan[chan].decimate = 1;
|
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;
|
save_audio_config_p->achan[chan].decimate = 3;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
//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. */
|
/* Version 1.2: Experiment with different slicing levels. */
|
||||||
|
|
||||||
|
|
916
src/demod_afsk.c
916
src/demod_afsk.c
File diff suppressed because it is too large
Load Diff
|
@ -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;
|
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.
|
* No point in having multiple numbers for signal level.
|
||||||
|
|
134
src/dsp.c
134
src/dsp.c
|
@ -43,7 +43,6 @@
|
||||||
#include "dsp.h"
|
#include "dsp.h"
|
||||||
|
|
||||||
|
|
||||||
//#include "fsk_demod_agc.h" /* for M_FILTER_SIZE, etc. */
|
|
||||||
|
|
||||||
#define MIN(a,b) ((a)<(b)?(a):(b))
|
#define MIN(a,b) ((a)<(b)?(a):(b))
|
||||||
#define MAX(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;
|
int j;
|
||||||
float G;
|
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;
|
lp_filter[j] = lp_filter[j] / G;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
// 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);
|
|
||||||
|
|
||||||
} /* end gen_lowpass */
|
} /* 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 */
|
} /* 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 */
|
/* end dsp.c */
|
||||||
|
|
|
@ -5,9 +5,13 @@
|
||||||
|
|
||||||
float window (bp_window_t type, int size, int j);
|
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_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);
|
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);
|
||||||
|
|
|
@ -1,2 +0,0 @@
|
||||||
#define TUNE_MS_FILTER_SIZE 140
|
|
||||||
#define TUNE_PRE_BAUD 1.080
|
|
|
@ -20,6 +20,31 @@ typedef enum bp_window_e { BP_WINDOW_TRUNCATED,
|
||||||
BP_WINDOW_BLACKMAN,
|
BP_WINDOW_BLACKMAN,
|
||||||
BP_WINDOW_FLATTOP } bp_window_t;
|
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
|
struct demodulator_state_s
|
||||||
{
|
{
|
||||||
|
@ -39,30 +64,12 @@ struct demodulator_state_s
|
||||||
// Data is sampled when it overflows.
|
// 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.
|
* Window type for the various filters.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
bp_window_t pre_window;
|
|
||||||
bp_window_t ms_window;
|
|
||||||
bp_window_t lp_window;
|
bp_window_t lp_window;
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Alternate Low pass filters.
|
* Alternate Low pass filters.
|
||||||
* First is arbitrary number for quick IIR.
|
* First is arbitrary number for quick IIR.
|
||||||
|
@ -78,16 +85,13 @@ struct demodulator_state_s
|
||||||
/* In practice, it turned out a little larger */
|
/* In practice, it turned out a little larger */
|
||||||
/* for profiles B, C, D. */
|
/* 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. */
|
#define lp_filter_len_bits lp_filter_width_sym // FIXME: temp hack
|
||||||
/* Previously it was always the same as the M/S */
|
|
||||||
/* filters but in version 1.2 it's now independent. */
|
|
||||||
|
|
||||||
int lp_filter_delay; /* Number of samples that the low pass filter */
|
int lp_filter_taps; /* Size of Low Pass filter, in audio samples. */
|
||||||
/* delays the signal. */
|
|
||||||
|
|
||||||
/* New in 1.6. */
|
#define lp_filter_size lp_filter_taps // FIXME: temp hack
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -111,6 +115,7 @@ struct demodulator_state_s
|
||||||
/*
|
/*
|
||||||
* Phase Locked Loop (PLL) inertia.
|
* Phase Locked Loop (PLL) inertia.
|
||||||
* Larger number means less influence by signal transitions.
|
* 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_locked_inertia;
|
||||||
float pll_searching_inertia;
|
float pll_searching_inertia;
|
||||||
|
@ -129,23 +134,17 @@ struct demodulator_state_s
|
||||||
/* lower = min(1600,1800) - 0.5 * 300 = 1450 */
|
/* lower = min(1600,1800) - 0.5 * 300 = 1450 */
|
||||||
/* upper = max(1600,1800) + 0.5 * 300 = 1950 */
|
/* 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)));
|
float pre_filter[MAX_FILTER_SIZE] __attribute__((aligned(16)));
|
||||||
|
|
||||||
|
float raw_cb[MAX_FILTER_SIZE] __attribute__((aligned(16))); // audio in, need better name.
|
||||||
/*
|
|
||||||
* 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)));
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The rest are continuously updated.
|
* The rest are continuously updated.
|
||||||
|
@ -154,11 +153,6 @@ struct demodulator_state_s
|
||||||
unsigned int lo_phase; /* Local oscillator for PSK. */
|
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.
|
* Use half of the AGC code to get a measure of input audio amplitude.
|
||||||
* These use "quick" attack and "sluggish" decay while the
|
* These use "quick" attack and "sluggish" decay while the
|
||||||
|
@ -170,24 +164,14 @@ struct demodulator_state_s
|
||||||
float alevel_mark_peak;
|
float alevel_mark_peak;
|
||||||
float alevel_space_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,
|
* Outputs from the mark and space amplitude detection,
|
||||||
* used as inputs to the FIR lowpass filters.
|
* used as inputs to the FIR lowpass filters.
|
||||||
* Kernel for the 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 lp_filter[MAX_FILTER_SIZE] __attribute__((aligned(16)));
|
||||||
|
|
||||||
|
|
||||||
float m_peak, s_peak;
|
float m_peak, s_peak;
|
||||||
float m_valley, s_valley;
|
float m_valley, s_valley;
|
||||||
float m_amp_prev, s_amp_prev;
|
float m_amp_prev, s_amp_prev;
|
||||||
|
@ -266,6 +250,113 @@ struct demodulator_state_s
|
||||||
|
|
||||||
union {
|
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. //
|
// PSK only. //
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
@CUSTOM_SHELL_SHABANG@
|
@CUSTOM_SHELL_SHABANG@
|
||||||
|
|
||||||
@GEN_PACKETS_BIN@ -n 100 -o test12.wav
|
@GEN_PACKETS_BIN@ -n 100 -o test12.wav
|
||||||
@ATEST_BIN@ -F0 -PE -L64 -G72 test12.wav
|
@ATEST_BIN@ -F0 -PA -L66 -G72 test12.wav
|
||||||
@ATEST_BIN@ -F1 -PE -L70 -G75 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
|
|
@ -1,5 +1,7 @@
|
||||||
@CUSTOM_SHELL_SHABANG@
|
@CUSTOM_SHELL_SHABANG@
|
||||||
|
|
||||||
@GEN_PACKETS_BIN@ -B300 -n 100 -o test3.wav
|
@GEN_PACKETS_BIN@ -B300 -n 100 -o test3.wav
|
||||||
@ATEST_BIN@ -B300 -F0 -L68 -G69 test3.wav
|
@ATEST_BIN@ -B300 -PA -F0 -L65 -G71 test3.wav
|
||||||
@ATEST_BIN@ -B300 -F1 -L71 -G75 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
|
||||||
|
|
Loading…
Reference in New Issue