Clean up fix-bits feature. New experimental demodulator.

This commit is contained in:
WB2OSZ 2015-11-29 10:44:30 -05:00
parent 95c22986ec
commit 178375f3ba
28 changed files with 682 additions and 978 deletions

View File

@ -1,6 +1,15 @@
# Revision History # # Revision History #
----------
## Version 1.3 -- Development snapshot H -- November 2015 ##
### New Feature: ###
- New experimental demodulator. More details later.
---------- ----------
## Version 1.3 -- Development snapshot G -- November 2015 ## ## Version 1.3 -- Development snapshot G -- November 2015 ##

View File

@ -238,6 +238,7 @@ strlcat.o : misc/strlcat.c
check : dtest ttest tttexttest pftest tlmtest lltest enctest kisstest check-modem1200 check-modem300 check-modem9600 check : dtest ttest tttexttest pftest tlmtest lltest enctest kisstest check-modem1200 check-modem300 check-modem9600
# Can we encode and decode at popular data rates? # Can we encode and decode at popular data rates?
# Verify that single bit fixup increases the count.
check-modem1200 : gen_packets atest check-modem1200 : gen_packets atest
gen_packets -n 100 -o test1.wav gen_packets -n 100 -o test1.wav
@ -361,14 +362,13 @@ demod_9600.o : tune.h
demod_afsk.o : tune.h demod_afsk.o : tune.h
testagc : atest.c demod.c dsp.c demod_afsk.c demod_9600.c fsk_demod_agc.h \ testagc : atest.c demod.c dsp.c demod_afsk.c demod_9600.o fsk_demod_agc.h \
hdlc_rec.o hdlc_rec2.o multi_modem.o \ hdlc_rec.o hdlc_rec2.o multi_modem.o \
rrbb.o fcs_calc.o ax25_pad.o decode_aprs.o latlong.o symbols.o textcolor.o telemetry.o \ rrbb.o fcs_calc.o ax25_pad.o decode_aprs.o latlong.o symbols.o textcolor.o telemetry.o \
regex.a misc.a \ dwgpsnmea.o dwgps.o serial_port.o tt_text.o regex.a misc.a
rm -f atest.exe rm -f atest.exe
$(CC) $(CFLAGS) -o atest $^ $(CC) $(CFLAGS) -o atest $^
./atest -P E ../02_Track_2.wav | grep "packets decoded in" >atest.out ./atest -P GGG- -F 0 ../02_Track_2.wav | grep "packets decoded in" >atest.out
echo " " > tune.h echo " " > tune.h

View File

@ -1561,7 +1561,7 @@ static void raw_tt_data_to_app (int chan, char *msg)
alevel.mark = -2; alevel.mark = -2;
alevel.space = -2; alevel.space = -2;
dlq_append (DLQ_REC_FRAME, chan, -1, pp, alevel, RETRY_NONE, "tt"); dlq_append (DLQ_REC_FRAME, chan, -1, 0, pp, alevel, RETRY_NONE, "tt");
} }
else { else {
text_color_set(DW_COLOR_ERROR); text_color_set(DW_COLOR_ERROR);

58
atest.c
View File

@ -167,6 +167,10 @@ static void usage (void);
static int decode_only = 0; /* Set to 0 or 1 to decode only one channel. 2 for both. */ static int decode_only = 0; /* Set to 0 or 1 to decode only one channel. 2 for both. */
static int sample_number = -1; /* Sample number from the file. */
/* Incremented only for channel 0. */
/* Use to print timestamp, relative to beginning */
/* of file, when frame was decoded. */
int main (int argc, char *argv[]) int main (int argc, char *argv[])
{ {
@ -495,6 +499,8 @@ int main (int argc, char *argv[])
if (audio_sample >= 256 * 256) if (audio_sample >= 256 * 256)
e_o_f = 1; e_o_f = 1;
if (c == 0) sample_number++;
if (decode_only == 0 && c != 0) continue; if (decode_only == 0 && c != 0) continue;
if (decode_only == 1 && c != 1) continue; if (decode_only == 1 && c != 1) continue;
@ -572,16 +578,16 @@ int audio_get (int a)
void rdq_append (rrbb_t rrbb) void rdq_append (rrbb_t rrbb)
{ {
int chan; int chan, subchan, slice;
alevel_t alevel; alevel_t alevel;
int subchan;
chan = rrbb_get_chan(rrbb); chan = rrbb_get_chan(rrbb);
subchan = rrbb_get_subchan(rrbb); subchan = rrbb_get_subchan(rrbb);
slice = rrbb_get_slice(rrbb);
alevel = rrbb_get_audio_level(rrbb); alevel = rrbb_get_audio_level(rrbb);
hdlc_rec2_try_to_fix_later (rrbb, chan, subchan, alevel); hdlc_rec2_try_to_fix_later (rrbb, chan, subchan, slice, alevel);
rrbb_delete (rrbb); rrbb_delete (rrbb);
} }
@ -590,7 +596,7 @@ void rdq_append (rrbb_t rrbb)
* This is called when we have a good frame. * This is called when we have a good frame.
*/ */
void dlq_append (dlq_type_t type, int chan, int subchan, packet_t pp, alevel_t alevel, retry_t retries, char *spectrum) void dlq_append (dlq_type_t type, int chan, int subchan, int slice, packet_t pp, alevel_t alevel, retry_t retries, char *spectrum)
{ {
char stemp[500]; char stemp[500];
@ -627,6 +633,15 @@ void dlq_append (dlq_type_t type, int chan, int subchan, packet_t pp, alevel_t a
text_color_set(DW_COLOR_DEBUG); text_color_set(DW_COLOR_DEBUG);
dw_printf ("\n"); dw_printf ("\n");
dw_printf("DECODED[%d] ", packets_decoded ); dw_printf("DECODED[%d] ", packets_decoded );
/* Insert time stamp relative to start of file. */
double sec = (double)sample_number / my_audio_config.adev[0].samples_per_sec;
int min = (int)(sec / 60.);
sec -= min * 60;
dw_printf ("%d:%07.4f ", min, sec);
if (h != AX25_SOURCE) { if (h != AX25_SOURCE) {
dw_printf ("Digipeater "); dw_printf ("Digipeater ");
} }
@ -641,27 +656,38 @@ void dlq_append (dlq_type_t type, int chan, int subchan, packet_t pp, alevel_t a
#endif #endif
#if defined(EXPERIMENT_G) || defined(EXPERIMENT_H) //#if defined(EXPERIMENT_G) || defined(EXPERIMENT_H)
int j; // int j;
//
for (j=0; j<MAX_SUBCHANS; j++) { // for (j=0; j<MAX_SUBCHANS; j++) {
if (spectrum[j] == '|') { // if (spectrum[j] == '|') {
count[j]++; // count[j]++;
} // }
} // }
#endif //#endif
// Display non-APRS packets in a different color. // Display non-APRS packets in a different color.
// TODO: display subchannel if appropriate. // Display channel with subchannel/slice if applicable.
if (ax25_is_aprs(pp)) { if (ax25_is_aprs(pp)) {
text_color_set(DW_COLOR_REC); text_color_set(DW_COLOR_REC);
dw_printf ("[%d] ", chan);
} }
else { else {
text_color_set(DW_COLOR_DEBUG); text_color_set(DW_COLOR_DEBUG);
}
if (my_audio_config.achan[chan].num_subchan > 1 && my_audio_config.achan[chan].num_slicers == 1) {
dw_printf ("[%d.%d] ", chan, subchan);
}
else if (my_audio_config.achan[chan].num_subchan == 1 && my_audio_config.achan[chan].num_slicers > 1) {
dw_printf ("[%d.%d] ", chan, slice);
}
else if (my_audio_config.achan[chan].num_subchan > 1 && my_audio_config.achan[chan].num_slicers > 1) {
dw_printf ("[%d.%d.%d] ", chan, subchan, slice);
}
else {
dw_printf ("[%d] ", chan); dw_printf ("[%d] ", chan);
} }
@ -671,7 +697,7 @@ void dlq_append (dlq_type_t type, int chan, int subchan, packet_t pp, alevel_t a
ax25_delete (pp); ax25_delete (pp);
} /* end app_process_rec_packet */ } /* end fake dlq_append */
void ptt_set (int ot, int chan, int ptt_signal) void ptt_set (int ot, int chan, int ptt_signal)

38
audio.h
View File

@ -41,19 +41,12 @@ enum audio_in_type_e {
typedef enum retry_e { typedef enum retry_e {
RETRY_NONE=0, RETRY_NONE=0,
RETRY_SWAP_SINGLE=1, RETRY_INVERT_SINGLE=1,
RETRY_SWAP_DOUBLE=2, RETRY_INVERT_DOUBLE=2,
RETRY_SWAP_TRIPLE=3, RETRY_INVERT_TRIPLE=3,
RETRY_REMOVE_SINGLE=4, RETRY_INVERT_TWO_SEP=4,
RETRY_REMOVE_DOUBLE=5, RETRY_MAX = 5} retry_t;
RETRY_REMOVE_TRIPLE=6,
RETRY_INSERT_SINGLE=7,
RETRY_INSERT_DOUBLE=8,
RETRY_SWAP_TWO_SEP=9,
RETRY_SWAP_MANY=10,
RETRY_REMOVE_MANY=11,
RETRY_REMOVE_TWO_SEP=12,
RETRY_MAX = 13} retry_t;
typedef enum sanity_e { SANITY_APRS, SANITY_AX25, SANITY_NONE } sanity_t; typedef enum sanity_e { SANITY_APRS, SANITY_AX25, SANITY_NONE } sanity_t;
@ -126,6 +119,9 @@ struct audio_s {
int decimate; /* Reduce AFSK sample rate by this factor to */ int decimate; /* Reduce AFSK sample rate by this factor to */
/* decrease computational requirements. */ /* decrease computational requirements. */
int interleave; /* If > 1, interleave samples among multiple decoders. */
/* Quick hack for experiment. */
int mark_freq; /* Two tones for AFSK modulation, in Hz. */ int mark_freq; /* Two tones for AFSK modulation, in Hz. */
int space_freq; /* Standard tones are 1200 and 2200 for 1200 baud. */ int space_freq; /* Standard tones are 1200 and 2200 for 1200 baud. */
@ -140,19 +136,13 @@ struct audio_s {
int offset; /* Spacing between filter frequencies. */ int offset; /* Spacing between filter frequencies. */
/* Next two are derived from 3 above by demod_init. */ int num_slicers; /* Number of different threshold points to decide */
/* between mark or space. */
int num_demod; /* Number of different demodulators (filters). */ /* This is derived from above by demod_init. */
/* Previously this was same as num_subchan but we add */
/* a new variation in version 1.2 where a single modem */
/* can feed multiple slicers and HDLC decoders. */
/* num_slicers could be added to be more general but */ int num_subchan; /* Total number of modems for each channel. */
/* for the intial experiment, we can just examine profiles. */
int num_subchan; /* Total number of modems / hdlc decoders for each channel. */
/* Potentially it could be product of strlen(profiles) * num_freq. */
/* Currently can't use both at once. */
/* These are for dealing with imperfect frames. */ /* These are for dealing with imperfect frames. */
@ -267,7 +257,7 @@ struct audio_s {
#define DEFAULT_BITS_PER_SAMPLE 16 #define DEFAULT_BITS_PER_SAMPLE 16
#define DEFAULT_FIX_BITS RETRY_SWAP_SINGLE #define DEFAULT_FIX_BITS RETRY_INVERT_SINGLE
/* /*
* Standard for AFSK on VHF FM. * Standard for AFSK on VHF FM.

View File

@ -914,7 +914,7 @@ static void beacon_send (int j, dwgps_info_t *gpsinfo)
/* Simulated reception. */ /* Simulated reception. */
memset (&alevel, 0xff, sizeof(alevel)); memset (&alevel, 0xff, sizeof(alevel));
dlq_append (DLQ_REC_FRAME, g_misc_config_p->beacon[j].sendto_chan, 0, pp, alevel, 0, ""); dlq_append (DLQ_REC_FRAME, g_misc_config_p->beacon[j].sendto_chan, 0, 0, pp, alevel, 0, "");
break; break;
} }
} }

View File

@ -1390,15 +1390,27 @@ void config_init (char *fname, struct audio_s *p_audio_config,
continue; continue;
} }
n = atoi(t); n = atoi(t);
if (n >= RETRY_NONE && n <= RETRY_REMOVE_TWO_SEP) { if (n >= RETRY_NONE && n < RETRY_MAX) { // MAX is actually last valid +1
p_audio_config->achan[channel].fix_bits = (retry_t)n; p_audio_config->achan[channel].fix_bits = (retry_t)n;
} }
else { else {
p_audio_config->achan[channel].fix_bits = DEFAULT_FIX_BITS; p_audio_config->achan[channel].fix_bits = DEFAULT_FIX_BITS;
text_color_set(DW_COLOR_ERROR); text_color_set(DW_COLOR_ERROR);
dw_printf ("Line %d: Invalid value for FIX_BITS. Using %d.\n", dw_printf ("Line %d: Invalid value %d for FIX_BITS. Using default of %d.\n",
line, p_audio_config->achan[channel].fix_bits); line, n, p_audio_config->achan[channel].fix_bits);
} }
if (p_audio_config->achan[channel].fix_bits > RETRY_INVERT_SINGLE) {
text_color_set(DW_COLOR_INFO);
dw_printf ("Line %d: Using a FIX_BITS value greater than %d is not recommended for normal operation.\n",
line, RETRY_INVERT_SINGLE);
}
if (p_audio_config->achan[channel].fix_bits >= RETRY_INVERT_TWO_SEP) {
text_color_set(DW_COLOR_INFO);
dw_printf ("Line %d: Using a FIX_BITS value of %d will waste a lot of CPU power and produce wrong results.\n",
line, RETRY_INVERT_TWO_SEP);
}
t = split(NULL,0); t = split(NULL,0);
while (t != NULL) { while (t != NULL) {

137
demod.c
View File

@ -119,6 +119,21 @@ int demod_init (struct audio_s *pa)
int num_letters; int num_letters;
int have_plus; int have_plus;
/*
* These are derived from config file parameters.
*
* num_subchan is number of demodulators.
* This can be increased by:
* Multiple frequencies.
* Multiple letters (not sure if I will continue this).
* New interleaved decoders.
*
* num_slicers is set to max by the "+" option.
*/
save_audio_config_p->achan[chan].num_subchan = 1;
save_audio_config_p->achan[chan].num_slicers = 1;
switch (save_audio_config_p->achan[chan].modem_type) { switch (save_audio_config_p->achan[chan].modem_type) {
case MODEM_OFF: case MODEM_OFF:
@ -234,8 +249,7 @@ int demod_init (struct audio_s *pa)
/* These can be increased later for the multi-frequency case. */ /* These can be increased later for the multi-frequency case. */
save_audio_config_p->achan[chan].num_subchan = num_letters; save_audio_config_p->achan[chan].num_subchan = num_letters;
save_audio_config_p->achan[chan].num_demod = num_letters; save_audio_config_p->achan[chan].num_slicers = 1;
/* /*
* Some error checking - Can use only one of these: * Some error checking - Can use only one of these:
@ -245,24 +259,10 @@ int demod_init (struct audio_s *pa)
* - Multiple frequencies. * - Multiple frequencies.
*/ */
if (have_plus && num_letters > 1) {
text_color_set(DW_COLOR_ERROR);
dw_printf ("Channel %d: Demodulator + option can't be combined with multiple letters.\n", chan);
strlcpy (save_audio_config_p->achan[chan].profiles, "C+", sizeof(save_audio_config_p->achan[chan].profiles)); // Reduce to one letter.
num_letters = 1;
save_audio_config_p->achan[chan].num_demod = 1;
save_audio_config_p->achan[chan].num_subchan = 1; // Will be set higher later.
save_audio_config_p->achan[chan].num_freq = 1;
}
if (have_plus && save_audio_config_p->achan[chan].num_freq > 1) { if (have_plus && save_audio_config_p->achan[chan].num_freq > 1) {
text_color_set(DW_COLOR_ERROR); text_color_set(DW_COLOR_ERROR);
dw_printf ("Channel %d: Demodulator + option can't be combined with multiple frequencies.\n", chan); dw_printf ("Channel %d: Demodulator + option can't be combined with multiple frequencies.\n", chan);
save_audio_config_p->achan[chan].num_demod = 1;
save_audio_config_p->achan[chan].num_subchan = 1; // Will be set higher later. save_audio_config_p->achan[chan].num_subchan = 1; // Will be set higher later.
save_audio_config_p->achan[chan].num_freq = 1; save_audio_config_p->achan[chan].num_freq = 1;
} }
@ -302,6 +302,7 @@ int demod_init (struct audio_s *pa)
* We have 3 cases to consider. * We have 3 cases to consider.
*/ */
// TODO1.3: revisit this logic now that it is less restrictive.
if (num_letters > 1) { if (num_letters > 1) {
int d; int d;
@ -311,17 +312,58 @@ int demod_init (struct audio_s *pa)
* Each one corresponds to a demodulator and subchannel. * Each one corresponds to a demodulator and subchannel.
* *
* An interesting experiment but probably not too useful. * An interesting experiment but probably not too useful.
* Can't have multiple frequency pairs or the + option. * Can't have multiple frequency pairs.
* In version 1.3 this can be combined with the + option.
*/ */
save_audio_config_p->achan[chan].num_subchan = num_letters; save_audio_config_p->achan[chan].num_subchan = num_letters;
save_audio_config_p->achan[chan].num_demod = num_letters;
/*
* Quick hack with special case for another experiment.
* Do this in a more general way if it turns out to be useful.
*/
save_audio_config_p->achan[chan].interleave = 1;
if (strcasecmp(save_audio_config_p->achan[chan].profiles, "EE") == 0) {
save_audio_config_p->achan[chan].interleave = 2;
save_audio_config_p->achan[chan].decimate = 1;
}
else if (strcasecmp(save_audio_config_p->achan[chan].profiles, "EEE") == 0) {
save_audio_config_p->achan[chan].interleave = 3;
save_audio_config_p->achan[chan].decimate = 1;
}
else if (strcasecmp(save_audio_config_p->achan[chan].profiles, "EEEE") == 0) {
save_audio_config_p->achan[chan].interleave = 4;
save_audio_config_p->achan[chan].decimate = 1;
}
else if (strcasecmp(save_audio_config_p->achan[chan].profiles, "EEEEE") == 0) {
save_audio_config_p->achan[chan].interleave = 5;
save_audio_config_p->achan[chan].decimate = 1;
}
else if (strcasecmp(save_audio_config_p->achan[chan].profiles, "GG") == 0) {
save_audio_config_p->achan[chan].interleave = 2;
save_audio_config_p->achan[chan].decimate = 1;
}
else if (strcasecmp(save_audio_config_p->achan[chan].profiles, "GGG") == 0) {
save_audio_config_p->achan[chan].interleave = 3;
save_audio_config_p->achan[chan].decimate = 1;
}
else if (strcasecmp(save_audio_config_p->achan[chan].profiles, "GGG+") == 0) {
save_audio_config_p->achan[chan].interleave = 3;
save_audio_config_p->achan[chan].decimate = 1;
}
else if (strcasecmp(save_audio_config_p->achan[chan].profiles, "GGGG") == 0) {
save_audio_config_p->achan[chan].interleave = 4;
save_audio_config_p->achan[chan].decimate = 1;
}
else if (strcasecmp(save_audio_config_p->achan[chan].profiles, "GGGGG") == 0) {
save_audio_config_p->achan[chan].interleave = 5;
save_audio_config_p->achan[chan].decimate = 1;
}
if (save_audio_config_p->achan[chan].num_demod != num_letters) { if (save_audio_config_p->achan[chan].num_subchan != num_letters) {
text_color_set(DW_COLOR_ERROR); text_color_set(DW_COLOR_ERROR);
dw_printf ("INTERNAL ERROR, %s:%d, chan=%d, num_demod(%d) != strlen(\"%s\")\n", dw_printf ("INTERNAL ERROR, %s:%d, chan=%d, num_subchan(%d) != strlen(\"%s\")\n",
__FILE__, __LINE__, chan, save_audio_config_p->achan[chan].num_demod, save_audio_config_p->achan[chan].profiles); __FILE__, __LINE__, chan, save_audio_config_p->achan[chan].num_subchan, save_audio_config_p->achan[chan].profiles);
} }
if (save_audio_config_p->achan[chan].num_freq != 1) { if (save_audio_config_p->achan[chan].num_freq != 1) {
@ -330,8 +372,7 @@ int demod_init (struct audio_s *pa)
__FILE__, __LINE__, chan, save_audio_config_p->achan[chan].num_freq); __FILE__, __LINE__, chan, save_audio_config_p->achan[chan].num_freq);
} }
for (d = 0; d < save_audio_config_p->achan[chan].num_demod; d++) { for (d = 0; d < save_audio_config_p->achan[chan].num_subchan; d++) {
int mark, space; int mark, space;
assert (d >= 0 && d < MAX_SUBCHANS); assert (d >= 0 && d < MAX_SUBCHANS);
@ -342,18 +383,26 @@ int demod_init (struct audio_s *pa)
mark = save_audio_config_p->achan[chan].mark_freq; mark = save_audio_config_p->achan[chan].mark_freq;
space = save_audio_config_p->achan[chan].space_freq; space = save_audio_config_p->achan[chan].space_freq;
if (save_audio_config_p->achan[chan].num_demod != 1) { if (save_audio_config_p->achan[chan].num_subchan != 1) {
text_color_set(DW_COLOR_DEBUG); text_color_set(DW_COLOR_DEBUG);
dw_printf (" %d.%d: %c %d & %d\n", chan, d, profile, mark, space); dw_printf (" %d.%d: %c %d & %d\n", chan, d, profile, mark, space);
} }
demod_afsk_init (save_audio_config_p->adev[ACHAN2ADEV(chan)].samples_per_sec / save_audio_config_p->achan[chan].decimate, demod_afsk_init (save_audio_config_p->adev[ACHAN2ADEV(chan)].samples_per_sec / (save_audio_config_p->achan[chan].decimate * save_audio_config_p->achan[chan].interleave),
save_audio_config_p->achan[chan].baud, save_audio_config_p->achan[chan].baud,
mark, mark,
space, space,
profile, profile,
D); D);
if (have_plus) {
/* I'm not happy about putting this hack here. */
/* should pass in as a parameter rather than adding on later. */
save_audio_config_p->achan[chan].num_slicers = MAX_SLICERS;
D->num_slicers = MAX_SLICERS;
}
/* For siginal level reporting, we want a longer term view. */ /* For siginal level reporting, we want a longer term view. */
// TODO: Should probably move this into the init functions. // TODO: Should probably move this into the init functions.
@ -364,7 +413,7 @@ int demod_init (struct audio_s *pa)
else if (have_plus) { else if (have_plus) {
/* /*
* PLUS - which implies we have only one letter and one frequency pair. * PLUS - which (formerly) implies we have only one letter and one frequency pair.
* *
* One demodulator feeds multiple slicers, each a subchannel. * One demodulator feeds multiple slicers, each a subchannel.
*/ */
@ -381,21 +430,19 @@ int demod_init (struct audio_s *pa)
__FILE__, __LINE__, chan, save_audio_config_p->achan[chan].num_freq); __FILE__, __LINE__, chan, save_audio_config_p->achan[chan].num_freq);
} }
if (save_audio_config_p->achan[chan].num_demod != save_audio_config_p->achan[chan].num_demod) { if (save_audio_config_p->achan[chan].num_freq != save_audio_config_p->achan[chan].num_subchan) {
text_color_set(DW_COLOR_ERROR); text_color_set(DW_COLOR_ERROR);
dw_printf ("INTERNAL ERROR, %s:%d, chan=%d, num_freq(%d) != num_demod(%d)\n", dw_printf ("INTERNAL ERROR, %s:%d, chan=%d, num_freq(%d) != num_subchan(%d)\n",
__FILE__, __LINE__, chan, save_audio_config_p->achan[chan].num_freq, save_audio_config_p->achan[chan].num_demod); __FILE__, __LINE__, chan, save_audio_config_p->achan[chan].num_freq, save_audio_config_p->achan[chan].num_subchan);
} }
struct demodulator_state_s *D; struct demodulator_state_s *D;
D = &demodulator_state[chan][0]; D = &demodulator_state[chan][0];
/* I'm not happy about putting this hack here. */ /* I'm not happy about putting this hack here. */
/* This belongs in demod_afsk_init but it doesn't have access to the audio config. */ /* This belongs in demod_afsk_init but it doesn't have access to the audio config. */
save_audio_config_p->achan[chan].num_demod = 1; save_audio_config_p->achan[chan].num_slicers = MAX_SLICERS;
save_audio_config_p->achan[chan].num_subchan = MAX_SUBCHANS;
demod_afsk_init (save_audio_config_p->adev[ACHAN2ADEV(chan)].samples_per_sec / save_audio_config_p->achan[chan].decimate, demod_afsk_init (save_audio_config_p->adev[ACHAN2ADEV(chan)].samples_per_sec / save_audio_config_p->achan[chan].decimate,
save_audio_config_p->achan[chan].baud, save_audio_config_p->achan[chan].baud,
@ -404,10 +451,13 @@ int demod_init (struct audio_s *pa)
save_audio_config_p->achan[chan].profiles[0], save_audio_config_p->achan[chan].profiles[0],
D); D);
/* I'm not happy about putting this hack here. */ if (have_plus) {
/* should pass in as a parameter rather than adding on later. */ /* I'm not happy about putting this hack here. */
/* should pass in as a parameter rather than adding on later. */
D->num_slicers = MAX_SUBCHANS; save_audio_config_p->achan[chan].num_slicers = MAX_SLICERS;
D->num_slicers = MAX_SLICERS;
}
/* For siginal level reporting, we want a longer term view. */ /* For siginal level reporting, we want a longer term view. */
@ -427,7 +477,6 @@ int demod_init (struct audio_s *pa)
__FILE__, __LINE__, chan, save_audio_config_p->achan[chan].profiles); __FILE__, __LINE__, chan, save_audio_config_p->achan[chan].profiles);
} }
save_audio_config_p->achan[chan].num_demod = save_audio_config_p->achan[chan].num_freq;
save_audio_config_p->achan[chan].num_subchan = save_audio_config_p->achan[chan].num_freq; save_audio_config_p->achan[chan].num_subchan = save_audio_config_p->achan[chan].num_freq;
for (d = 0; d < save_audio_config_p->achan[chan].num_freq; d++) { for (d = 0; d < save_audio_config_p->achan[chan].num_freq; d++) {
@ -455,6 +504,14 @@ int demod_init (struct audio_s *pa)
profile, profile,
D); D);
if (have_plus) {
/* I'm not happy about putting this hack here. */
/* should pass in as a parameter rather than adding on later. */
save_audio_config_p->achan[chan].num_slicers = MAX_SLICERS;
D->num_slicers = MAX_SLICERS;
}
/* For siginal level reporting, we want a longer term view. */ /* For siginal level reporting, we want a longer term view. */
D->quick_attack = D->agc_fast_attack * 0.2; D->quick_attack = D->agc_fast_attack * 0.2;
@ -495,15 +552,14 @@ int demod_init (struct audio_s *pa)
D = &demodulator_state[chan][0]; // first subchannel D = &demodulator_state[chan][0]; // first subchannel
save_audio_config_p->achan[chan].num_subchan = 1; save_audio_config_p->achan[chan].num_subchan = 1;
save_audio_config_p->achan[chan].num_demod = 1; save_audio_config_p->achan[chan].num_slicers = 1;
if (strchr(save_audio_config_p->achan[chan].profiles, '+') != NULL) { if (strchr(save_audio_config_p->achan[chan].profiles, '+') != NULL) {
/* I'm not happy about putting this hack here. */ /* I'm not happy about putting this hack here. */
/* This belongs in demod_9600_init but it doesn't have access to the audio config. */ /* This belongs in demod_9600_init but it doesn't have access to the audio config. */
save_audio_config_p->achan[chan].num_demod = 1; save_audio_config_p->achan[chan].num_slicers = MAX_SLICERS;
save_audio_config_p->achan[chan].num_subchan = MAX_SUBCHANS;
} }
demod_9600_init (UPSAMPLE * save_audio_config_p->adev[ACHAN2ADEV(chan)].samples_per_sec, save_audio_config_p->achan[chan].baud, D); demod_9600_init (UPSAMPLE * save_audio_config_p->adev[ACHAN2ADEV(chan)].samples_per_sec, save_audio_config_p->achan[chan].baud, D);
@ -513,7 +569,8 @@ int demod_init (struct audio_s *pa)
/* I'm not happy about putting this hack here. */ /* I'm not happy about putting this hack here. */
/* should pass in as a parameter rather than adding on later. */ /* should pass in as a parameter rather than adding on later. */
D->num_slicers = MAX_SUBCHANS; save_audio_config_p->achan[chan].num_slicers = MAX_SLICERS;
D->num_slicers = MAX_SLICERS;
} }
/* For siginal level reporting, we want a longer term view. */ /* For siginal level reporting, we want a longer term view. */

View File

@ -145,6 +145,7 @@ void demod_9600_init (int samples_per_sec, int baud, struct demodulator_state_s
int j; int j;
memset (D, 0, sizeof(struct demodulator_state_s)); memset (D, 0, sizeof(struct demodulator_state_s));
D->num_slicers = 1;
//dw_printf ("demod_9600_init(rate=%d, baud=%d, D ptr)\n", samples_per_sec, baud); //dw_printf ("demod_9600_init(rate=%d, baud=%d, D ptr)\n", samples_per_sec, baud);
@ -259,7 +260,7 @@ void demod_9600_init (int samples_per_sec, int baud, struct demodulator_state_s
* *
*--------------------------------------------------------------------*/ *--------------------------------------------------------------------*/
static void nudge_pll (int chan, int subchan, int demod_data, struct demodulator_state_s *D); static void inline nudge_pll (int chan, int subchan, int slice, int demod_data, struct demodulator_state_s *D);
__attribute__((hot)) __attribute__((hot))
void demod_9600_process_sample (int chan, int sam, struct demodulator_state_s *D) void demod_9600_process_sample (int chan, int sam, struct demodulator_state_s *D)
@ -377,19 +378,16 @@ void demod_9600_process_sample (int chan, int sam, struct demodulator_state_s *D
/* AGC should generally keep this around -1 to +1 range. */ /* AGC should generally keep this around -1 to +1 range. */
demod_data = demod_out > 0; demod_data = demod_out > 0;
nudge_pll (chan, subchan, 0, demod_data, D);
nudge_pll (chan, subchan, demod_data, D);
} }
else { else {
int s; int slice;
assert (subchan == 0);
/* Multiple slicers each feeding its own HDLC decoder. */ /* Multiple slicers each feeding its own HDLC decoder. */
for (s=0; s<D->num_slicers; s++) { for (slice=0; slice<D->num_slicers; slice++) {
demod_data = demod_out > slice_point[s]; demod_data = demod_out > slice_point[slice];
nudge_pll (chan, s, demod_data, D); nudge_pll (chan, subchan, slice, demod_data, D);
} }
} }
@ -397,7 +395,7 @@ void demod_9600_process_sample (int chan, int sam, struct demodulator_state_s *D
__attribute__((hot)) __attribute__((hot))
static void nudge_pll (int chan, int subchan, int demod_data, struct demodulator_state_s *D) static void inline nudge_pll (int chan, int subchan, int slice, int demod_data, struct demodulator_state_s *D)
{ {
/* /*
@ -425,10 +423,11 @@ static void nudge_pll (int chan, int subchan, int demod_data, struct demodulator
* This was optimized for 1200 baud AFSK. There might be some opportunity * This was optimized for 1200 baud AFSK. There might be some opportunity
* for improvement here. * for improvement here.
*/ */
D->slicer[subchan].prev_d_c_pll = D->slicer[subchan].data_clock_pll;
D->slicer[subchan].data_clock_pll += D->pll_step_per_sample;
if (D->slicer[subchan].data_clock_pll < 0 && D->slicer[subchan].prev_d_c_pll > 0) { D->slicer[slice].prev_d_c_pll = D->slicer[slice].data_clock_pll;
D->slicer[slice].data_clock_pll += D->pll_step_per_sample;
if (D->slicer[slice].data_clock_pll < 0 && D->slicer[slice].prev_d_c_pll > 0) {
/* Overflow. */ /* Overflow. */
@ -443,20 +442,20 @@ static void nudge_pll (int chan, int subchan, int demod_data, struct demodulator
// Warning: 'descram' set but not used. // Warning: 'descram' set but not used.
// It's used in conditional debug code below. // It's used in conditional debug code below.
// descram = // descram =
descramble (demod_data, &(D->slicer[subchan].lfsr)); descramble (demod_data, &(D->slicer[slice].lfsr));
hdlc_rec_bit (chan, subchan, demod_data, 1, D->slicer[subchan].lfsr); hdlc_rec_bit (chan, subchan, slice, demod_data, 1, D->slicer[slice].lfsr);
} }
if (demod_data != D->slicer[subchan].prev_demod_data) { if (demod_data != D->slicer[slice].prev_demod_data) {
// Note: Test for this demodulator, not overall for channel. // Note: Test for this demodulator, not overall for channel.
if (hdlc_rec_gathering (chan, subchan)) { if (hdlc_rec_gathering (chan, subchan, slice)) {
D->slicer[subchan].data_clock_pll = (int)(D->slicer[subchan].data_clock_pll * D->pll_locked_inertia); D->slicer[slice].data_clock_pll = (int)(D->slicer[slice].data_clock_pll * D->pll_locked_inertia);
} }
else { else {
D->slicer[subchan].data_clock_pll = (int)(D->slicer[subchan].data_clock_pll * D->pll_searching_inertia); D->slicer[slice].data_clock_pll = (int)(D->slicer[slice].data_clock_pll * D->pll_searching_inertia);
} }
} }
@ -464,7 +463,7 @@ static void nudge_pll (int chan, int subchan, int demod_data, struct demodulator
#if DEBUG5 #if DEBUG5
//if (chan == 0) { //if (chan == 0) {
if (hdlc_rec_gathering (chan,subchan)) { if (hdlc_rec_gathering (chan,subchan,slice)) {
char fname[30]; char fname[30];
@ -507,7 +506,7 @@ static void nudge_pll (int chan, int subchan, int demod_data, struct demodulator
* Remember demodulator output (pre-descrambling) so we can compare next time * Remember demodulator output (pre-descrambling) so we can compare next time
* for the DPLL sync. * for the DPLL sync.
*/ */
D->slicer[subchan].prev_demod_data = demod_data; D->slicer[slice].prev_demod_data = demod_data;
} /* end nudge_pll */ } /* end nudge_pll */

View File

@ -198,6 +198,7 @@ void demod_afsk_init (int samples_per_sec, int baud, int mark_freq,
int j; int j;
memset (D, 0, sizeof(struct demodulator_state_s)); memset (D, 0, sizeof(struct demodulator_state_s));
D->num_slicers = 1;
#if DEBUG1 #if DEBUG1
dw_printf ("demod_afsk_init (rate=%d, baud=%d, mark=%d, space=%d, profile=%c\n", dw_printf ("demod_afsk_init (rate=%d, baud=%d, mark=%d, space=%d, profile=%c\n",
@ -332,7 +333,7 @@ void demod_afsk_init (int samples_per_sec, int baud, int mark_freq,
case 'E': case 'E':
/* 1200 baud - Started out similar to C but add prefilter. */ /* 1200 baud - Started out similar to C but add prefilter. */
/* Version 1.2 - EXPERIMENTAL - Needs more fine tuning. */ /* Version 1.2 */
/* Enhancements: */ /* Enhancements: */
/* + Add prefilter. Previously used for 300 baud D, but not 1200. */ /* + Add prefilter. Previously used for 300 baud D, but not 1200. */
/* + Prefilter length now independent of M/S filters. */ /* + Prefilter length now independent of M/S filters. */
@ -366,6 +367,34 @@ void demod_afsk_init (int samples_per_sec, int baud, int mark_freq,
D->pll_searching_inertia = 0.50; D->pll_searching_inertia = 0.50;
break; break;
case 'G':
/* 1200 baud - Started out same as E but add 3 way interleave. */
/* Version 1.3 - EXPERIMENTAL - Needs more fine tuning. */
//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.15;
D->pre_filter_len_bits = 128 * 1200. / (44100. / 3.);
D->pre_window = BP_WINDOW_TRUNCATED;
D->ms_filter_len_bits = 25 * 1200. / (44100. / 3.);
D->ms_window = BP_WINDOW_COSINE;
D->lpf_use_fir = 1;
D->lpf_baud = 1.16;
D->lp_filter_len_bits = 21 * 1200. / (44100. / 3.);
D->lp_window = BP_WINDOW_TRUNCATED;
D->agc_fast_attack = 0.130;
D->agc_slow_decay = 0.00013;
D->hysteresis = 0.01;
D->pll_locked_inertia = 0.73;
D->pll_searching_inertia = 0.64;
break;
default: default:
text_color_set(DW_COLOR_ERROR); text_color_set(DW_COLOR_ERROR);
@ -719,8 +748,8 @@ int main ()
modem.achan[0].mark_freq = DEFAULT_MARK_FREQ; modem.achan[0].mark_freq = DEFAULT_MARK_FREQ;
modem.achan[0].space_freq = DEFAULT_SPACE_FREQ; modem.achan[0].space_freq = DEFAULT_SPACE_FREQ;
modem.achan[0].baud = DEFAULT_BAUD; modem.achan[0].baud = DEFAULT_BAUD;
modem.achan[0].num_demod = 1;
modem.achan[0].num_subchan = 1; modem.achan[0].num_subchan = 1;
modem.achan[0].num_slicers = 1;
demod_afsk_init (modem.adev[0].samples_per_sec, modem.achan[0].baud, demod_afsk_init (modem.adev[0].samples_per_sec, modem.achan[0].baud,
@ -792,7 +821,7 @@ int main ()
* *
*--------------------------------------------------------------------*/ *--------------------------------------------------------------------*/
static void nudge_pll (int chan, int subchan, int demod_data, struct demodulator_state_s *D); static void inline nudge_pll (int chan, int subchan, int slice, int demod_data, struct demodulator_state_s *D);
__attribute__((hot)) __attribute__((hot))
void demod_afsk_process_sample (int chan, int subchan, int sam, struct demodulator_state_s *D) void demod_afsk_process_sample (int chan, int subchan, int sam, struct demodulator_state_s *D)
@ -1023,26 +1052,18 @@ void demod_afsk_process_sample (int chan, int subchan, int sam, struct demodulat
else { else {
demod_data = D->slicer[subchan].prev_demod_data; demod_data = D->slicer[subchan].prev_demod_data;
} }
nudge_pll (chan, subchan, 0, demod_data, D);
nudge_pll (chan, subchan, demod_data, D);
} }
else { else {
int s; int slice;
assert (subchan == 0); for (slice=0; slice<D->num_slicers; slice++) {
demod_data = m_amp > s_amp * space_gain[slice];
/* "G" profile with one demodulator and multiple slicers */ nudge_pll (chan, subchan, slice, demod_data, D);
/* each feeding its own HDLC decoder. */
for (s=0; s<D->num_slicers; s++) {
demod_data = m_amp > s_amp * space_gain[s];
nudge_pll (chan, s, demod_data, D);
} }
} }
#if DEBUG4 #if DEBUG4
if (chan == 0) { if (chan == 0) {
@ -1080,12 +1101,15 @@ void demod_afsk_process_sample (int chan, int subchan, int sam, struct demodulat
__attribute__((hot)) __attribute__((hot))
static void nudge_pll (int chan, int subchan, int demod_data, struct demodulator_state_s *D) static void inline 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. * Finally, a PLL is used to sample near the centers of the data bits.
* *
* D points to a demodulator for a channel/subchannel pair so we don't
* have to keep recalculating it.
*
* D->data_clock_pll is a SIGNED 32 bit variable. * D->data_clock_pll is a SIGNED 32 bit variable.
* When it overflows from a large positive value to a negative value, we * When it overflows from a large positive value to a negative value, we
* sample a data bit from the demodulated signal. * sample a data bit from the demodulated signal.
@ -1109,33 +1133,34 @@ static void nudge_pll (int chan, int subchan, int demod_data, struct demodulator
* I don't think the optimal value will depend on the audio sample rate * I don't think the optimal value will depend on the audio sample rate
* because this happens for each transition from the demodulator. * because this happens for each transition from the demodulator.
*/ */
D->slicer[subchan].prev_d_c_pll = D->slicer[subchan].data_clock_pll;
D->slicer[subchan].data_clock_pll += D->pll_step_per_sample; D->slicer[slice].prev_d_c_pll = D->slicer[slice].data_clock_pll;
D->slicer[slice].data_clock_pll += D->pll_step_per_sample;
//text_color_set(DW_COLOR_DEBUG); //text_color_set(DW_COLOR_DEBUG);
// dw_printf ("prev = %lx, new data clock pll = %lx\n" D->prev_d_c_pll, D->data_clock_pll); // dw_printf ("prev = %lx, new data clock pll = %lx\n" D->prev_d_c_pll, D->data_clock_pll);
if (D->slicer[subchan].data_clock_pll < 0 && D->slicer[subchan].prev_d_c_pll > 0) { if (D->slicer[slice].data_clock_pll < 0 && D->slicer[slice].prev_d_c_pll > 0) {
/* Overflow. */ /* Overflow. */
hdlc_rec_bit (chan, subchan, demod_data, 0, -1); hdlc_rec_bit (chan, subchan, slice, demod_data, 0, -1);
} }
if (demod_data != D->slicer[subchan].prev_demod_data) { if (demod_data != D->slicer[slice].prev_demod_data) {
if (hdlc_rec_gathering (chan, subchan)) { if (hdlc_rec_gathering (chan, subchan, slice)) {
D->slicer[subchan].data_clock_pll = (int)(D->slicer[subchan].data_clock_pll * D->pll_locked_inertia); D->slicer[slice].data_clock_pll = (int)(D->slicer[slice].data_clock_pll * D->pll_locked_inertia);
} }
else { else {
D->slicer[subchan].data_clock_pll = (int)(D->slicer[subchan].data_clock_pll * D->pll_searching_inertia); D->slicer[slice].data_clock_pll = (int)(D->slicer[slice].data_clock_pll * D->pll_searching_inertia);
} }
} }
/* /*
* Remember demodulator output so we can compare next time. * Remember demodulator output so we can compare next time.
*/ */
D->slicer[subchan].prev_demod_data = demod_data; D->slicer[slice].prev_demod_data = demod_data;
} /* end nudge_pll */ } /* end nudge_pll */

View File

@ -231,7 +231,7 @@ int main (int argc, char *argv[])
text_color_init(t_opt); text_color_init(t_opt);
text_color_set(DW_COLOR_INFO); text_color_set(DW_COLOR_INFO);
//dw_printf ("Dire Wolf version %d.%d (%s) Beta Test\n", MAJOR_VERSION, MINOR_VERSION, __DATE__); //dw_printf ("Dire Wolf version %d.%d (%s) Beta Test\n", MAJOR_VERSION, MINOR_VERSION, __DATE__);
dw_printf ("Dire Wolf DEVELOPMENT version %d.%d %s (%s)\n", MAJOR_VERSION, MINOR_VERSION, "G", __DATE__); dw_printf ("Dire Wolf DEVELOPMENT version %d.%d %s (%s)\n", MAJOR_VERSION, MINOR_VERSION, "H", __DATE__);
//dw_printf ("Dire Wolf version %d.%d\n", MAJOR_VERSION, MINOR_VERSION); //dw_printf ("Dire Wolf version %d.%d\n", MAJOR_VERSION, MINOR_VERSION);
#if defined(ENABLE_GPSD) // later or hamlib ... #if defined(ENABLE_GPSD) // later or hamlib ...
@ -721,6 +721,7 @@ int main (int argc, char *argv[])
* Inputs: chan - Audio channel number, 0 or 1. * Inputs: chan - Audio channel number, 0 or 1.
* subchan - Which modem caught it. * subchan - Which modem caught it.
* Special case -1 for DTMF decoder. * Special case -1 for DTMF decoder.
* slice - Slicer which caught it.
* pp - Packet handle. * pp - Packet handle.
* alevel - Audio level, range of 0 - 100. * alevel - Audio level, range of 0 - 100.
* (Special case, use negative to skip * (Special case, use negative to skip
@ -737,8 +738,7 @@ int main (int argc, char *argv[])
// TODO: Use only one printf per line so output doesn't get jumbled up with stuff from other threads. // TODO: Use only one printf per line so output doesn't get jumbled up with stuff from other threads.
void app_process_rec_packet (int chan, int subchan, int slice, packet_t pp, alevel_t alevel, retry_t retries, char *spectrum)
void app_process_rec_packet (int chan, int subchan, packet_t pp, alevel_t alevel, retry_t retries, char *spectrum)
{ {
char stemp[500]; char stemp[500];
@ -751,6 +751,7 @@ void app_process_rec_packet (int chan, int subchan, packet_t pp, alevel_t alevel
assert (chan >= 0 && chan < MAX_CHANS); assert (chan >= 0 && chan < MAX_CHANS);
assert (subchan >= -1 && subchan < MAX_SUBCHANS); assert (subchan >= -1 && subchan < MAX_SUBCHANS);
assert (slice >= 0 && slice < MAX_SLICERS);
assert (pp != NULL); // 1.1J+ assert (pp != NULL); // 1.1J+
strlcpy (display_retries, "", sizeof(display_retries)); strlcpy (display_retries, "", sizeof(display_retries));
@ -848,9 +849,16 @@ void app_process_rec_packet (int chan, int subchan, packet_t pp, alevel_t alevel
else { else {
text_color_set(DW_COLOR_DEBUG); text_color_set(DW_COLOR_DEBUG);
} }
if (audio_config.achan[chan].num_subchan > 1) {
if (audio_config.achan[chan].num_subchan > 1 && audio_config.achan[chan].num_slicers == 1) {
dw_printf ("[%d.%d] ", chan, subchan); dw_printf ("[%d.%d] ", chan, subchan);
} }
else if (audio_config.achan[chan].num_subchan == 1 && audio_config.achan[chan].num_slicers > 1) {
dw_printf ("[%d.%d] ", chan, slice);
}
else if (audio_config.achan[chan].num_subchan > 1 && audio_config.achan[chan].num_slicers > 1) {
dw_printf ("[%d.%d.%d] ", chan, subchan, slice);
}
else { else {
dw_printf ("[%d] ", chan); dw_printf ("[%d] ", chan);
} }

View File

@ -48,6 +48,17 @@
#define MAX_SUBCHANS 9 #define MAX_SUBCHANS 9
/*
* Each one of these can have multiple slicers, at
* different levels, to compensate for different
* amplitudes of the AFSK tones.
* Intially used same number as subchannels but
* we could probably trim this down a little
* without impacting performance.
*/
#define MAX_SLICERS 9
#if __WIN32__ #if __WIN32__
#include <windows.h> #include <windows.h>

20
dlq.c
View File

@ -64,14 +64,15 @@ struct dlq_item_s {
/* Special case, -1 means DTMF decoder. */ /* Special case, -1 means DTMF decoder. */
/* Maybe we should have a different type in this case? */ /* Maybe we should have a different type in this case? */
int slice; /* Winning slicer. */
packet_t pp; /* Pointer to frame structure. */ packet_t pp; /* Pointer to frame structure. */
alevel_t alevel; /* Audio level. */ alevel_t alevel; /* Audio level. */
retry_t retries; /* Effort expended to get a valid CRC. */ retry_t retries; /* Effort expended to get a valid CRC. */
char spectrum[MAX_SUBCHANS+1]; /* "Spectrum" display for multi-decoders. */ char spectrum[MAX_SUBCHANS*MAX_SLICERS+1]; /* "Spectrum" display for multi-decoders. */
}; };
@ -206,6 +207,8 @@ void dlq_init (void)
* subchan - Which modem caught it. * subchan - Which modem caught it.
* Special case -1 for APRStt gateway. * Special case -1 for APRStt gateway.
* *
* slice - Which slice we picked.
*
* pp - Address of packet object. * pp - Address of packet object.
* Caller should NOT make any references to * Caller should NOT make any references to
* it after this point because it could * it after this point because it could
@ -231,7 +234,7 @@ void dlq_init (void)
* *
*--------------------------------------------------------------------*/ *--------------------------------------------------------------------*/
void dlq_append (dlq_type_t type, int chan, int subchan, packet_t pp, alevel_t alevel, retry_t retries, char *spectrum) void dlq_append (dlq_type_t type, int chan, int subchan, int slice, packet_t pp, alevel_t alevel, retry_t retries, char *spectrum)
{ {
struct dlq_item_s *pnew; struct dlq_item_s *pnew;
@ -271,6 +274,7 @@ void dlq_append (dlq_type_t type, int chan, int subchan, packet_t pp, alevel_t a
pnew->nextp = NULL; pnew->nextp = NULL;
pnew->type = type; pnew->type = type;
pnew->chan = chan; pnew->chan = chan;
pnew->slice = slice;
pnew->subchan = subchan; pnew->subchan = subchan;
pnew->pp = pp; pnew->pp = pp;
pnew->alevel = alevel; pnew->alevel = alevel;
@ -514,7 +518,8 @@ void dlq_wait_while_empty (void)
* Outputs: type - type of queue entry. * Outputs: type - type of queue entry.
* *
* chan - channel of received frame. * chan - channel of received frame.
* subchan - which modem caught it. * subchan - which demodulator caught it.
* slice - which slicer caught it.
* *
* pp - pointer to packet object when type is DLQ_REC_FRAME. * pp - pointer to packet object when type is DLQ_REC_FRAME.
* Caller should destroy it with ax25_delete when finished with it. * Caller should destroy it with ax25_delete when finished with it.
@ -524,7 +529,8 @@ void dlq_wait_while_empty (void)
* *
*--------------------------------------------------------------------*/ *--------------------------------------------------------------------*/
int dlq_remove (dlq_type_t *type, int *chan, int *subchan, packet_t *pp, alevel_t *alevel, retry_t *retries, char *spectrum, size_t spectrumsize)
int dlq_remove (dlq_type_t *type, int *chan, int *subchan, int *slice, packet_t *pp, alevel_t *alevel, retry_t *retries, char *spectrum, size_t spectrumsize)
{ {
struct dlq_item_s *phead; struct dlq_item_s *phead;
@ -557,6 +563,7 @@ int dlq_remove (dlq_type_t *type, int *chan, int *subchan, packet_t *pp, alevel_
*type = -1; *type = -1;
*chan = -1; *chan = -1;
*subchan = -1; *subchan = -1;
*slice = -1;
*pp = NULL; *pp = NULL;
memset (alevel, 0xff, sizeof(*alevel)); memset (alevel, 0xff, sizeof(*alevel));
@ -573,6 +580,7 @@ int dlq_remove (dlq_type_t *type, int *chan, int *subchan, packet_t *pp, alevel_
*type = phead->type; *type = phead->type;
*chan = phead->chan; *chan = phead->chan;
*subchan = phead->subchan; *subchan = phead->subchan;
*slice = phead->slice;
*pp = phead->pp; *pp = phead->pp;
*alevel = phead->alevel; *alevel = phead->alevel;
*retries = phead->retries; *retries = phead->retries;

5
dlq.h
View File

@ -18,12 +18,11 @@ void dlq_init (void);
typedef enum dlq_type_e {DLQ_REC_FRAME} dlq_type_t; typedef enum dlq_type_e {DLQ_REC_FRAME} dlq_type_t;
void dlq_append (dlq_type_t type, int chan, int subchan, packet_t pp, alevel_t alevel, retry_t retries, char *spectrum); void dlq_append (dlq_type_t type, int chan, int subchan, int slice, packet_t pp, alevel_t alevel, retry_t retries, char *spectrum);
void dlq_wait_while_empty (void); void dlq_wait_while_empty (void);
int dlq_remove (dlq_type_t *type, int *chan, int *subchan, packet_t *pp, alevel_t *alevel, retry_t *retries, char *spectrum, size_t spectrumsize); int dlq_remove (dlq_type_t *type, int *chan, int *subchan, int *slice, packet_t *pp, alevel_t *alevel, retry_t *retries, char *spectrum, size_t spectrumsize);
#endif #endif

Binary file not shown.

Binary file not shown.

2
dtmf.c
View File

@ -273,7 +273,7 @@ char dtmf_sample (int c, float input)
// Update Data Carrier Detect Indicator. // Update Data Carrier Detect Indicator.
dcd_change (c, MAX_SUBCHANS, decoded != ' '); dcd_change (c, MAX_SUBCHANS, 0, decoded != ' ');
/* Reset timeout timer. */ /* Reset timeout timer. */
if (decoded != ' ') { if (decoded != ' ') {

View File

@ -180,7 +180,28 @@ struct demodulator_state_s
* Each slicer has its own PLL and HDLC decoder. * Each slicer has its own PLL and HDLC decoder.
*/ */
#if 1 /*
* Version 1.3: Clean up subchan vs. slicer.
*
* Originally some number of CHANNELS (originally 2, later 6)
* which can have multiple parallel demodulators called SUB-CHANNELS.
* This was originally for staggered frequencies for HF SSB.
* It can also be used for multiple demodulators with the same
* frequency but other differing parameters.
* Each subchannel has its own demodulator and HDLC decoder.
*
* In version 1.2 we added multiple SLICERS.
* The data structure, here, has multiple slicers per
* demodulator (subchannel). Due to fuzzy thinking or
* expediency, the multiple slicers got mapped into subchannels.
* This means we can't use both multiple decoders and
* multiple slicers at the same time.
*
* Clean this up in 1.3 and keep the concepts separate.
* This means adding a third variable many places
* we are passing around the origin.
*
*/
struct { struct {
signed int data_clock_pll; // PLL for data clock recovery. signed int data_clock_pll; // PLL for data clock recovery.
@ -197,25 +218,13 @@ struct demodulator_state_s
int lfsr; // Descrambler shift register. int lfsr; // Descrambler shift register.
} slicer [MAX_SUBCHANS]; } slicer [MAX_SLICERS]; // Actual number in use is num_slicers.
// Should be in range 1 .. MAX_SLICERS,
#else
signed int data_clock_pll; // PLL for data clock recovery.
// It is incremented by pll_step_per_sample
// for each audio sample.
signed int prev_d_c_pll; // Previous value of above, before
// incrementing, to detect overflows.
int prev_demod_data; // Previous data bit detected.
// Used to look for transitions.
#endif
/* /*
* Special for Rino decoder only. * Special for Rino decoder only.
* One for each possible signal polarity. * One for each possible signal polarity.
* The project showed promise but fell by the wayside.
*/ */
#if 0 #if 0

View File

@ -29,6 +29,7 @@
#include <stdio.h> #include <stdio.h>
#include <assert.h> #include <assert.h>
#include <string.h>
#include "direwolf.h" #include "direwolf.h"
#include "demod.h" #include "demod.h"
@ -105,13 +106,11 @@ struct hdlc_state_s {
}; };
static struct hdlc_state_s hdlc_state[MAX_CHANS][MAX_SUBCHANS][MAX_SLICERS];
static struct hdlc_state_s hdlc_state[MAX_CHANS][MAX_SUBCHANS];
static int num_subchan[MAX_CHANS]; //TODO1.2 use ptr rather than copy. static int num_subchan[MAX_CHANS]; //TODO1.2 use ptr rather than copy.
static int composite_dcd[MAX_CHANS]; static int composite_dcd[MAX_CHANS][MAX_SUBCHANS+1];
/*********************************************************************************** /***********************************************************************************
@ -128,7 +127,7 @@ static int was_init = 0;
void hdlc_rec_init (struct audio_s *pa) void hdlc_rec_init (struct audio_s *pa)
{ {
int j, k; int ch, sub, slice;
struct hdlc_state_s *H; struct hdlc_state_s *H;
//text_color_set(DW_COLOR_DEBUG); //text_color_set(DW_COLOR_DEBUG);
@ -136,34 +135,33 @@ void hdlc_rec_init (struct audio_s *pa)
assert (pa != NULL); assert (pa != NULL);
for (j=0; j<MAX_CHANS; j++) memset (composite_dcd, 0, sizeof(composite_dcd));
for (ch = 0; ch < MAX_CHANS; ch++)
{ {
composite_dcd[j] = 0;
if (pa->achan[j].valid) { if (pa->achan[ch].valid) {
num_subchan[j] = pa->achan[j].num_subchan; num_subchan[ch] = pa->achan[ch].num_subchan;
assert (num_subchan[j] >= 1 && num_subchan[j] <= MAX_SUBCHANS); assert (num_subchan[ch] >= 1 && num_subchan[ch] <= MAX_SUBCHANS);
for (k=0; k<MAX_SUBCHANS; k++) for (sub = 0; sub < num_subchan[ch]; sub++)
{ {
H = &hdlc_state[j][k]; for (slice = 0; slice < MAX_SLICERS; slice++) {
H->prev_raw = 0; H = &hdlc_state[ch][sub][slice];
H->lfsr = 0;
H->prev_descram = 0; H->olen = -1;
H->pat_det = 0;
H->flag4_det = 0; // TODO: FIX13 wasteful if not needed.
H->olen = -1; // Should loop on number of slicers, not max.
H->frame_len = 0;
H->data_detect = 0; H->rrbb = rrbb_new(ch, sub, slice, pa->achan[ch].modem_type == MODEM_SCRAMBLE, H->lfsr, H->prev_descram);
// TODO: wasteful if not needed. }
H->rrbb = rrbb_new(j, k, pa->achan[j].modem_type == MODEM_SCRAMBLE, H->lfsr, H->prev_descram);
} }
} }
} }
hdlc_rec2_init (pa); hdlc_rec2_init (pa);
was_init = 1; was_init = 1;
} }
@ -178,14 +176,16 @@ void hdlc_rec_init (struct audio_s *pa)
* *
* Inputs: chan - Channel number. * Inputs: chan - Channel number.
* *
* subchan - This allows multiple decoders per channel. * subchan - This allows multiple demodulators per channel.
*
* slice - Allows multiple slicers per demodulator (subchannel).
* *
* raw - One bit from the demodulator. * raw - One bit from the demodulator.
* should be 0 or 1. * should be 0 or 1.
* *
* is_scrambled - Is the data scrambled? * is_scrambled - Is the data scrambled?
* *
* descram_state - Current descrambler state. * descram_state - Current descrambler state. (not used - remove)
* *
* *
* Description: This is called once for each received bit. * Description: This is called once for each received bit.
@ -196,9 +196,7 @@ void hdlc_rec_init (struct audio_s *pa)
// TODO: int not_used_remove // TODO: int not_used_remove
void hdlc_rec_bit (int chan, int subchan, int slice, int raw, int is_scrambled, int not_used_remove)
void hdlc_rec_bit (int chan, int subchan, int raw, int is_scrambled, int not_used_remove)
{ {
int dbit; /* Data bit after undoing NRZI. */ int dbit; /* Data bit after undoing NRZI. */
@ -210,10 +208,12 @@ void hdlc_rec_bit (int chan, int subchan, int raw, int is_scrambled, int not_use
assert (chan >= 0 && chan < MAX_CHANS); assert (chan >= 0 && chan < MAX_CHANS);
assert (subchan >= 0 && subchan < MAX_SUBCHANS); assert (subchan >= 0 && subchan < MAX_SUBCHANS);
assert (slice >= 0 && slice < MAX_SLICERS);
/* /*
* Different state information for each channel. * Different state information for each channel / subchannel / slice.
*/ */
H = &hdlc_state[chan][subchan]; H = &hdlc_state[chan][subchan][slice];
/* /*
* Using NRZI encoding, * Using NRZI encoding,
@ -284,7 +284,7 @@ void hdlc_rec_bit (int chan, int subchan, int raw, int is_scrambled, int not_use
if ( ! H->data_detect) { if ( ! H->data_detect) {
H->data_detect = 1; H->data_detect = 1;
dcd_change (chan, subchan, 1); dcd_change (chan, subchan, slice, 1);
} }
} }
//else if (H->flag4_det == 0x7e000000) { //else if (H->flag4_det == 0x7e000000) {
@ -293,7 +293,7 @@ void hdlc_rec_bit (int chan, int subchan, int raw, int is_scrambled, int not_use
if ( ! H->data_detect) { if ( ! H->data_detect) {
H->data_detect = 1; H->data_detect = 1;
dcd_change (chan, subchan, 1); dcd_change (chan, subchan, slice, 1);
} }
} }
@ -308,7 +308,7 @@ void hdlc_rec_bit (int chan, int subchan, int raw, int is_scrambled, int not_use
if ( H->data_detect ) { if ( H->data_detect ) {
H->data_detect = 0; H->data_detect = 0;
dcd_change (chan, subchan, 0); dcd_change (chan, subchan, slice, 0);
} }
} }
@ -372,7 +372,7 @@ void hdlc_rec_bit (int chan, int subchan, int raw, int is_scrambled, int not_use
if (actual_fcs == expected_fcs) { if (actual_fcs == expected_fcs) {
alevel_t alevel = demod_get_audio_level (chan, subchan); alevel_t alevel = demod_get_audio_level (chan, subchan);
multi_modem_process_rec_frame (chan, subchan, H->frame_buf, H->frame_len - 2, alevel, RETRY_NONE); /* len-2 to remove FCS. */ multi_modem_process_rec_frame (chan, subchan, slice, H->frame_buf, H->frame_len - 2, alevel, RETRY_NONE); /* len-2 to remove FCS. */
} }
else { else {
@ -401,7 +401,8 @@ void hdlc_rec_bit (int chan, int subchan, int raw, int is_scrambled, int not_use
rrbb_set_audio_level (H->rrbb, alevel); rrbb_set_audio_level (H->rrbb, alevel);
hdlc_rec2_block (H->rrbb); hdlc_rec2_block (H->rrbb);
/* Now owned by someone else who will free it. */ /* Now owned by someone else who will free it. */
H->rrbb = rrbb_new (chan, subchan, is_scrambled, H->lfsr, H->prev_descram); /* Allocate a new one. */
H->rrbb = rrbb_new (chan, subchan, slice, is_scrambled, H->lfsr, H->prev_descram); /* Allocate a new one. */
} }
else { else {
rrbb_clear (H->rrbb, is_scrambled, H->lfsr, H->prev_descram); rrbb_clear (H->rrbb, is_scrambled, H->lfsr, H->prev_descram);
@ -514,86 +515,81 @@ void hdlc_rec_bit (int chan, int subchan, int raw, int is_scrambled, int not_use
* *
* Inputs: chan * Inputs: chan
* subchan * subchan
* slice
* *
* Returns: True if we are currently gathering bits. * Returns: True if we are currently gathering bits.
* In this case we want the PLL to have more inertia. * In this case we want the PLL to have more inertia.
* *
* Discussion: Originally I used the data carrier detect. * Discussion: This simply returns the data carrier detect state.
* Later, it seemed like the we should be using "olen>=0" instead. * A couple other variations were tried but turned out to
* * be slightly worse.
* Seems to make no difference for Track 1 and the original
* way was a hair better for Track 2.
* *
*--------------------------------------------------------------------*/ *--------------------------------------------------------------------*/
int hdlc_rec_gathering (int chan, int subchan) int hdlc_rec_gathering (int chan, int subchan, int slice)
{ {
assert (chan >= 0 && chan < MAX_CHANS); assert (chan >= 0 && chan < MAX_CHANS);
assert (subchan >= 0 && subchan < MAX_SUBCHANS); assert (subchan >= 0 && subchan < MAX_SUBCHANS);
assert (slice >= 0 && slice < MAX_SLICERS);
// Counts from Track 1 & Track 2 // Counts from Track 1 & Track 2
// data_detect 992 988 // data_detect 992 988
// olen>=0 992 985 // olen>=0 992 985
// OR-ed 992 985 // OR-ed 992 985
return ( hdlc_state[chan][subchan][slice].data_detect );
return ( hdlc_state[chan][subchan].data_detect );
//return ( hdlc_state[chan][subchan].olen >= 0);
//return ( hdlc_state[chan][subchan].data_detect || hdlc_state[chan][subchan].olen >= 0 );
} /* end hdlc_rec_gathering */ } /* end hdlc_rec_gathering */
/*------------------------------------------------------------------- /*-------------------------------------------------------------------
* *
* Name: dcd_change * Name: dcd_change
* *
* Purpose: Combine DCD states of all subchannels into an overall * Purpose: Combine DCD states of all subchannels/ into an overall
* state for the channel. * state for the channel.
* *
* Inputs: chan * Inputs: chan
* *
* subchan 0 to MAX_SUBCHANS-1 for HDLC. * subchan 0 to MAX_SUBCHANS-1 for HDLC.
* MAX_SUBCHANS for DTMF decoder. * SPECIAL CASE --> MAX_SUBCHANS for DTMF decoder.
*
* slice slicer number, 0 .. MAX_SLICERS - 1.
* *
* state 1 for active, 0 for not. * state 1 for active, 0 for not.
* *
* Returns: None. Use ??? to retrieve result. * Returns: None. Use hdlc_rec_data_detect_any to retrieve result.
* *
* Description: DCD for the channel is active if ANY of the subchannels * Description: DCD for the channel is active if ANY of the subchannels/slices
* is active. Update the DCD indicator. * are active. Update the DCD indicator.
* *
* version 1.3: Add DTMF detection into the final result. * version 1.3: Add DTMF detection into the final result.
* This is now called from dtmf.c too. * This is now called from dtmf.c too.
* *
*--------------------------------------------------------------------*/ *--------------------------------------------------------------------*/
void dcd_change (int chan, int subchan, int slice, int state)
void dcd_change (int chan, int subchan, int state)
{ {
int old, new; int old, new;
assert (chan >= 0 && chan < MAX_CHANS); assert (chan >= 0 && chan < MAX_CHANS);
assert (subchan >= 0 && subchan <= MAX_SUBCHANS); assert (subchan >= 0 && subchan <= MAX_SUBCHANS);
assert (slice >= 0 && slice < MAX_SLICERS);
assert (state == 0 || state == 1); assert (state == 0 || state == 1);
#if DEBUG3 #if DEBUG3
text_color_set(DW_COLOR_DEBUG); text_color_set(DW_COLOR_DEBUG);
dw_printf ("DCD %d.%d = %d \n", chan, subchan, state); dw_printf ("DCD %d.%d.%d = %d \n", chan, subchan, slice, state);
#endif #endif
old = hdlc_rec_data_detect_any(chan); old = hdlc_rec_data_detect_any(chan);
if (state) { if (state) {
composite_dcd[chan] |= (1 << subchan); composite_dcd[chan][subchan] |= (1 << slice);
} }
else { else {
composite_dcd[chan] &= ~ (1 << subchan); composite_dcd[chan][subchan] &= ~ (1 << slice);
} }
new = hdlc_rec_data_detect_any(chan); new = hdlc_rec_data_detect_any(chan);
@ -634,13 +630,17 @@ void dcd_change (int chan, int subchan, int state)
int hdlc_rec_data_detect_any (int chan) int hdlc_rec_data_detect_any (int chan)
{ {
int sc;
assert (chan >= 0 && chan < MAX_CHANS); assert (chan >= 0 && chan < MAX_CHANS);
return (composite_dcd[chan] != 0); for (sc = 0; sc < num_subchan[chan]; sc++) {
if (composite_dcd[chan][sc] != 0)
return (1);
}
return (0);
} /* end hdlc_rec_data_detect_any */ } /* end hdlc_rec_data_detect_any */
/* end hdlc_rec.c */ /* end hdlc_rec.c */

View File

@ -5,9 +5,7 @@
void hdlc_rec_init (struct audio_s *pa); void hdlc_rec_init (struct audio_s *pa);
void hdlc_rec_bit (int chan, int subchan, int slice, int raw, int is_scrambled, int descram_state);
void hdlc_rec_bit (int chan, int subchan, int raw, int is_scrambled, int descram_state);
/* Provided elsewhere to process a complete frame. */ /* Provided elsewhere to process a complete frame. */
@ -18,11 +16,10 @@ void hdlc_rec_bit (int chan, int subchan, int raw, int is_scrambled, int descram
/* Similar to, but not exactly the same as, data carrier detect. */ /* Similar to, but not exactly the same as, data carrier detect. */
/* We use this to influence the PLL inertia. */ /* We use this to influence the PLL inertia. */
int hdlc_rec_gathering (int chan, int subchan); int hdlc_rec_gathering (int chan, int subchan, int slice);
/* Transmit needs to know when someone else is transmitting. */ /* Transmit needs to know when someone else is transmitting. */
void dcd_change (int chan, int subchan, int state); void dcd_change (int chan, int subchan, int slice, int state);
int hdlc_rec_data_detect_any (int chan); int hdlc_rec_data_detect_any (int chan);

View File

@ -76,6 +76,12 @@
* It was necessary to retain more initial state information after * It was necessary to retain more initial state information after
* the start flag octet. * the start flag octet.
* *
* Version 1.3: Took out all of the "insert" and "remove" cases because they
* offer no benenfit.
*
* Took out the delayed processing and just do it realtime.
* Changed SWAP to INVERT because it is more descriptive.
*
*******************************************************************************/ *******************************************************************************/
#include <stdio.h> #include <stdio.h>
@ -152,11 +158,11 @@ struct hdlc_state_s {
}; };
#define MAX_RETRY_SWAP_BITS 24 /* Maximum number of contiguous bits to swap */
#define MAX_RETRY_REMOVE_SEPARATED_BITS 24 /* Maximum number of contiguous bits to remove */
static int try_decode (rrbb_t block, int chan, int subchan, alevel_t alevel, retry_conf_t retry_conf, int passall); static int try_decode (rrbb_t block, int chan, int subchan, int slice, alevel_t alevel, retry_conf_t retry_conf, int passall);
static int try_to_fix_quick_now (rrbb_t block, int chan, int subchan, alevel_t alevel);
static int try_to_fix_quick_now (rrbb_t block, int chan, int subchan, int slice, alevel_t alevel);
static int sanity_check (unsigned char *buf, int blen, retry_t bits_flipped, enum sanity_e sanity_test); static int sanity_check (unsigned char *buf, int blen, retry_t bits_flipped, enum sanity_e sanity_test);
@ -173,7 +179,7 @@ static int sanity_check (unsigned char *buf, int blen, retry_t bits_flipped, enu
* Level of effort to recover from * Level of effort to recover from
* a bad FCS on the frame. * a bad FCS on the frame.
* 0 = no effort * 0 = no effort
* 1 = try fixing a single bit * 1 = try inverting a single bit
* 2... = more techniques... * 2... = more techniques...
* *
* enum sanity_e sanity_test; * enum sanity_e sanity_test;
@ -222,6 +228,7 @@ void hdlc_rec2_block (rrbb_t block)
{ {
int chan = rrbb_get_chan(block); int chan = rrbb_get_chan(block);
int subchan = rrbb_get_subchan(block); int subchan = rrbb_get_subchan(block);
int slice = rrbb_get_slice(block);
alevel_t alevel = rrbb_get_audio_level(block); alevel_t alevel = rrbb_get_audio_level(block);
retry_t fix_bits = save_audio_config_p->achan[chan].fix_bits; retry_t fix_bits = save_audio_config_p->achan[chan].fix_bits;
int passall = save_audio_config_p->achan[chan].passall; int passall = save_audio_config_p->achan[chan].passall;
@ -245,11 +252,8 @@ void hdlc_rec2_block (rrbb_t block)
retry_cfg.retry = RETRY_NONE; retry_cfg.retry = RETRY_NONE;
retry_cfg.u_bits.contig.nr_bits = 0; retry_cfg.u_bits.contig.nr_bits = 0;
retry_cfg.u_bits.contig.bit_idx = 0; retry_cfg.u_bits.contig.bit_idx = 0;
/* Prepare the decoded bits in an array for faster processing
*(at cost of memory but 1 or 2 kbytes is nothing compared to processing time ) */
rrbb_compute_bits(block);
ok = try_decode (block, chan, subchan, alevel, retry_cfg, passall & (fix_bits == RETRY_NONE));
ok = try_decode (block, chan, subchan, slice, alevel, retry_cfg, passall & (fix_bits == RETRY_NONE));
if (ok) { if (ok) {
#if DEBUG #if DEBUG
text_color_set(DW_COLOR_INFO); text_color_set(DW_COLOR_INFO);
@ -261,29 +265,19 @@ void hdlc_rec2_block (rrbb_t block)
/* /*
* Not successful with frame in orginal form. * Not successful with frame in orginal form.
* Try the quick techniques with time proportional to the frame length. * See if we can "fix" it.
*/ */
if (try_to_fix_quick_now (block, chan, subchan, alevel)) { if (try_to_fix_quick_now (block, chan, subchan, slice, alevel)) {
rrbb_delete (block); rrbb_delete (block);
return; return;
} }
/*
* Not successful with the quick fix up attempts.
* Do we want to try the more aggressive techniques where processing
* time is proportional to the square of length?
* Rather than doing it now, we throw it in a queue for processing
* by a different thread.
*/
if (fix_bits >= RETRY_SWAP_TWO_SEP) { if (passall) {
rdq_append (block);
}
else if (passall) {
/* Exhausted all desired fix up attempts. */ /* Exhausted all desired fix up attempts. */
/* Let thru even with bad CRC. Of course, it still */ /* Let thru even with bad CRC. Of course, it still */
/* needs to be a minimum number of whole octets. */ /* needs to be a minimum number of whole octets. */
ok = try_decode (block, chan, subchan, alevel, retry_cfg, 1); ok = try_decode (block, chan, subchan, slice, alevel, retry_cfg, 1);
rrbb_delete (block); rrbb_delete (block);
} }
else { else {
@ -307,7 +301,7 @@ void hdlc_rec2_block (rrbb_t block)
* Global In: configuration fix_bits - Maximum level of fix up to attempt. * Global In: configuration fix_bits - Maximum level of fix up to attempt.
* *
* RETRY_NONE (0) - Don't try any. * RETRY_NONE (0) - Don't try any.
* RETRY_SWAP_SINGLE (1) - Try inverting single bits. * RETRY_INVERT_SINGLE (1) - Try inverting single bits.
* etc. * etc.
* *
* configuration passall - Let it thru with bad CRC after exhausting * configuration passall - Let it thru with bad CRC after exhausting
@ -318,15 +312,19 @@ void hdlc_rec2_block (rrbb_t block)
* processing step. * processing step.
* 0 for failure. Caller might continue with more aggressive attempts. * 0 for failure. Caller might continue with more aggressive attempts.
* *
* Description: Some of the attempted fix up techniques are quick. * Original: Some of the attempted fix up techniques are quick.
* We will attempt them immediately after receiving the frame. * We will attempt them immediately after receiving the frame.
* Others, that take time order N**2, will be done in a later section. * Others, that take time order N**2, will be done in a later section.
* *
* Version 1.2: Now works properly for G3RUH type scrambling. * Version 1.2: Now works properly for G3RUH type scrambling.
* *
* Version 1.3: Removed the extra cases that didn't help.
* The separated bit case is now handled immediately instead of
* being thrown in a queue for later processing.
*
***********************************************************************************/ ***********************************************************************************/
static int try_to_fix_quick_now (rrbb_t block, int chan, int subchan, alevel_t alevel) static int try_to_fix_quick_now (rrbb_t block, int chan, int subchan, int slice, alevel_t alevel)
{ {
int ok; int ok;
int len, i,j; int len, i,j;
@ -340,9 +338,9 @@ static int try_to_fix_quick_now (rrbb_t block, int chan, int subchan, alevel_t a
/* Will modify only contiguous bits*/ /* Will modify only contiguous bits*/
retry_cfg.mode = RETRY_MODE_CONTIGUOUS; retry_cfg.mode = RETRY_MODE_CONTIGUOUS;
/* /*
* Try fixing one bit. * Try inverting one bit.
*/ */
if (fix_bits < RETRY_SWAP_SINGLE) { if (fix_bits < RETRY_INVERT_SINGLE) {
/* Stop before single bit fix up. */ /* Stop before single bit fix up. */
@ -350,13 +348,13 @@ static int try_to_fix_quick_now (rrbb_t block, int chan, int subchan, alevel_t a
} }
/* Try to swap one bit */ /* Try to swap one bit */
retry_cfg.type = RETRY_TYPE_SWAP; retry_cfg.type = RETRY_TYPE_SWAP;
retry_cfg.retry = RETRY_SWAP_SINGLE; retry_cfg.retry = RETRY_INVERT_SINGLE;
retry_cfg.u_bits.contig.nr_bits = 1; retry_cfg.u_bits.contig.nr_bits = 1;
for (i=0; i<len; i++) { for (i=0; i<len; i++) {
/* Set the index of the bit to swap */ /* Set the index of the bit to swap */
retry_cfg.u_bits.contig.bit_idx = i; retry_cfg.u_bits.contig.bit_idx = i;
ok = try_decode (block, chan, subchan, alevel, retry_cfg, 0); ok = try_decode (block, chan, subchan, slice, alevel, retry_cfg, 0);
if (ok) { if (ok) {
#if DEBUG #if DEBUG
text_color_set(DW_COLOR_ERROR); text_color_set(DW_COLOR_ERROR);
@ -367,19 +365,19 @@ static int try_to_fix_quick_now (rrbb_t block, int chan, int subchan, alevel_t a
} }
/* /*
* Try fixing two adjacent bits. * Try inverting two adjacent bits.
*/ */
if (fix_bits < RETRY_SWAP_DOUBLE) { if (fix_bits < RETRY_INVERT_DOUBLE) {
return 0; return 0;
} }
/* Try to swap two contiguous bits */ /* Try to swap two contiguous bits */
retry_cfg.retry = RETRY_SWAP_DOUBLE; retry_cfg.retry = RETRY_INVERT_DOUBLE;
retry_cfg.u_bits.contig.nr_bits = 2; retry_cfg.u_bits.contig.nr_bits = 2;
for (i=0; i<len-1; i++) { for (i=0; i<len-1; i++) {
retry_cfg.u_bits.contig.bit_idx = i; retry_cfg.u_bits.contig.bit_idx = i;
ok = try_decode (block, chan, subchan, alevel, retry_cfg, 0); ok = try_decode (block, chan, subchan, slice, alevel, retry_cfg, 0);
if (ok) { if (ok) {
#if DEBUG #if DEBUG
text_color_set(DW_COLOR_ERROR); text_color_set(DW_COLOR_ERROR);
@ -390,18 +388,18 @@ static int try_to_fix_quick_now (rrbb_t block, int chan, int subchan, alevel_t a
} }
/* /*
* Try fixing adjacent three bits. * Try inverting adjacent three bits.
*/ */
if (fix_bits < RETRY_SWAP_TRIPLE) { if (fix_bits < RETRY_INVERT_TRIPLE) {
return 0; return 0;
} }
/* Try to swap three contiguous bits */ /* Try to swap three contiguous bits */
retry_cfg.retry = RETRY_SWAP_TRIPLE; retry_cfg.retry = RETRY_INVERT_TRIPLE;
retry_cfg.u_bits.contig.nr_bits = 3; retry_cfg.u_bits.contig.nr_bits = 3;
for (i=0; i<len-2; i++) { for (i=0; i<len-2; i++) {
retry_cfg.u_bits.contig.bit_idx = i; retry_cfg.u_bits.contig.bit_idx = i;
ok = try_decode (block, chan, subchan, alevel, retry_cfg, 0); ok = try_decode (block, chan, subchan, slice, alevel, retry_cfg, 0);
if (ok) { if (ok) {
#if DEBUG #if DEBUG
text_color_set(DW_COLOR_ERROR); text_color_set(DW_COLOR_ERROR);
@ -411,204 +409,20 @@ static int try_to_fix_quick_now (rrbb_t block, int chan, int subchan, alevel_t a
} }
} }
if (fix_bits < RETRY_REMOVE_SINGLE) {
return 0;
}
/*
* Try removing one bit.
*/
retry_cfg.type = RETRY_TYPE_REMOVE;
retry_cfg.retry = RETRY_REMOVE_SINGLE;
retry_cfg.u_bits.contig.nr_bits = 1;
for (i=0; i<len; i++) {
retry_cfg.u_bits.contig.bit_idx = i;
ok = try_decode (block, chan, subchan, alevel, retry_cfg, 0);
if (ok) {
#if DEBUG
text_color_set(DW_COLOR_ERROR);
dw_printf ("*** Success by removing SINGLE bit %d of %d ***\n", i, len);
#endif
return 1;
}
}
if (fix_bits < RETRY_REMOVE_DOUBLE) {
return 0;
}
/*
* Try removing two contiguous bits.
*/
#if DEBUG
text_color_set(DW_COLOR_ERROR);
dw_printf ("*** Try removing DOUBLE bits *** for %d bits\n", len);
#endif
retry_cfg.retry = RETRY_REMOVE_DOUBLE;
retry_cfg.u_bits.contig.nr_bits = 2;
for (i=0; i<len-1; i++) {
retry_cfg.u_bits.contig.bit_idx = i;
ok = try_decode (block, chan, subchan, alevel, retry_cfg, 0);
if (ok) {
#if DEBUG
text_color_set(DW_COLOR_ERROR);
dw_printf ("*** Success by removing DOUBLE bits %d of %d ***\n", i, len);
#endif
return 1;
}
}
if (fix_bits < RETRY_REMOVE_TRIPLE) {
return 0;
}
/*
* Try removing three contiguous bits.
*/
#if DEBUG
text_color_set(DW_COLOR_ERROR);
dw_printf ("*** Try removing TRIPLE bits *** for %d bits\n", len);
#endif
retry_cfg.retry = RETRY_REMOVE_TRIPLE;
retry_cfg.u_bits.contig.nr_bits = 3;
for (i=0; i<len-2; i++) {
retry_cfg.u_bits.contig.bit_idx = i;
ok = try_decode (block, chan, subchan, alevel, retry_cfg, 0);
if (ok) {
#if DEBUG
text_color_set(DW_COLOR_ERROR);
dw_printf ("*** Success by removing TRIPLE bits %d of %d ***\n", i, len);
#endif
return 1;
}
}
if (fix_bits < RETRY_INSERT_SINGLE) {
return 0;
}
/*
* Try inserting one bit (two values possibles for this inserted bit).
*/
#if DEBUG
text_color_set(DW_COLOR_ERROR);
dw_printf ("*** Try inserting SINGLE bit *** for %d bits\n", len);
#endif
retry_cfg.type = RETRY_TYPE_INSERT;
retry_cfg.retry = RETRY_INSERT_SINGLE;
retry_cfg.u_bits.contig.nr_bits = 1;
for (i=0; i<len; i++) {
retry_cfg.u_bits.contig.bit_idx = i;
retry_cfg.insert_value=0;
ok = try_decode (block, chan, subchan, alevel, retry_cfg, 0);
if (!ok) {
retry_cfg.insert_value=1;
ok = try_decode (block, chan, subchan, alevel, retry_cfg, 0);
}
if (ok) {
#if DEBUG
text_color_set(DW_COLOR_ERROR);
dw_printf ("*** Success by inserting SINGLE bit %d of %d ***\n", i, len);
#endif
return 1;
}
}
if (fix_bits < RETRY_INSERT_DOUBLE) {
return 0;
}
/*
* Try inserting two contiguous bits (4 possible values for two bits).
*/
#if DEBUG
text_color_set(DW_COLOR_ERROR);
dw_printf ("*** Try inserting DOUBLE bits *** for %d bits\n", len);
#endif
retry_cfg.retry = RETRY_INSERT_DOUBLE;
retry_cfg.u_bits.contig.nr_bits = 2;
for (i=0; i<len-1; i++) {
retry_cfg.u_bits.contig.bit_idx = i;
for (j=0;j<4;j++) {
retry_cfg.insert_value=j;
ok = try_decode (block, chan, subchan, alevel, retry_cfg, 0);
if (ok) {
#if DEBUG
text_color_set(DW_COLOR_ERROR);
dw_printf ("*** Success by inserting DOUBLE bits %d of %d ***\n", i, len);
#endif
return 1;
}
}
}
return 0;
}
/***********************************************************************************
*
* Name: hdlc_rec2_try_to_fix_later
*
* Purpose: Attempt some more time-consuming techniques.
* Rather than trying these immediately, the information is
* put into a queue and processed by another thread.
*
* Inputs: block - Stream of bits that might be a frame.
* chan - Radio channel from which it was received.
* subchan - Which demodulator when more than one per channel.
* alevel - Audio level for later reporting.
*
* Global In: configuration fix_bits - Maximum level of fix up to attempt.
*
* RETRY_NONE (0) - Don't try any.
* RETRY_SWAP_SINGLE (1) - Try inverting single bits.
* etc.
*
* configuration passall - Let it thru with bad CRC after exhausting
* all fixup attempts.
*
*
* Returns: 1 for success. "try_decode" has passed the result along to the
* processing step.
* 0 for failure. Caller might try again if "passall" option specified.
*
***********************************************************************************/
int hdlc_rec2_try_to_fix_later (rrbb_t block, int chan, int subchan, alevel_t alevel)
{
int ok;
int len, i, j;
retry_t fix_bits = save_audio_config_p->achan[chan].fix_bits;
int passall = save_audio_config_p->achan[chan].passall;
#if DEBUG_LATER
double tstart, tend;
#endif
retry_conf_t retry_cfg;
len = rrbb_get_len(block);
if (fix_bits < RETRY_SWAP_TWO_SEP) {
goto failure;
}
retry_cfg.mode = RETRY_MODE_SEPARATED;
/* /*
* Two non-adjacent ("separated") single bits. * Two non-adjacent ("separated") single bits.
* It chews up a lot of CPU time. Test takes 4 times longer to run. * It chews up a lot of CPU time. Usual test takes 4 times longer to run.
* *
* Ran up to xx seconds (TODO check again with optimized code) seconds for 1040 bits before giving up .
* Processing time is order N squared so time goes up rapidly with larger frames. * Processing time is order N squared so time goes up rapidly with larger frames.
*/ */
if (fix_bits < RETRY_INVERT_TWO_SEP) {
return 0;
}
retry_cfg.mode = RETRY_MODE_SEPARATED;
retry_cfg.type = RETRY_TYPE_SWAP; retry_cfg.type = RETRY_TYPE_SWAP;
retry_cfg.retry = RETRY_SWAP_TWO_SEP; retry_cfg.retry = RETRY_INVERT_TWO_SEP;
retry_cfg.u_bits.sep.bit_idx_c = -1; retry_cfg.u_bits.sep.bit_idx_c = -1;
#ifdef DEBUG_LATER #ifdef DEBUG_LATER
@ -623,7 +437,7 @@ int hdlc_rec2_try_to_fix_later (rrbb_t block, int chan, int subchan, alevel_t al
ok = 0; ok = 0;
for (j=i+2; j<len; j++) { for (j=i+2; j<len; j++) {
retry_cfg.u_bits.sep.bit_idx_b = j; retry_cfg.u_bits.sep.bit_idx_b = j;
ok = try_decode (block, chan, subchan, alevel, retry_cfg, 0); ok = try_decode (block, chan, subchan, slice, alevel, retry_cfg, 0);
if (ok) { if (ok) {
break; break;
} }
@ -637,128 +451,28 @@ int hdlc_rec2_try_to_fix_later (rrbb_t block, int chan, int subchan, alevel_t al
return (1); return (1);
} }
} }
return 0;
}
// TODO: Remove this. but first figure out what to do in atest.c
int hdlc_rec2_try_to_fix_later (rrbb_t block, int chan, int subchan, int slice, alevel_t alevel)
{
int ok;
int len, i, j;
retry_t fix_bits = save_audio_config_p->achan[chan].fix_bits;
int passall = save_audio_config_p->achan[chan].passall;
#if DEBUG_LATER #if DEBUG_LATER
tend = dtime_now(); double tstart, tend;
text_color_set(DW_COLOR_ERROR);
dw_printf ("*** No luck flipping TWO SEPARATED bits of %d *** %.3f sec.\n", len, tend-tstart);
#endif
if (fix_bits < RETRY_SWAP_MANY) {
goto failure;
}
/* Try to swap many contiguous bits */
retry_cfg.mode = RETRY_MODE_CONTIGUOUS;
retry_cfg.type = RETRY_TYPE_SWAP;
retry_cfg.retry = RETRY_SWAP_MANY;
#ifdef DEBUG_LATER
tstart = dtime_now();
dw_printf ("*** Try swapping many BITS %d bits\n", len);
#endif #endif
retry_conf_t retry_cfg;
len = rrbb_get_len(block); len = rrbb_get_len(block);
for (i=0; i<len; i++) {
for (j=1; j<len-i && j < MAX_RETRY_SWAP_BITS;j++) {
retry_cfg.u_bits.contig.bit_idx = i;
retry_cfg.u_bits.contig.nr_bits = j;
// dw_printf ("*** Trying swapping %d bits starting at %d of %d ***\n", j,i, len);
ok = try_decode (block, chan, subchan, alevel, retry_cfg, 0);
if (ok) {
#if DEBUG
text_color_set(DW_COLOR_ERROR);
dw_printf ("*** Success by swapping %d bits starting at %d of %d ***\n", j,i, len);
#endif
return (1);
}
}
}
#if DEBUG_LATER
tend = dtime_now();
text_color_set(DW_COLOR_ERROR);
dw_printf ("*** No luck swapping many bits for len %d in %.3f sec.\n",len, tend-tstart);
#endif
if (fix_bits < RETRY_REMOVE_MANY) {
goto failure;
}
/* Try to remove many contiguous bits */
retry_cfg.type = RETRY_TYPE_REMOVE;
retry_cfg.retry = RETRY_REMOVE_MANY;
#ifdef DEBUG_LATER
tstart = dtime_now();
dw_printf ("*** Trying removing many bits for len\n", len);
#endif
len = rrbb_get_len(block);
for (i=0; i<2; i++) {
for (j=1; j<len-i && j<len/2;j++) {
retry_cfg.u_bits.contig.bit_idx = i;
retry_cfg.u_bits.contig.nr_bits = j;
#ifdef DEBUG
dw_printf ("*** Trying removing %d bits starting at %d of %d ***\n", j,i, len);
#endif
ok = try_decode (block, chan, subchan, alevel, retry_cfg, 0);
if (ok) {
#if DEBUG
text_color_set(DW_COLOR_ERROR);
dw_printf ("*** Success by removing %d bits starting at %d of %d ***\n", j,i, len);
#endif
return (1);
}
}
}
#if DEBUG_LATER
tend = dtime_now();
text_color_set(DW_COLOR_ERROR);
dw_printf ("*** No luck removing many bits for len %d *** in %.3f sec.\n", len, tend-tstart);
#endif
if (fix_bits < RETRY_REMOVE_TWO_SEP) {
goto failure;
}
/*
* Try to remove Two non-adjacent ("separated") single bits.
*/
retry_cfg.mode = RETRY_MODE_SEPARATED;
retry_cfg.type = RETRY_TYPE_REMOVE;
retry_cfg.retry = RETRY_REMOVE_TWO_SEP;
retry_cfg.u_bits.sep.bit_idx_c = -1;
#if DEBUG_LATER
tstart = dtime_now();
dw_printf ("*** Try removing TWO SEPARATED BITS %d bits\n", len);
#endif
len = rrbb_get_len(block);
for (i=0; i<len-2; i++) {
retry_cfg.u_bits.sep.bit_idx_a = i;
int j;
ok = 0;
for (j=i+2; j<len && j - i < MAX_RETRY_REMOVE_SEPARATED_BITS; j++) {
retry_cfg.u_bits.sep.bit_idx_b = j;
ok = try_decode (block, chan, subchan, alevel, retry_cfg, 0);
if (ok) {
break;
}
}
if (ok) {
#if DEBUG_LATER
text_color_set(DW_COLOR_ERROR);
dw_printf ("*** Success by removing TWO SEPARATED bits %d and %d of %d \n", i, j, len);
#endif
return (1);
}
}
#if DEBUG_LATER
tend = dtime_now();
text_color_set(DW_COLOR_ERROR);
dw_printf ("*** No luck removing TWO SEPARATED bits of %d *** %.3f sec.\n", len, tend-tstart);
#endif
failure:
/* /*
* All fix up attempts have failed. * All fix up attempts have failed.
@ -769,11 +483,10 @@ failure:
retry_cfg.type = RETRY_TYPE_NONE; retry_cfg.type = RETRY_TYPE_NONE;
retry_cfg.mode = RETRY_MODE_CONTIGUOUS; retry_cfg.mode = RETRY_MODE_CONTIGUOUS;
retry_cfg.retry = RETRY_NONE; retry_cfg.retry = RETRY_NONE;
retry_cfg.u_bits.contig.nr_bits = 0; retry_cfg.u_bits.contig.nr_bits = 0;
retry_cfg.u_bits.contig.bit_idx = 0; retry_cfg.u_bits.contig.bit_idx = 0;
ok = try_decode (block, chan, subchan, slice, alevel, retry_cfg, passall);
ok = try_decode (block, chan, subchan, alevel, retry_cfg, passall);
return (ok); return (ok);
} }
@ -782,6 +495,7 @@ failure:
} /* end hdlc_rec2_try_to_fix_later */ } /* end hdlc_rec2_try_to_fix_later */
/* /*
* Check if the specified index of bit has been modified with the current type of configuration * Check if the specified index of bit has been modified with the current type of configuration
* Provide a specific implementation for contiguous mode to optimize number of tests done in the loop * Provide a specific implementation for contiguous mode to optimize number of tests done in the loop
@ -811,14 +525,7 @@ inline static char is_sep_bit_modified(int bit_idx, retry_conf_t retry_conf) {
return 0; return 0;
} }
/*
* Get the bit value from a precalculated array to optimize access time in the loop
*/
inline static unsigned int get_bit (const rrbb_t b,const unsigned int ind)
{
return b->computed_data[ind];
}
/*********************************************************************************** /***********************************************************************************
* *
@ -835,20 +542,12 @@ inline static unsigned int get_bit (const rrbb_t b,const unsigned int ind)
* retry_conf - Controls changes that will be attempted to get a good CRC. * retry_conf - Controls changes that will be attempted to get a good CRC.
* *
* retry: * retry:
* Level of effort to recover from A bad FCS on the frame. * Level of effort to recover from a bad FCS on the frame.
* RETRY_NONE=0, * RETRY_NONE = 0
* RETRY_SWAP_SINGLE=1, * RETRY_INVERT_SINGLE = 1
* RETRY_SWAP_DOUBLE=2, * RETRY_INVERT_DOUBLE = 2
* RETRY_SWAP_TRIPLE=3, * RETRY_INVERT_TRIPLE = 3
* RETRY_REMOVE_SINGLE=4, * RETRY_INVERT_TWO_SEP = 4
* RETRY_REMOVE_DOUBLE=5,
* RETRY_REMOVE_TRIPLE=6,
* RETRY_INSERT_SINGLE=7,
* RETRY_INSERT_DOUBLE=8,
* RETRY_SWAP_TWO_SEP=9,
* RETRY_SWAP_MANY=10,
* RETRY_REMOVE_MANY=11,
* RETRY_REMOVE_TWO_SEP=12,
* *
* mode: RETRY_MODE_CONTIGUOUS - change adjacent bits. * mode: RETRY_MODE_CONTIGUOUS - change adjacent bits.
* contig.bit_idx - first bit position * contig.bit_idx - first bit position
@ -861,8 +560,6 @@ inline static unsigned int get_bit (const rrbb_t b,const unsigned int ind)
* *
* type: RETRY_TYPE_NONE - Make no changes. * type: RETRY_TYPE_NONE - Make no changes.
* RETRY_TYPE_SWAP - Try inverting. * RETRY_TYPE_SWAP - Try inverting.
* RETRY_TYPE_REMOVE - Try removing.
* RETRY_TYPE_INSERT - Try inserting.
* *
* passall - All it thru even with bad CRC. * passall - All it thru even with bad CRC.
* Valid only when no changes make. i.e. * Valid only when no changes make. i.e.
@ -873,8 +570,7 @@ inline static unsigned int get_bit (const rrbb_t b,const unsigned int ind)
* *
***********************************************************************************/ ***********************************************************************************/
static int try_decode (rrbb_t block, int chan, int subchan, int slice, alevel_t alevel, retry_conf_t retry_conf, int passall)
static int try_decode (rrbb_t block, int chan, int subchan, alevel_t alevel, retry_conf_t retry_conf, int passall)
{ {
struct hdlc_state_s H; struct hdlc_state_s H;
int blen; /* Block length in bits. */ int blen; /* Block length in bits. */
@ -891,7 +587,7 @@ static int try_decode (rrbb_t block, int chan, int subchan, alevel_t alevel, ret
H.is_scrambled = rrbb_get_is_scrambled (block); H.is_scrambled = rrbb_get_is_scrambled (block);
H.prev_descram = rrbb_get_prev_descram (block); H.prev_descram = rrbb_get_prev_descram (block);
H.lfsr = rrbb_get_descram_state (block); H.lfsr = rrbb_get_descram_state (block);
H.prev_raw = get_bit (block, 0); /* Actually last bit of the */ H.prev_raw = rrbb_get_bit (block, 0); /* Actually last bit of the */
/* opening flag so we can derive the */ /* opening flag so we can derive the */
/* first data bit. */ /* first data bit. */
@ -911,9 +607,6 @@ static int try_decode (rrbb_t block, int chan, int subchan, alevel_t alevel, ret
H.frame_len = 0; H.frame_len = 0;
blen = rrbb_get_len(block); blen = rrbb_get_len(block);
/* Prepare space for the inserted bits in contiguous mode (separated mode for insert is not supported yet) */
if (retry_conf.type == RETRY_TYPE_INSERT && retry_conf.mode == RETRY_MODE_CONTIGUOUS)
blen+=retry_conf.u_bits.contig.nr_bits;
#if DEBUGx #if DEBUGx
text_color_set(DW_COLOR_DEBUG); text_color_set(DW_COLOR_DEBUG);
@ -922,43 +615,16 @@ static int try_decode (rrbb_t block, int chan, int subchan, alevel_t alevel, ret
#endif #endif
for (i=1; i<blen; i++) { for (i=1; i<blen; i++) {
/* Get the value for the current bit */ /* Get the value for the current bit */
raw = get_bit (block, i); raw = rrbb_get_bit (block, i);
/* If swap two sep mode , swap the bit if needed */ /* If swap two sep mode , swap the bit if needed */
if (retry_conf_retry == RETRY_SWAP_TWO_SEP) { if (retry_conf_retry == RETRY_INVERT_TWO_SEP) {
if (is_sep_bit_modified(i, retry_conf)) if (is_sep_bit_modified(i, retry_conf))
raw = ! raw; raw = ! raw;
/* Else if remove two sep bits mode , remove the bit if needed */
} else if (retry_conf_retry == RETRY_REMOVE_TWO_SEP) {
if (is_sep_bit_modified(i, retry_conf))
//Remove (ignore) this bit from the buffer!
continue;
} }
/* Else handle all the others contiguous modes */ /* Else handle all the others contiguous modes */
else if (retry_conf_mode == RETRY_MODE_CONTIGUOUS) { else if (retry_conf_mode == RETRY_MODE_CONTIGUOUS) {
/* If contiguous remove, ignore this bit from the buffer */
if (retry_conf_type == RETRY_TYPE_REMOVE) { if (retry_conf_type == RETRY_TYPE_SWAP) {
if ( is_contig_bit_modified(i, retry_conf))
//Remove (ignore) this bit from the buffer!
continue;
}
/* If insert bits mode */
else if (retry_conf_type == RETRY_TYPE_INSERT) {
int nr_bits = retry_conf.u_bits.contig.nr_bits;
int bit_idx = retry_conf.u_bits.contig.bit_idx;
/* If bit is after the index to insert, use the existing bit value (but shifted from the array) */
if (i >= bit_idx + nr_bits)
raw = get_bit (block, i-nr_bits);
/* Else if this is a bit to insert, calculate the value of the bit from insert_value */
else if (is_contig_bit_modified(i, retry_conf)) {
raw = (retry_conf.insert_value >> (i-bit_idx)) & 1;
/* dw_printf ("raw is %d for i %d bit_idx %d insert_value %d\n",
raw, i, bit_idx, retry_conf.insert_value);*/
/* Else use the original bit value from the buffer */
} else {
/* Already set before */
}
/* If in swap mode */
} else if (retry_conf_type == RETRY_TYPE_SWAP) {
/* If this is the bit to swap */ /* If this is the bit to swap */
if (is_contig_bit_modified(i, retry_conf)) if (is_contig_bit_modified(i, retry_conf))
raw = ! raw; raw = ! raw;
@ -1097,8 +763,7 @@ static int try_decode (rrbb_t block, int chan, int subchan, alevel_t alevel, ret
assert (rrbb_get_chan(block) == chan); assert (rrbb_get_chan(block) == chan);
assert (rrbb_get_subchan(block) == subchan); assert (rrbb_get_subchan(block) == subchan);
multi_modem_process_rec_frame (chan, subchan, slice, H.frame_buf, H.frame_len - 2, alevel, retry_conf.retry); /* len-2 to remove FCS. */
multi_modem_process_rec_frame (chan, subchan, H.frame_buf, H.frame_len - 2, alevel, retry_conf.retry); /* len-2 to remove FCS. */
return 1; /* success */ return 1; /* success */
} else if (passall) { } else if (passall) {
@ -1107,7 +772,7 @@ static int try_decode (rrbb_t block, int chan, int subchan, alevel_t alevel, ret
//text_color_set(DW_COLOR_ERROR); //text_color_set(DW_COLOR_ERROR);
//dw_printf ("ATTEMPTING PASSALL PROCESSING\n"); //dw_printf ("ATTEMPTING PASSALL PROCESSING\n");
multi_modem_process_rec_frame (chan, subchan, H.frame_buf, H.frame_len - 2, alevel, RETRY_MAX); /* len-2 to remove FCS. */ multi_modem_process_rec_frame (chan, subchan, slice, H.frame_buf, H.frame_len - 2, alevel, RETRY_MAX); /* len-2 to remove FCS. */
return 1; /* success */ return 1; /* success */
} }
else { else {
@ -1186,7 +851,9 @@ end:
* *
* Returns: 1 if it passes the sanity test. * Returns: 1 if it passes the sanity test.
* *
* Description: * Description: This is NOT a validity check.
* We don't know if modifying the frame fixed the problem or made it worse.
* We can only test if it looks reasonable.
* *
***********************************************************************************/ ***********************************************************************************/

View File

@ -17,9 +17,7 @@ typedef enum retry_mode_e {
typedef enum retry_type_e { typedef enum retry_type_e {
RETRY_TYPE_NONE=0, RETRY_TYPE_NONE=0,
RETRY_TYPE_SWAP=1, RETRY_TYPE_SWAP=1 } retry_type_t;
RETRY_TYPE_REMOVE=2,
RETRY_TYPE_INSERT=3} retry_type_t;
typedef struct retry_conf_s { typedef struct retry_conf_s {
retry_t retry; retry_t retry;
@ -52,15 +50,7 @@ static const char * retry_text[] = {
"SINGLE", "SINGLE",
"DOUBLE", "DOUBLE",
"TRIPLE", "TRIPLE",
"REMOVE_SINGLE",
"REMOVE_DOUBLE",
"REMOVE_TRIPLE",
"INSERT_SINGLE",
"INSERT_DOUBLE",
"TWO_SEP", "TWO_SEP",
"MANY",
"REMOVE_MANY",
"REMOVE_SEP",
"PASSALL" }; "PASSALL" };
#endif #endif
@ -68,11 +58,10 @@ void hdlc_rec2_init (struct audio_s *audio_config_p);
void hdlc_rec2_block (rrbb_t block); void hdlc_rec2_block (rrbb_t block);
int hdlc_rec2_try_to_fix_later (rrbb_t block, int chan, int subchan, alevel_t alevel); int hdlc_rec2_try_to_fix_later (rrbb_t block, int chan, int subchan, int slice, alevel_t alevel);
/* Provided by the top level application to process a complete frame. */ /* Provided by the top level application to process a complete frame. */
void app_process_rec_packet (int chan, int subchan, packet_t pp, alevel_t level, retry_t retries, char *spectrum); void app_process_rec_packet (int chan, int subchan, int slice, packet_t pp, alevel_t level, retry_t retries, char *spectrum);
#endif #endif

View File

@ -98,23 +98,15 @@ static struct audio_s *save_audio_config_p;
// Candidates for further processing. // Candidates for further processing.
static struct { static struct {
packet_t packet_p; packet_t packet_p;
alevel_t alevel; alevel_t alevel;
retry_t retries; retry_t retries;
int age; int age;
unsigned int crc; unsigned int crc;
int score; int score;
} candidate[MAX_CHANS][MAX_SUBCHANS][MAX_SLICERS];
} candidate[MAX_CHANS][MAX_SUBCHANS];
#define MAX_STORED_CRC 256
typedef struct crc_s {
struct crc_s* nextp; /* Next pointer to maintain a queue. */
unsigned int crc;
} *crc_t;
static crc_t crc_queue_of_last_to_app[MAX_CHANS];
#define PROCESS_AFTER_BITS 2 #define PROCESS_AFTER_BITS 2
@ -163,12 +155,14 @@ void multi_modem_init (struct audio_s *pa)
save_audio_config_p->achan[chan].baud = DEFAULT_BAUD; save_audio_config_p->achan[chan].baud = DEFAULT_BAUD;
} }
process_age[chan] = PROCESS_AFTER_BITS * save_audio_config_p->adev[ACHAN2ADEV(chan)].samples_per_sec / save_audio_config_p->achan[chan].baud; process_age[chan] = PROCESS_AFTER_BITS * save_audio_config_p->adev[ACHAN2ADEV(chan)].samples_per_sec / save_audio_config_p->achan[chan].baud;
crc_queue_of_last_to_app[chan] = NULL; //crc_queue_of_last_to_app[chan] = NULL;
} }
} }
} }
#if 0
//Add a crc to the end of the queue and returns the numbers of CRC stored in the queue //Add a crc to the end of the queue and returns the numbers of CRC stored in the queue
int crc_queue_append (unsigned int crc, unsigned int chan) { int crc_queue_append (unsigned int crc, unsigned int chan) {
crc_t plast; crc_t plast;
@ -257,6 +251,7 @@ unsigned char is_crc_in_queue(unsigned int chan, unsigned int crc) {
} }
return 0; return 0;
} }
#endif /* if 0 */
/*------------------------------------------------------------------------------ /*------------------------------------------------------------------------------
* *
@ -282,6 +277,11 @@ unsigned char is_crc_in_queue(unsigned int chan, unsigned int crc) {
* We now have a separate variable, num_demod, which could be 1 * We now have a separate variable, num_demod, which could be 1
* while num_subchan is larger. * while num_subchan is larger.
* *
* Version 1.3: Go back to num_subchan with single meaning of number of demodulators.
* We now have separate independent variable, num_slicers, for the
* mark/space imbalance compensation.
* num_demod, while probably more descriptive, should not exist anymore.
*
*------------------------------------------------------------------------------*/ *------------------------------------------------------------------------------*/
@ -290,25 +290,48 @@ void multi_modem_process_sample (int chan, int audio_sample)
{ {
int d; int d;
int subchan; int subchan;
static int i = 0; /* for interleaving among multiple demodulators. */
// TODO: temp debug, remove this.
assert (save_audio_config_p->achan[chan].num_subchan > 0 && save_audio_config_p->achan[chan].num_subchan <= MAX_SUBCHANS);
assert (save_audio_config_p->achan[chan].num_slicers > 0 && save_audio_config_p->achan[chan].num_slicers <= MAX_SLICERS);
/* Formerly one loop. */ /* Formerly one loop. */
/* 1.2: We can feed one demodulator but end up with multiple outputs. */ /* 1.2: We can feed one demodulator but end up with multiple outputs. */
for (d = 0; d < save_audio_config_p->achan[chan].num_demod; d++) { if (save_audio_config_p->achan[chan].interleave > 1) {
demod_process_sample(chan, d, audio_sample); // TODO: temp debug, remove this.
assert (save_audio_config_p->achan[chan].interleave == save_audio_config_p->achan[chan].num_subchan);
demod_process_sample(chan, i, audio_sample);
i++;
if (i >= save_audio_config_p->achan[chan].interleave) i = 0;
}
else {
/* Send same thing to all. */
for (d = 0; d < save_audio_config_p->achan[chan].num_subchan; d++) {
demod_process_sample(chan, d, audio_sample);
}
} }
for (subchan = 0; subchan < save_audio_config_p->achan[chan].num_subchan; subchan++) { for (subchan = 0; subchan < save_audio_config_p->achan[chan].num_subchan; subchan++) {
int slice;
if (candidate[chan][subchan].packet_p != NULL) { for (slice = 0; slice < save_audio_config_p->achan[chan].num_slicers; slice++) {
candidate[chan][subchan].age++;
if (candidate[chan][subchan].age > process_age[chan]) { if (candidate[chan][subchan][slice].packet_p != NULL) {
pick_best_candidate (chan); candidate[chan][subchan][slice].age++;
if (candidate[chan][subchan][slice].age > process_age[chan]) {
pick_best_candidate (chan);
}
} }
} }
}} }
}
@ -320,7 +343,8 @@ void multi_modem_process_sample (int chan, int audio_sample)
* FCS and acceptable size. * FCS and acceptable size.
* *
* Inputs: chan - Audio channel number, 0 or 1. * Inputs: chan - Audio channel number, 0 or 1.
* subchan - Which modem/decoder found it. * subchan - Which modem found it.
* slice - Which slice found it.
* fbuf - Pointer to first byte in HDLC frame. * fbuf - Pointer to first byte in HDLC frame.
* flen - Number of bytes excluding the FCS. * flen - Number of bytes excluding the FCS.
* alevel - Audio level, range of 0 - 100. * alevel - Audio level, range of 0 - 100.
@ -414,73 +438,31 @@ void multi_modem_process_sample (int chan, int audio_sample)
than one. than one.
*/ */
void multi_modem_process_rec_frame (int chan, int subchan, unsigned char *fbuf, int flen, alevel_t alevel, retry_t retries) void multi_modem_process_rec_frame (int chan, int subchan, int slice, unsigned char *fbuf, int flen, alevel_t alevel, retry_t retries)
{ {
packet_t pp; packet_t pp;
assert (chan >= 0 && chan < MAX_CHANS); assert (chan >= 0 && chan < MAX_CHANS);
assert (subchan >= 0 && subchan < MAX_SUBCHANS); assert (subchan >= 0 && subchan < MAX_SUBCHANS);
assert (slice >= 0 && slice < MAX_SUBCHANS);
pp = ax25_from_frame (fbuf, flen, alevel); pp = ax25_from_frame (fbuf, flen, alevel);
if (pp == NULL) { if (pp == NULL) {
text_color_set(DW_COLOR_ERROR);
dw_printf ("Unexpected internal problem, %s %d\n", __FILE__, __LINE__);
return; /* oops! why would it fail? */ return; /* oops! why would it fail? */
} }
/* /*
* If single modem/deocder, push it thru and forget about all this foolishness. * If only one demodulator/slicer, push it thru and forget about all this foolishness.
*/ */
if (save_audio_config_p->achan[chan].num_subchan == 1) { if (save_audio_config_p->achan[chan].num_subchan == 1 &&
dlq_append (DLQ_REC_FRAME, chan, subchan, pp, alevel, retries, ""); save_audio_config_p->achan[chan].num_slicers == 1) {
return;
}
/* dlq_append (DLQ_REC_FRAME, chan, subchan, slice, pp, alevel, retries, "");
* Special handing for two separated bit errors.
* See description earlier.
*
* Not combined with others to find the best score.
* Either pass it along or drop if duplicate.
*/
if (retries >= RETRY_SWAP_TWO_SEP) {
int mycrc;
char spectrum[MAX_SUBCHANS+1];
int dropped = 0;
memset (spectrum, 0, sizeof(spectrum));
memset (spectrum, '_', (size_t)save_audio_config_p->achan[chan].num_subchan);
spectrum[subchan] = '.';
mycrc = ax25_m_m_crc(pp);
/* Smetimes recovered packet is not the latest one send to the app:
* It can be a packet sent to the app before the latest one because of the processing time ...
* So we check if the crc of current packet has already been received in the queue of others crc
*/
dropped = is_crc_in_queue(chan, mycrc);
#if DEBUG
text_color_set(DW_COLOR_DEBUG);
dw_printf ("\n%s\n%d.%d: ptr=%p, retry=%d, age=, crc=%04x, score= , dropped =%d\n",
spectrum, chan, subchan, pp, (int)retries, mycrc,dropped);
#endif
if (dropped) {
/* Same as last one. Drop it. */
ax25_delete (pp);
#if DEBUG
dw_printf ("Drop duplicate.\n");
#endif
return;
}
#if DEBUG
dw_printf ("Send the best one along.\n");
#endif
dlq_append (DLQ_REC_FRAME, chan, subchan, pp, alevel, retries, spectrum);
if (crc_queue_append(mycrc, chan) > MAX_STORED_CRC)
crc_queue_remove(chan);
return; return;
} }
@ -488,17 +470,17 @@ void multi_modem_process_rec_frame (int chan, int subchan, unsigned char *fbuf,
/* /*
* Otherwise, save them up for a few bit times so we can pick the best. * Otherwise, save them up for a few bit times so we can pick the best.
*/ */
if (candidate[chan][subchan].packet_p != NULL) { if (candidate[chan][subchan][slice].packet_p != NULL) {
/* Oops! Didn't expect it to be there. */ /* Oops! Didn't expect it to be there. */
ax25_delete (candidate[chan][subchan].packet_p); ax25_delete (candidate[chan][subchan][slice].packet_p);
candidate[chan][subchan].packet_p = NULL; candidate[chan][subchan][slice].packet_p = NULL;
} }
candidate[chan][subchan].packet_p = pp; candidate[chan][subchan][slice].packet_p = pp;
candidate[chan][subchan].alevel = alevel; candidate[chan][subchan][slice].alevel = alevel;
candidate[chan][subchan].retries = retries; candidate[chan][subchan][slice].retries = retries;
candidate[chan][subchan].age = 0; candidate[chan][subchan][slice].age = 0;
candidate[chan][subchan].crc = ax25_m_m_crc(pp); candidate[chan][subchan][slice].crc = ax25_m_m_crc(pp);
} }
@ -519,56 +501,82 @@ void multi_modem_process_rec_frame (int chan, int subchan, unsigned char *fbuf,
* *
*--------------------------------------------------------------------*/ *--------------------------------------------------------------------*/
/* This is a suitable order for interleaved "G" demodulators. */
/* Opposite order would be suitable for multi-frequency although */
/* multiple slicers are of questionable value for HF SSB. */
#define subchan_from_n(x) ((x) % save_audio_config_p->achan[chan].num_subchan)
#define slice_from_n(x) ((x) / save_audio_config_p->achan[chan].num_subchan)
static void pick_best_candidate (int chan) static void pick_best_candidate (int chan)
{ {
int subchan; int best_n, best_score;
int best_subchan, best_score; char spectrum[MAX_SUBCHANS*MAX_SLICERS+1];
char spectrum[MAX_SUBCHANS+1]; int n, j, k;
int k; int num_bars = save_audio_config_p->achan[chan].num_slicers * save_audio_config_p->achan[chan].num_subchan;
memset (spectrum, 0, sizeof(spectrum)); memset (spectrum, 0, sizeof(spectrum));
for (subchan = 0; subchan < save_audio_config_p->achan[chan].num_subchan; subchan++) { for (n = 0; n < num_bars; n++) {
j = subchan_from_n(n);
k = slice_from_n(n);
/* Build the spectrum display. */ /* Build the spectrum display. */
if (candidate[chan][subchan].packet_p == NULL) { if (candidate[chan][j][k].packet_p == NULL) {
spectrum[subchan] = '_'; spectrum[n] = '_';
} }
else if (candidate[chan][subchan].retries == RETRY_NONE) { else if (candidate[chan][j][k].retries == RETRY_NONE) {
spectrum[subchan] = '|'; spectrum[n] = '|';
} }
else if (candidate[chan][subchan].retries == RETRY_SWAP_SINGLE) { else if (candidate[chan][j][k].retries == RETRY_INVERT_SINGLE) {
spectrum[subchan] = ':'; spectrum[n] = ':';
} }
else { else {
spectrum[subchan] = '.'; spectrum[n] = '.';
} }
/* Begining score depends on effort to get a valid frame CRC. */ /* Begining score depends on effort to get a valid frame CRC. */
candidate[chan][subchan].score = RETRY_MAX * 1000 - ((int)candidate[chan][subchan].retries * 1000); candidate[chan][j][k].score = RETRY_MAX * 1000 - ((int)candidate[chan][j][k].retries * 1000);
}
/* Bump it up slightly if others nearby have the same CRC. */ /* Bump it up slightly if others nearby have the same CRC. */
for (k = 0; k < save_audio_config_p->achan[chan].num_subchan; k++) { for (n = 0; n < num_bars; n++) {
if (k != subchan && candidate[chan][k].packet_p != NULL) { int m;
if (candidate[chan][k].crc == candidate[chan][subchan].crc) {
candidate[chan][subchan].score += (MAX_SUBCHANS+1) - abs(subchan-k); j = subchan_from_n(n);
k = slice_from_n(n);
if (candidate[chan][j][k].packet_p != NULL) {
for (m = 0; m < num_bars; m++) {
int mj = subchan_from_n(m);
int mk = slice_from_n(m);
if (m != n && candidate[chan][mj][mk].packet_p != NULL) {
if (candidate[chan][j][k].crc == candidate[chan][mj][mk].crc) {
candidate[chan][j][k].score += (num_bars+1) - abs(m-n);
}
} }
} }
} }
} }
best_subchan = 0; best_n = 0;
best_score = 0; best_score = 0;
for (subchan = 0; subchan < save_audio_config_p->achan[chan].num_subchan; subchan++) { for (n = 0; n < num_bars; n++) {
if (candidate[chan][subchan].packet_p != NULL) { j = subchan_from_n(n);
if (candidate[chan][subchan].score > best_score) { k = slice_from_n(n);
best_score = candidate[chan][subchan].score;
best_subchan = subchan; if (candidate[chan][j][k].packet_p != NULL) {
if (candidate[chan][j][k].score > best_score) {
best_score = candidate[chan][j][k].score;
best_n = n;
} }
} }
} }
@ -577,20 +585,22 @@ static void pick_best_candidate (int chan)
text_color_set(DW_COLOR_DEBUG); text_color_set(DW_COLOR_DEBUG);
dw_printf ("\n%s\n", spectrum); dw_printf ("\n%s\n", spectrum);
for (subchan = 0; subchan < save_audio_config_p->achan[chan].num_subchan; subchan++) { for (n = 0; n < num_bars; n++) {
j = subchan_from_n(n);
k = slice_from_n(n);
if (candidate[chan][subchan].packet_p == NULL) { if (candidate[chan][j][k].packet_p == NULL) {
dw_printf ("%d.%d: ptr=%p\n", chan, subchan, dw_printf ("%d.%d.%d: ptr=%p\n", chan, j, k,
candidate[chan][subchan].packet_p); candidate[chan][j][k].packet_p);
} }
else { else {
dw_printf ("%d.%d: ptr=%p, retry=%d, age=%3d, crc=%04x, score=%d %s\n", chan, subchan, dw_printf ("%d.%d.%d: ptr=%p, retry=%d, age=%3d, crc=%04x, score=%d %s\n", chan, j, k,
candidate[chan][subchan].packet_p, candidate[chan][j][k].packet_p,
(int)(candidate[chan][subchan].retries), (int)(candidate[chan][j][k].retries),
candidate[chan][subchan].age, candidate[chan][j][k].age,
candidate[chan][subchan].crc, candidate[chan][j][k].crc,
candidate[chan][subchan].score, candidate[chan][j][k].score,
subchan == best_subchan ? "***" : ""); (n == best_n) ? "***" : "");
} }
} }
#endif #endif
@ -599,75 +609,38 @@ static void pick_best_candidate (int chan)
* send the best one along. * send the best one along.
*/ */
#if 1 // v1.2 dev F, Reverse original order. Delete rejects THEN process the best one.
/* Delete those not chosen. */ /* Delete those not chosen. */
for (subchan = 0; subchan < save_audio_config_p->achan[chan].num_subchan; subchan++) { for (n = 0; n < num_bars; n++) {
if (subchan != best_subchan && candidate[chan][subchan].packet_p != NULL) { j = subchan_from_n(n);
ax25_delete (candidate[chan][subchan].packet_p); k = slice_from_n(n);
candidate[chan][subchan].packet_p = NULL; if (n != best_n && candidate[chan][j][k].packet_p != NULL) {
ax25_delete (candidate[chan][j][k].packet_p);
candidate[chan][j][k].packet_p = NULL;
} }
} }
/* Pass along one. */ /* Pass along one. */
dlq_append (DLQ_REC_FRAME, chan, best_subchan,
candidate[chan][best_subchan].packet_p, j = subchan_from_n(best_n);
candidate[chan][best_subchan].alevel, k = slice_from_n(best_n);
(int)(candidate[chan][best_subchan].retries),
dlq_append (DLQ_REC_FRAME, chan, j, k,
candidate[chan][j][k].packet_p,
candidate[chan][j][k].alevel,
(int)(candidate[chan][j][k].retries),
spectrum); spectrum);
if (crc_queue_append(candidate[chan][best_subchan].crc, chan) > MAX_STORED_CRC)
crc_queue_remove(chan);
/* Someone else owns it now and will delete it later. */ /* Someone else owns it now and will delete it later. */
candidate[chan][best_subchan].packet_p = NULL; candidate[chan][j][k].packet_p = NULL;
/* Clear in preparation for next time. */ /* Clear in preparation for next time. */
for (subchan = 0; subchan < save_audio_config_p->achan[chan].num_subchan; subchan++) { memset (candidate, 0, sizeof(candidate));
candidate[chan][subchan].alevel.rec = 0; } /* end pick_best_candidate */
candidate[chan][subchan].alevel.mark = 0;
candidate[chan][subchan].alevel.space = 0;
candidate[chan][subchan].retries = 0;
candidate[chan][subchan].age = 0;
candidate[chan][subchan].crc = 0;
}
#else
dlq_append (DLQ_REC_FRAME, chan, best_subchan,
candidate[chan][best_subchan].packet_p,
candidate[chan][best_subchan].alevel,
(int)(candidate[chan][best_subchan].retries),
spectrum);
if (crc_queue_append(candidate[chan][best_subchan].crc, chan) > MAX_STORED_CRC)
crc_queue_remove(chan);
/* Someone else will delete so don't do it below. */
candidate[chan][best_subchan].packet_p = NULL;
/* Clear out in preparation for next time. */
for (subchan = 0; subchan < save_audio_config_p->achan[chan].num_subchan; subchan++) {
if (candidate[chan][subchan].packet_p != NULL) {
ax25_delete (candidate[chan][subchan].packet_p);
candidate[chan][subchan].packet_p = NULL;
}
candidate[chan][subchan].alevel.rec = 0;
candidate[chan][subchan].alevel.mark = 0;
candidate[chan][subchan].alevel.space = 0;
candidate[chan][subchan].retries = 0;
candidate[chan][subchan].age = 0;
candidate[chan][subchan].crc = 0;
}
#endif
}
/* end multi_modem.c */ /* end multi_modem.c */

View File

@ -14,7 +14,6 @@ void multi_modem_init (struct audio_s *pmodem);
void multi_modem_process_sample (int c, int audio_sample); void multi_modem_process_sample (int c, int audio_sample);
void multi_modem_process_rec_frame (int chan, int subchan, unsigned char *fbuf, int flen, alevel_t alevel, retry_t retries); void multi_modem_process_rec_frame (int chan, int subchan, int slice, unsigned char *fbuf, int flen, alevel_t alevel, retry_t retries);
#endif #endif

9
recv.c
View File

@ -287,11 +287,11 @@ void recv_process (void)
dlq_type_t type; dlq_type_t type;
int chan; int chan;
int subchan; int subchan;
int slice;
packet_t pp; packet_t pp;
alevel_t alevel; alevel_t alevel;
retry_t retries; retry_t retries;
char spectrum[MAX_SUBCHANS+1]; char spectrum[MAX_SUBCHANS*MAX_SLICERS+1];
while (1) { while (1) {
@ -301,16 +301,15 @@ void recv_process (void)
dw_printf ("recv_process: woke up\n"); dw_printf ("recv_process: woke up\n");
#endif #endif
ok = dlq_remove (&type, &chan, &subchan, &slice, &pp, &alevel, &retries, spectrum, sizeof(spectrum));
ok = dlq_remove (&type, &chan, &subchan, &pp, &alevel, &retries, spectrum, sizeof(spectrum));
#if DEBUG #if DEBUG
text_color_set(DW_COLOR_DEBUG); text_color_set(DW_COLOR_DEBUG);
dw_printf ("recv_process: dlq_remove() returned ok=%d, type=%d, chan=%d, pp=%p\n", dw_printf ("recv_process: dlq_remove() returned ok=%d, type=%d, chan=%d, pp=%p\n",
ok, (int)type, chan, pp); ok, (int)type, chan, pp);
#endif #endif
if (ok) { if (ok) {
app_process_rec_packet (chan, subchan, slice, pp, alevel, retries, spectrum);
app_process_rec_packet (chan, subchan, pp, alevel, retries, spectrum);
} }
#if DEBUG #if DEBUG
else { else {

View File

@ -98,6 +98,8 @@ static void * redecode_thread (void *arg);
void redecode_init (struct audio_s *p_audio_config) void redecode_init (struct audio_s *p_audio_config)
{ {
#if 0
#if __WIN32__ #if __WIN32__
HANDLE redecode_th; HANDLE redecode_th;
#else #else
@ -150,7 +152,7 @@ void redecode_init (struct audio_s *p_audio_config)
text_color_set(DW_COLOR_DEBUG); text_color_set(DW_COLOR_DEBUG);
dw_printf ("redecode_init: finished \n"); dw_printf ("redecode_init: finished \n");
#endif #endif
#endif
} /* end redecode_init */ } /* end redecode_init */
@ -174,6 +176,8 @@ void redecode_init (struct audio_s *p_audio_config)
* *
*--------------------------------------------------------------------*/ *--------------------------------------------------------------------*/
#if 0
#if __WIN32__ #if __WIN32__
static unsigned redecode_thread (void *arg) static unsigned redecode_thread (void *arg)
#else #else
@ -241,7 +245,7 @@ static void * redecode_thread (void *arg)
} /* end redecode_thread */ } /* end redecode_thread */
#endif

145
rrbb.c
View File

@ -23,17 +23,14 @@
* File: rrbb.c * File: rrbb.c
* *
* Purpose: Raw Received Bit Buffer. * Purpose: Raw Received Bit Buffer.
* Implementation of an array of bits used to hold data out of * An array of bits used to hold data out of
* the demodulator before feeding it into the HLDC decoding. * the demodulator before feeding it into the HLDC decoding.
* *
* Version 1.0: Let's try something new.
* Rather than storing a single bit from the demodulator
* output, let's store a value which we can try later
* comparing to threshold values besides 0.
*
* Version 1.2: Save initial state of 9600 baud descrambler so we can * Version 1.2: Save initial state of 9600 baud descrambler so we can
* attempt bit fix up on G3RUH/K9NG scrambled data. * attempt bit fix up on G3RUH/K9NG scrambled data.
* *
* Version 1.3: Store as bytes rather than packing 8 bits per byte.
*
*******************************************************************************/ *******************************************************************************/
#define RRBB_C #define RRBB_C
@ -49,44 +46,9 @@
#include "rrbb.h" #include "rrbb.h"
#define MAGIC1 0x12344321 #define MAGIC1 0x12344321
#define MAGIC2 0x56788765 #define MAGIC2 0x56788765
static const unsigned int masks[SOI] = {
0x00000001,
0x00000002,
0x00000004,
0x00000008,
0x00000010,
0x00000020,
0x00000040,
0x00000080,
0x00000100,
0x00000200,
0x00000400,
0x00000800,
0x00001000,
0x00002000,
0x00004000,
0x00008000,
0x00010000,
0x00020000,
0x00040000,
0x00080000,
0x00100000,
0x00200000,
0x00400000,
0x00800000,
0x01000000,
0x02000000,
0x04000000,
0x08000000,
0x10000000,
0x20000000,
0x40000000,
0x80000000 };
static int new_count = 0; static int new_count = 0;
static int delete_count = 0; static int delete_count = 0;
@ -102,6 +64,8 @@ static int delete_count = 0;
* *
* subchan - Which demodulator of the channel. * subchan - Which demodulator of the channel.
* *
* slice - multiple thresholds per demodulator.
*
* is_scrambled - Is data scrambled? (true, false) * is_scrambled - Is data scrambled? (true, false)
* *
* descram_state - State of data descrambler. * descram_state - State of data descrambler.
@ -114,21 +78,20 @@ static int delete_count = 0;
* *
***********************************************************************************/ ***********************************************************************************/
rrbb_t rrbb_new (int chan, int subchan, int is_scrambled, int descram_state, int prev_descram) rrbb_t rrbb_new (int chan, int subchan, int slice, int is_scrambled, int descram_state, int prev_descram)
{ {
rrbb_t result; rrbb_t result;
assert (SOI == 8 * sizeof(unsigned int));
assert (chan >= 0 && chan < MAX_CHANS); assert (chan >= 0 && chan < MAX_CHANS);
assert (subchan >= 0 && subchan < MAX_SUBCHANS); assert (subchan >= 0 && subchan < MAX_SUBCHANS);
assert (slice >= 0 && slice < MAX_SLICERS);
result = malloc(sizeof(struct rrbb_s)); result = malloc(sizeof(struct rrbb_s));
result->magic1 = MAGIC1; result->magic1 = MAGIC1;
result->chan = chan; result->chan = chan;
result->subchan = subchan; result->subchan = subchan;
result->slice = slice;
result->magic2 = MAGIC2; result->magic2 = MAGIC2;
new_count++; new_count++;
@ -181,6 +144,7 @@ void rrbb_clear (rrbb_t b, int is_scrambled, int descram_state, int prev_descram
b->prev_descram = prev_descram; b->prev_descram = prev_descram;
} }
/*********************************************************************************** /***********************************************************************************
* *
* Name: rrbb_append_bit * Name: rrbb_append_bit
@ -192,35 +156,7 @@ void rrbb_clear (rrbb_t b, int is_scrambled, int descram_state, int prev_descram
* *
***********************************************************************************/ ***********************************************************************************/
/* Definition in header file so it can be inlined. */
// TODO: Forget about bit packing and just use bytes.
// We have hundreds of MB. Why waste time to save a couple KB?
void rrbb_append_bit (rrbb_t b, int val)
{
unsigned int di, mi;
assert (b != NULL);
assert (b->magic1 == MAGIC1);
assert (b->magic2 == MAGIC2);
if (b->len >= MAX_NUM_BITS) {
return; /* Silently discard if full. */
}
di = b->len / SOI;
mi = b->len % SOI;
if (val) {
b->data[di] |= masks[mi];
}
else {
b->data[di] &= ~ masks[mi];
}
b->len++;
}
/*********************************************************************************** /***********************************************************************************
@ -279,46 +215,9 @@ int rrbb_get_len (rrbb_t b)
* *
***********************************************************************************/ ***********************************************************************************/
int rrbb_get_bit (rrbb_t b, unsigned int ind) /* Definition in header file so it can be inlined. */
{
assert (b != NULL);
assert (b->magic1 == MAGIC1);
assert (b->magic2 == MAGIC2);
assert (ind < b->len);
if (b->data[ind / SOI] & masks[ind % SOI]) {
return 1;
}
else {
return 0;
}
}
unsigned int rrbb_get_computed_bit (rrbb_t b, unsigned int ind)
{
return b->computed_data[ind];
}
int rrbb_compute_bits (rrbb_t b)
{
unsigned int i,val;
assert (b != NULL);
assert (b->magic1 == MAGIC1);
assert (b->magic2 == MAGIC2);
for (i=0;i<b->len;i++) {
if (b->data[i / SOI] & masks[i % SOI]) {
val = 1;
}
else {
val = 0;
}
b->computed_data[i] = val;
}
return 0;
}
/*********************************************************************************** /***********************************************************************************
@ -457,6 +356,28 @@ int rrbb_get_subchan (rrbb_t b)
} }
/***********************************************************************************
*
* Name: rrbb_get_slice
*
* Purpose: Get slice number from which bit buffer was received.
*
* Inputs: b Handle for bit array.
*
***********************************************************************************/
int rrbb_get_slice (rrbb_t b)
{
assert (b != NULL);
assert (b->magic1 == MAGIC1);
assert (b->magic2 == MAGIC2);
assert (b->slice >= 0 && b->slice < MAX_SLICERS);
return (b->slice);
}
/*********************************************************************************** /***********************************************************************************
* *
* Name: rrbb_set_audio_level * Name: rrbb_set_audio_level

42
rrbb.h
View File

@ -4,10 +4,11 @@
#define RRBB_H #define RRBB_H
typedef short slice_t; #define FASTER13 1 // Don't pack 8 samples per byte.
#ifdef RRBB_C //typedef short slice_t;
/* /*
* Maximum size (in bytes) of an AX.25 frame including the 2 octet FCS. * Maximum size (in bytes) of an AX.25 frame including the 2 octet FCS.
@ -23,13 +24,14 @@ typedef short slice_t;
#define MAX_NUM_BITS (MAX_FRAME_LEN * 8 * 6 / 5) #define MAX_NUM_BITS (MAX_FRAME_LEN * 8 * 6 / 5)
#define SOI 32
typedef struct rrbb_s { typedef struct rrbb_s {
int magic1; int magic1;
struct rrbb_s* nextp; /* Next pointer to maintain a queue. */ struct rrbb_s* nextp; /* Next pointer to maintain a queue. */
int chan; /* Radio channel from which it was received. */ int chan; /* Radio channel from which it was received. */
int subchan; /* Which modem when more than one per channel. */ int subchan; /* Which modem when more than one per channel. */
int slice; /* Which slicer. */
alevel_t alevel; /* Received audio level at time of frame capture. */ alevel_t alevel; /* Received audio level at time of frame capture. */
unsigned int len; /* Current number of samples in array. */ unsigned int len; /* Current number of samples in array. */
@ -37,38 +39,37 @@ typedef struct rrbb_s {
int descram_state; /* Descrambler state before first data bit of frame. */ int descram_state; /* Descrambler state before first data bit of frame. */
int prev_descram; /* Previous descrambled bit. */ int prev_descram; /* Previous descrambled bit. */
unsigned int data[(MAX_NUM_BITS+SOI-1)/SOI]; unsigned char fdata[MAX_NUM_BITS];
unsigned int computed_data[MAX_NUM_BITS];
int magic2; int magic2;
} *rrbb_t; } *rrbb_t;
#else
/* Hide the implementation. */
typedef void *rrbb_t;
#endif
rrbb_t rrbb_new (int chan, int subchan, int slice, int is_scrambled, int descram_state, int prev_descram);
rrbb_t rrbb_new (int chan, int subchan, int is_scrambled, int descram_state, int prev_descram);
void rrbb_clear (rrbb_t b, int is_scrambled, int descram_state, int prev_descram); void rrbb_clear (rrbb_t b, int is_scrambled, int descram_state, int prev_descram);
void rrbb_append_bit (rrbb_t b, int val); static __attribute__((always_inline)) void rrbb_append_bit (rrbb_t b, const unsigned char val)
{
if (b->len >= MAX_NUM_BITS) {
return; /* Silently discard if full. */
}
b->fdata[b->len] = val;
b->len++;
}
static __attribute__((always_inline)) unsigned char rrbb_get_bit (const rrbb_t b, const int ind)
{
return (b->fdata[ind]);
}
void rrbb_chop8 (rrbb_t b); void rrbb_chop8 (rrbb_t b);
int rrbb_get_len (rrbb_t b); int rrbb_get_len (rrbb_t b);
int rrbb_get_bit (rrbb_t b, unsigned int ind);
unsigned int rrbb_get_computed_bit (rrbb_t b, unsigned int ind);
int rrbb_compute_bits (rrbb_t b);
//void rrbb_flip_bit (rrbb_t b, unsigned int ind); //void rrbb_flip_bit (rrbb_t b, unsigned int ind);
void rrbb_delete (rrbb_t b); void rrbb_delete (rrbb_t b);
@ -78,6 +79,7 @@ rrbb_t rrbb_get_nextp (rrbb_t b);
int rrbb_get_chan (rrbb_t b); int rrbb_get_chan (rrbb_t b);
int rrbb_get_subchan (rrbb_t b); int rrbb_get_subchan (rrbb_t b);
int rrbb_get_slice (rrbb_t b);
void rrbb_set_audio_level (rrbb_t b, alevel_t alevel); void rrbb_set_audio_level (rrbb_t b, alevel_t alevel);
alevel_t rrbb_get_audio_level (rrbb_t b); alevel_t rrbb_get_audio_level (rrbb_t b);