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 #
----------
## Version 1.3 -- Development snapshot H -- November 2015 ##
### New Feature: ###
- New experimental demodulator. More details later.
----------
## 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
# Can we encode and decode at popular data rates?
# Verify that single bit fixup increases the count.
check-modem1200 : gen_packets atest
gen_packets -n 100 -o test1.wav
@ -361,14 +362,13 @@ demod_9600.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 \
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
$(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

View File

@ -1561,7 +1561,7 @@ static void raw_tt_data_to_app (int chan, char *msg)
alevel.mark = -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 {
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 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[])
{
@ -495,6 +499,8 @@ int main (int argc, char *argv[])
if (audio_sample >= 256 * 256)
e_o_f = 1;
if (c == 0) sample_number++;
if (decode_only == 0 && c != 0) continue;
if (decode_only == 1 && c != 1) continue;
@ -572,16 +578,16 @@ int audio_get (int a)
void rdq_append (rrbb_t rrbb)
{
int chan;
int chan, subchan, slice;
alevel_t alevel;
int subchan;
chan = rrbb_get_chan(rrbb);
subchan = rrbb_get_subchan(rrbb);
slice = rrbb_get_slice(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);
}
@ -590,7 +596,7 @@ void rdq_append (rrbb_t rrbb)
* 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];
@ -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);
dw_printf ("\n");
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) {
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
#if defined(EXPERIMENT_G) || defined(EXPERIMENT_H)
int j;
for (j=0; j<MAX_SUBCHANS; j++) {
if (spectrum[j] == '|') {
count[j]++;
}
}
#endif
//#if defined(EXPERIMENT_G) || defined(EXPERIMENT_H)
// int j;
//
// for (j=0; j<MAX_SUBCHANS; j++) {
// if (spectrum[j] == '|') {
// count[j]++;
// }
// }
//#endif
// 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)) {
text_color_set(DW_COLOR_REC);
dw_printf ("[%d] ", chan);
}
else {
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);
}
@ -671,7 +697,7 @@ void dlq_append (dlq_type_t type, int chan, int subchan, packet_t pp, alevel_t a
ax25_delete (pp);
} /* end app_process_rec_packet */
} /* end fake dlq_append */
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 {
RETRY_NONE=0,
RETRY_SWAP_SINGLE=1,
RETRY_SWAP_DOUBLE=2,
RETRY_SWAP_TRIPLE=3,
RETRY_REMOVE_SINGLE=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,
RETRY_MAX = 13} retry_t;
RETRY_INVERT_SINGLE=1,
RETRY_INVERT_DOUBLE=2,
RETRY_INVERT_TRIPLE=3,
RETRY_INVERT_TWO_SEP=4,
RETRY_MAX = 5} retry_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 */
/* 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 space_freq; /* Standard tones are 1200 and 2200 for 1200 baud. */
@ -140,19 +136,13 @@ struct audio_s {
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). */
/* 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. */
/* This is derived from above by demod_init. */
/* num_slicers could be added to be more general but */
/* for the intial experiment, we can just examine profiles. */
int num_subchan; /* Total number of modems for each channel. */
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. */
@ -267,7 +257,7 @@ struct audio_s {
#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.

View File

@ -914,7 +914,7 @@ static void beacon_send (int j, dwgps_info_t *gpsinfo)
/* Simulated reception. */
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;
}
}

View File

@ -1390,15 +1390,27 @@ void config_init (char *fname, struct audio_s *p_audio_config,
continue;
}
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;
}
else {
p_audio_config->achan[channel].fix_bits = DEFAULT_FIX_BITS;
text_color_set(DW_COLOR_ERROR);
dw_printf ("Line %d: Invalid value for FIX_BITS. Using %d.\n",
line, p_audio_config->achan[channel].fix_bits);
}
dw_printf ("Line %d: Invalid value %d for FIX_BITS. Using default of %d.\n",
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);
while (t != NULL) {

137
demod.c
View File

@ -119,6 +119,21 @@ int demod_init (struct audio_s *pa)
int num_letters;
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) {
case MODEM_OFF:
@ -234,8 +249,7 @@ int demod_init (struct audio_s *pa)
/* 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_demod = num_letters;
save_audio_config_p->achan[chan].num_slicers = 1;
/*
* Some error checking - Can use only one of these:
@ -245,24 +259,10 @@ int demod_init (struct audio_s *pa)
* - 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) {
text_color_set(DW_COLOR_ERROR);
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_freq = 1;
}
@ -302,6 +302,7 @@ int demod_init (struct audio_s *pa)
* We have 3 cases to consider.
*/
// TODO1.3: revisit this logic now that it is less restrictive.
if (num_letters > 1) {
int d;
@ -311,17 +312,58 @@ int demod_init (struct audio_s *pa)
* Each one corresponds to a demodulator and subchannel.
*
* 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_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);
dw_printf ("INTERNAL ERROR, %s:%d, chan=%d, num_demod(%d) != strlen(\"%s\")\n",
__FILE__, __LINE__, chan, save_audio_config_p->achan[chan].num_demod, save_audio_config_p->achan[chan].profiles);
dw_printf ("INTERNAL ERROR, %s:%d, chan=%d, num_subchan(%d) != strlen(\"%s\")\n",
__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) {
@ -330,8 +372,7 @@ int demod_init (struct audio_s *pa)
__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;
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;
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);
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,
mark,
space,
profile,
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. */
// TODO: Should probably move this into the init functions.
@ -364,7 +413,7 @@ int demod_init (struct audio_s *pa)
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.
*/
@ -381,21 +430,19 @@ int demod_init (struct audio_s *pa)
__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);
dw_printf ("INTERNAL ERROR, %s:%d, chan=%d, num_freq(%d) != num_demod(%d)\n",
__FILE__, __LINE__, chan, save_audio_config_p->achan[chan].num_freq, save_audio_config_p->achan[chan].num_demod);
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_subchan);
}
struct demodulator_state_s *D;
D = &demodulator_state[chan][0];
/* 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. */
save_audio_config_p->achan[chan].num_demod = 1;
save_audio_config_p->achan[chan].num_subchan = MAX_SUBCHANS;
save_audio_config_p->achan[chan].num_slicers = MAX_SLICERS;
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,
@ -404,10 +451,13 @@ int demod_init (struct audio_s *pa)
save_audio_config_p->achan[chan].profiles[0],
D);
/* I'm not happy about putting this hack here. */
/* should pass in as a parameter rather than adding on later. */
if (have_plus) {
/* 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. */
@ -427,7 +477,6 @@ int demod_init (struct audio_s *pa)
__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;
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,
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. */
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
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) {
/* 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. */
save_audio_config_p->achan[chan].num_demod = 1;
save_audio_config_p->achan[chan].num_subchan = MAX_SUBCHANS;
save_audio_config_p->achan[chan].num_slicers = MAX_SLICERS;
}
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. */
/* 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. */

View File

@ -145,6 +145,7 @@ void demod_9600_init (int samples_per_sec, int baud, struct demodulator_state_s
int j;
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);
@ -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))
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. */
demod_data = demod_out > 0;
nudge_pll (chan, subchan, demod_data, D);
nudge_pll (chan, subchan, 0, demod_data, D);
}
else {
int s;
assert (subchan == 0);
int slice;
/* Multiple slicers each feeding its own HDLC decoder. */
for (s=0; s<D->num_slicers; s++) {
demod_data = demod_out > slice_point[s];
nudge_pll (chan, s, demod_data, D);
for (slice=0; slice<D->num_slicers; slice++) {
demod_data = demod_out > slice_point[slice];
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))
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
* 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. */
@ -443,20 +442,20 @@ static void nudge_pll (int chan, int subchan, int demod_data, struct demodulator
// Warning: 'descram' set but not used.
// It's used in conditional debug code below.
// 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.
if (hdlc_rec_gathering (chan, subchan)) {
D->slicer[subchan].data_clock_pll = (int)(D->slicer[subchan].data_clock_pll * D->pll_locked_inertia);
if (hdlc_rec_gathering (chan, subchan, slice)) {
D->slicer[slice].data_clock_pll = (int)(D->slicer[slice].data_clock_pll * D->pll_locked_inertia);
}
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 (chan == 0) {
if (hdlc_rec_gathering (chan,subchan)) {
if (hdlc_rec_gathering (chan,subchan,slice)) {
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
* for the DPLL sync.
*/
D->slicer[subchan].prev_demod_data = demod_data;
D->slicer[slice].prev_demod_data = demod_data;
} /* end nudge_pll */

View File

@ -198,6 +198,7 @@ void demod_afsk_init (int samples_per_sec, int baud, int mark_freq,
int j;
memset (D, 0, sizeof(struct demodulator_state_s));
D->num_slicers = 1;
#if DEBUG1
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':
/* 1200 baud - Started out similar to C but add prefilter. */
/* Version 1.2 - EXPERIMENTAL - Needs more fine tuning. */
/* Version 1.2 */
/* Enhancements: */
/* + Add prefilter. Previously used for 300 baud D, but not 1200. */
/* + 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;
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:
text_color_set(DW_COLOR_ERROR);
@ -719,8 +748,8 @@ int main ()
modem.achan[0].mark_freq = DEFAULT_MARK_FREQ;
modem.achan[0].space_freq = DEFAULT_SPACE_FREQ;
modem.achan[0].baud = DEFAULT_BAUD;
modem.achan[0].num_demod = 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,
@ -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))
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 {
demod_data = D->slicer[subchan].prev_demod_data;
}
nudge_pll (chan, subchan, demod_data, D);
nudge_pll (chan, subchan, 0, demod_data, D);
}
else {
int s;
int slice;
assert (subchan == 0);
/* "G" profile with one demodulator and multiple slicers */
/* 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);
for (slice=0; slice<D->num_slicers; slice++) {
demod_data = m_amp > s_amp * space_gain[slice];
nudge_pll (chan, subchan, slice, demod_data, D);
}
}
#if DEBUG4
if (chan == 0) {
@ -1080,12 +1101,15 @@ void demod_afsk_process_sample (int chan, int subchan, int sam, struct demodulat
__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.
*
* 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.
* When it overflows from a large positive value to a negative value, we
* 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
* 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);
// 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. */
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)) {
D->slicer[subchan].data_clock_pll = (int)(D->slicer[subchan].data_clock_pll * D->pll_locked_inertia);
if (hdlc_rec_gathering (chan, subchan, slice)) {
D->slicer[slice].data_clock_pll = (int)(D->slicer[slice].data_clock_pll * D->pll_locked_inertia);
}
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.
*/
D->slicer[subchan].prev_demod_data = demod_data;
D->slicer[slice].prev_demod_data = demod_data;
} /* end nudge_pll */

View File

@ -231,7 +231,7 @@ int main (int argc, char *argv[])
text_color_init(t_opt);
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 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);
#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.
* subchan - Which modem caught it.
* Special case -1 for DTMF decoder.
* slice - Slicer which caught it.
* pp - Packet handle.
* alevel - Audio level, range of 0 - 100.
* (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.
void app_process_rec_packet (int chan, int subchan, packet_t pp, alevel_t alevel, retry_t retries, char *spectrum)
void app_process_rec_packet (int chan, int subchan, int slice, packet_t pp, alevel_t alevel, retry_t retries, char *spectrum)
{
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 (subchan >= -1 && subchan < MAX_SUBCHANS);
assert (slice >= 0 && slice < MAX_SLICERS);
assert (pp != NULL); // 1.1J+
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 {
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);
}
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 {
dw_printf ("[%d] ", chan);
}

View File

@ -48,6 +48,17 @@
#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__
#include <windows.h>

20
dlq.c
View File

@ -64,14 +64,15 @@ struct dlq_item_s {
/* Special case, -1 means DTMF decoder. */
/* Maybe we should have a different type in this case? */
int slice; /* Winning slicer. */
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. */
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.
* Special case -1 for APRStt gateway.
*
* slice - Which slice we picked.
*
* pp - Address of packet object.
* Caller should NOT make any references to
* 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;
@ -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->type = type;
pnew->chan = chan;
pnew->slice = slice;
pnew->subchan = subchan;
pnew->pp = pp;
pnew->alevel = alevel;
@ -514,7 +518,8 @@ void dlq_wait_while_empty (void)
* Outputs: type - type of queue entry.
*
* 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.
* 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;
@ -557,6 +563,7 @@ int dlq_remove (dlq_type_t *type, int *chan, int *subchan, packet_t *pp, alevel_
*type = -1;
*chan = -1;
*subchan = -1;
*slice = -1;
*pp = NULL;
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;
*chan = phead->chan;
*subchan = phead->subchan;
*slice = phead->slice;
*pp = phead->pp;
*alevel = phead->alevel;
*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;
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);
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

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.
dcd_change (c, MAX_SUBCHANS, decoded != ' ');
dcd_change (c, MAX_SUBCHANS, 0, decoded != ' ');
/* Reset timeout timer. */
if (decoded != ' ') {

View File

@ -180,7 +180,28 @@ struct demodulator_state_s
* 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 {
signed int data_clock_pll; // PLL for data clock recovery.
@ -197,25 +218,13 @@ struct demodulator_state_s
int lfsr; // Descrambler shift register.
} slicer [MAX_SUBCHANS];
#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
} slicer [MAX_SLICERS]; // Actual number in use is num_slicers.
// Should be in range 1 .. MAX_SLICERS,
/*
* Special for Rino decoder only.
* One for each possible signal polarity.
* The project showed promise but fell by the wayside.
*/
#if 0

View File

@ -29,6 +29,7 @@
#include <stdio.h>
#include <assert.h>
#include <string.h>
#include "direwolf.h"
#include "demod.h"
@ -105,13 +106,11 @@ struct hdlc_state_s {
};
static struct hdlc_state_s hdlc_state[MAX_CHANS][MAX_SUBCHANS];
static struct hdlc_state_s hdlc_state[MAX_CHANS][MAX_SUBCHANS][MAX_SLICERS];
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)
{
int j, k;
int ch, sub, slice;
struct hdlc_state_s *H;
//text_color_set(DW_COLOR_DEBUG);
@ -136,34 +135,33 @@ void hdlc_rec_init (struct audio_s *pa)
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->lfsr = 0;
H->prev_descram = 0;
H->pat_det = 0;
H->flag4_det = 0;
H->olen = -1;
H->frame_len = 0;
H->data_detect = 0;
// TODO: wasteful if not needed.
H->rrbb = rrbb_new(j, k, pa->achan[j].modem_type == MODEM_SCRAMBLE, H->lfsr, H->prev_descram);
H = &hdlc_state[ch][sub][slice];
H->olen = -1;
// TODO: FIX13 wasteful if not needed.
// Should loop on number of slicers, not max.
H->rrbb = rrbb_new(ch, sub, slice, pa->achan[ch].modem_type == MODEM_SCRAMBLE, H->lfsr, H->prev_descram);
}
}
}
}
hdlc_rec2_init (pa);
was_init = 1;
}
@ -178,14 +176,16 @@ void hdlc_rec_init (struct audio_s *pa)
*
* 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.
* should be 0 or 1.
*
* 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.
@ -196,9 +196,7 @@ void hdlc_rec_init (struct audio_s *pa)
// TODO: int not_used_remove
void hdlc_rec_bit (int chan, int subchan, int raw, int is_scrambled, int not_used_remove)
void hdlc_rec_bit (int chan, int subchan, int slice, int raw, int is_scrambled, int not_used_remove)
{
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 (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,
@ -284,7 +284,7 @@ void hdlc_rec_bit (int chan, int subchan, int raw, int is_scrambled, int not_use
if ( ! H->data_detect) {
H->data_detect = 1;
dcd_change (chan, subchan, 1);
dcd_change (chan, subchan, slice, 1);
}
}
//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) {
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 ) {
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) {
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 {
@ -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);
hdlc_rec2_block (H->rrbb);
/* 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 {
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
* subchan
* slice
*
* Returns: True if we are currently gathering bits.
* In this case we want the PLL to have more inertia.
*
* Discussion: Originally I used the data carrier detect.
* Later, it seemed like the we should be using "olen>=0" instead.
*
* Seems to make no difference for Track 1 and the original
* way was a hair better for Track 2.
* Discussion: This simply returns the data carrier detect state.
* A couple other variations were tried but turned out to
* be slightly worse.
*
*--------------------------------------------------------------------*/
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 (subchan >= 0 && subchan < MAX_SUBCHANS);
assert (slice >= 0 && slice < MAX_SLICERS);
// Counts from Track 1 & Track 2
// data_detect 992 988
// olen>=0 992 985
// OR-ed 992 985
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 );
return ( hdlc_state[chan][subchan][slice].data_detect );
} /* end hdlc_rec_gathering */
/*-------------------------------------------------------------------
*
* 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.
*
* Inputs: chan
*
* 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.
*
* 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
* is active. Update the DCD indicator.
* Description: DCD for the channel is active if ANY of the subchannels/slices
* are active. Update the DCD indicator.
*
* version 1.3: Add DTMF detection into the final result.
* This is now called from dtmf.c too.
*
*--------------------------------------------------------------------*/
void dcd_change (int chan, int subchan, int state)
void dcd_change (int chan, int subchan, int slice, int state)
{
int old, new;
assert (chan >= 0 && chan < MAX_CHANS);
assert (subchan >= 0 && subchan <= MAX_SUBCHANS);
assert (slice >= 0 && slice < MAX_SLICERS);
assert (state == 0 || state == 1);
#if DEBUG3
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
old = hdlc_rec_data_detect_any(chan);
if (state) {
composite_dcd[chan] |= (1 << subchan);
composite_dcd[chan][subchan] |= (1 << slice);
}
else {
composite_dcd[chan] &= ~ (1 << subchan);
composite_dcd[chan][subchan] &= ~ (1 << slice);
}
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 sc;
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.c */

View File

@ -5,9 +5,7 @@
void hdlc_rec_init (struct audio_s *pa);
void hdlc_rec_bit (int chan, int subchan, int raw, int is_scrambled, int descram_state);
void hdlc_rec_bit (int chan, int subchan, int slice, int raw, int is_scrambled, int descram_state);
/* 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. */
/* 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. */
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);

View File

@ -76,6 +76,12 @@
* It was necessary to retain more initial state information after
* 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>
@ -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_to_fix_quick_now (rrbb_t block, int chan, int subchan, alevel_t alevel);
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, int slice, alevel_t alevel);
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
* a bad FCS on the frame.
* 0 = no effort
* 1 = try fixing a single bit
* 1 = try inverting a single bit
* 2... = more techniques...
*
* enum sanity_e sanity_test;
@ -222,6 +228,7 @@ void hdlc_rec2_block (rrbb_t block)
{
int chan = rrbb_get_chan(block);
int subchan = rrbb_get_subchan(block);
int slice = rrbb_get_slice(block);
alevel_t alevel = rrbb_get_audio_level(block);
retry_t fix_bits = save_audio_config_p->achan[chan].fix_bits;
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.u_bits.contig.nr_bits = 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 DEBUG
text_color_set(DW_COLOR_INFO);
@ -261,29 +265,19 @@ void hdlc_rec2_block (rrbb_t block)
/*
* 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);
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) {
rdq_append (block);
}
else if (passall) {
if (passall) {
/* Exhausted all desired fix up attempts. */
/* Let thru even with bad CRC. Of course, it still */
/* 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);
}
else {
@ -307,7 +301,7 @@ void hdlc_rec2_block (rrbb_t block)
* 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.
* RETRY_INVERT_SINGLE (1) - Try inverting single bits.
* etc.
*
* configuration passall - Let it thru with bad CRC after exhausting
@ -318,15 +312,19 @@ void hdlc_rec2_block (rrbb_t block)
* processing step.
* 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.
* 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.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 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*/
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. */
@ -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 */
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;
for (i=0; i<len; i++) {
/* Set the index of the bit to swap */
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 DEBUG
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;
}
/* 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;
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);
ok = try_decode (block, chan, subchan, slice, alevel, retry_cfg, 0);
if (ok) {
#if DEBUG
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;
}
/* 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;
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);
ok = try_decode (block, chan, subchan, slice, alevel, retry_cfg, 0);
if (ok) {
#if DEBUG
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.
* 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.
*/
if (fix_bits < RETRY_INVERT_TWO_SEP) {
return 0;
}
retry_cfg.mode = RETRY_MODE_SEPARATED;
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;
#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;
for (j=i+2; j<len; 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) {
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 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
tend = dtime_now();
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);
double tstart, tend;
#endif
retry_conf_t retry_cfg;
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.
@ -769,11 +483,10 @@ failure:
retry_cfg.type = RETRY_TYPE_NONE;
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.bit_idx = 0;
ok = try_decode (block, chan, subchan, alevel, retry_cfg, passall);
ok = try_decode (block, chan, subchan, slice, alevel, retry_cfg, passall);
return (ok);
}
@ -782,6 +495,7 @@ failure:
} /* end hdlc_rec2_try_to_fix_later */
/*
* 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
@ -811,14 +525,7 @@ inline static char is_sep_bit_modified(int bit_idx, retry_conf_t retry_conf) {
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:
* Level of effort to recover from A bad FCS on the frame.
* RETRY_NONE=0,
* RETRY_SWAP_SINGLE=1,
* RETRY_SWAP_DOUBLE=2,
* RETRY_SWAP_TRIPLE=3,
* RETRY_REMOVE_SINGLE=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,
* Level of effort to recover from a bad FCS on the frame.
* RETRY_NONE = 0
* RETRY_INVERT_SINGLE = 1
* RETRY_INVERT_DOUBLE = 2
* RETRY_INVERT_TRIPLE = 3
* RETRY_INVERT_TWO_SEP = 4
*
* mode: RETRY_MODE_CONTIGUOUS - change adjacent bits.
* 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.
* RETRY_TYPE_SWAP - Try inverting.
* RETRY_TYPE_REMOVE - Try removing.
* RETRY_TYPE_INSERT - Try inserting.
*
* passall - All it thru even with bad CRC.
* 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, 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)
{
struct hdlc_state_s H;
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.prev_descram = rrbb_get_prev_descram (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 */
/* 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;
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
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
for (i=1; i<blen; i++) {
/* 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 (retry_conf_retry == RETRY_SWAP_TWO_SEP) {
if (retry_conf_retry == RETRY_INVERT_TWO_SEP) {
if (is_sep_bit_modified(i, retry_conf))
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 if (retry_conf_mode == RETRY_MODE_CONTIGUOUS) {
/* If contiguous remove, ignore this bit from the buffer */
if (retry_conf_type == RETRY_TYPE_REMOVE) {
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 (retry_conf_type == RETRY_TYPE_SWAP) {
/* If this is the bit to swap */
if (is_contig_bit_modified(i, retry_conf))
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_subchan(block) == subchan);
multi_modem_process_rec_frame (chan, subchan, H.frame_buf, H.frame_len - 2, alevel, retry_conf.retry); /* len-2 to remove FCS. */
multi_modem_process_rec_frame (chan, subchan, slice, H.frame_buf, H.frame_len - 2, alevel, retry_conf.retry); /* len-2 to remove FCS. */
return 1; /* success */
} 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);
//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 */
}
else {
@ -1186,7 +851,9 @@ end:
*
* 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 {
RETRY_TYPE_NONE=0,
RETRY_TYPE_SWAP=1,
RETRY_TYPE_REMOVE=2,
RETRY_TYPE_INSERT=3} retry_type_t;
RETRY_TYPE_SWAP=1 } retry_type_t;
typedef struct retry_conf_s {
retry_t retry;
@ -52,15 +50,7 @@ static const char * retry_text[] = {
"SINGLE",
"DOUBLE",
"TRIPLE",
"REMOVE_SINGLE",
"REMOVE_DOUBLE",
"REMOVE_TRIPLE",
"INSERT_SINGLE",
"INSERT_DOUBLE",
"TWO_SEP",
"MANY",
"REMOVE_MANY",
"REMOVE_SEP",
"PASSALL" };
#endif
@ -68,11 +58,10 @@ void hdlc_rec2_init (struct audio_s *audio_config_p);
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. */
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

View File

@ -98,23 +98,15 @@ static struct audio_s *save_audio_config_p;
// Candidates for further processing.
static struct {
packet_t packet_p;
alevel_t alevel;
retry_t retries;
int age;
unsigned int crc;
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
@ -163,12 +155,14 @@ void multi_modem_init (struct audio_s *pa)
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;
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
int crc_queue_append (unsigned int crc, unsigned int chan) {
crc_t plast;
@ -257,6 +251,7 @@ unsigned char is_crc_in_queue(unsigned int chan, unsigned int crc) {
}
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
* 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 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. */
/* 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++) {
int slice;
if (candidate[chan][subchan].packet_p != NULL) {
candidate[chan][subchan].age++;
if (candidate[chan][subchan].age > process_age[chan]) {
pick_best_candidate (chan);
for (slice = 0; slice < save_audio_config_p->achan[chan].num_slicers; slice++) {
if (candidate[chan][subchan][slice].packet_p != NULL) {
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.
*
* 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.
* flen - Number of bytes excluding the FCS.
* alevel - Audio level, range of 0 - 100.
@ -414,73 +438,31 @@ void multi_modem_process_sample (int chan, int audio_sample)
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;
assert (chan >= 0 && chan < MAX_CHANS);
assert (subchan >= 0 && subchan < MAX_SUBCHANS);
assert (slice >= 0 && slice < MAX_SUBCHANS);
pp = ax25_from_frame (fbuf, flen, alevel);
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? */
}
/*
* 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) {
dlq_append (DLQ_REC_FRAME, chan, subchan, pp, alevel, retries, "");
return;
}
if (save_audio_config_p->achan[chan].num_subchan == 1 &&
save_audio_config_p->achan[chan].num_slicers == 1) {
/*
* 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);
dlq_append (DLQ_REC_FRAME, chan, subchan, slice, pp, alevel, retries, "");
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.
*/
if (candidate[chan][subchan].packet_p != NULL) {
if (candidate[chan][subchan][slice].packet_p != NULL) {
/* Oops! Didn't expect it to be there. */
ax25_delete (candidate[chan][subchan].packet_p);
candidate[chan][subchan].packet_p = NULL;
ax25_delete (candidate[chan][subchan][slice].packet_p);
candidate[chan][subchan][slice].packet_p = NULL;
}
candidate[chan][subchan].packet_p = pp;
candidate[chan][subchan].alevel = alevel;
candidate[chan][subchan].retries = retries;
candidate[chan][subchan].age = 0;
candidate[chan][subchan].crc = ax25_m_m_crc(pp);
candidate[chan][subchan][slice].packet_p = pp;
candidate[chan][subchan][slice].alevel = alevel;
candidate[chan][subchan][slice].retries = retries;
candidate[chan][subchan][slice].age = 0;
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)
{
int subchan;
int best_subchan, best_score;
char spectrum[MAX_SUBCHANS+1];
int k;
int best_n, best_score;
char spectrum[MAX_SUBCHANS*MAX_SLICERS+1];
int n, j, 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));
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. */
if (candidate[chan][subchan].packet_p == NULL) {
spectrum[subchan] = '_';
if (candidate[chan][j][k].packet_p == NULL) {
spectrum[n] = '_';
}
else if (candidate[chan][subchan].retries == RETRY_NONE) {
spectrum[subchan] = '|';
else if (candidate[chan][j][k].retries == RETRY_NONE) {
spectrum[n] = '|';
}
else if (candidate[chan][subchan].retries == RETRY_SWAP_SINGLE) {
spectrum[subchan] = ':';
else if (candidate[chan][j][k].retries == RETRY_INVERT_SINGLE) {
spectrum[n] = ':';
}
else {
spectrum[subchan] = '.';
spectrum[n] = '.';
}
/* 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++) {
if (k != subchan && candidate[chan][k].packet_p != NULL) {
if (candidate[chan][k].crc == candidate[chan][subchan].crc) {
candidate[chan][subchan].score += (MAX_SUBCHANS+1) - abs(subchan-k);
for (n = 0; n < num_bars; n++) {
int m;
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;
for (subchan = 0; subchan < save_audio_config_p->achan[chan].num_subchan; subchan++) {
if (candidate[chan][subchan].packet_p != NULL) {
if (candidate[chan][subchan].score > best_score) {
best_score = candidate[chan][subchan].score;
best_subchan = subchan;
for (n = 0; n < num_bars; n++) {
j = subchan_from_n(n);
k = slice_from_n(n);
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);
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) {
dw_printf ("%d.%d: ptr=%p\n", chan, subchan,
candidate[chan][subchan].packet_p);
if (candidate[chan][j][k].packet_p == NULL) {
dw_printf ("%d.%d.%d: ptr=%p\n", chan, j, k,
candidate[chan][j][k].packet_p);
}
else {
dw_printf ("%d.%d: ptr=%p, retry=%d, age=%3d, crc=%04x, score=%d %s\n", chan, subchan,
candidate[chan][subchan].packet_p,
(int)(candidate[chan][subchan].retries),
candidate[chan][subchan].age,
candidate[chan][subchan].crc,
candidate[chan][subchan].score,
subchan == best_subchan ? "***" : "");
dw_printf ("%d.%d.%d: ptr=%p, retry=%d, age=%3d, crc=%04x, score=%d %s\n", chan, j, k,
candidate[chan][j][k].packet_p,
(int)(candidate[chan][j][k].retries),
candidate[chan][j][k].age,
candidate[chan][j][k].crc,
candidate[chan][j][k].score,
(n == best_n) ? "***" : "");
}
}
#endif
@ -599,75 +609,38 @@ static void pick_best_candidate (int chan)
* 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. */
for (subchan = 0; subchan < save_audio_config_p->achan[chan].num_subchan; subchan++) {
if (subchan != best_subchan && candidate[chan][subchan].packet_p != NULL) {
ax25_delete (candidate[chan][subchan].packet_p);
candidate[chan][subchan].packet_p = NULL;
for (n = 0; n < num_bars; n++) {
j = subchan_from_n(n);
k = slice_from_n(n);
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. */
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),
j = subchan_from_n(best_n);
k = slice_from_n(best_n);
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);
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. */
candidate[chan][best_subchan].packet_p = NULL;
candidate[chan][j][k].packet_p = NULL;
/* 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;
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 pick_best_candidate */
/* 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_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

9
recv.c
View File

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

View File

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

145
rrbb.c
View File

@ -23,17 +23,14 @@
* File: rrbb.c
*
* 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.
*
* 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
* 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
@ -49,44 +46,9 @@
#include "rrbb.h"
#define MAGIC1 0x12344321
#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 delete_count = 0;
@ -102,6 +64,8 @@ static int delete_count = 0;
*
* subchan - Which demodulator of the channel.
*
* slice - multiple thresholds per demodulator.
*
* is_scrambled - Is data scrambled? (true, false)
*
* 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;
assert (SOI == 8 * sizeof(unsigned int));
assert (chan >= 0 && chan < MAX_CHANS);
assert (subchan >= 0 && subchan < MAX_SUBCHANS);
assert (slice >= 0 && slice < MAX_SLICERS);
result = malloc(sizeof(struct rrbb_s));
result->magic1 = MAGIC1;
result->chan = chan;
result->subchan = subchan;
result->slice = slice;
result->magic2 = MAGIC2;
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;
}
/***********************************************************************************
*
* Name: rrbb_append_bit
@ -192,35 +156,7 @@ void rrbb_clear (rrbb_t b, int is_scrambled, int descram_state, int prev_descram
*
***********************************************************************************/
// 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++;
}
/* Definition in header file so it can be inlined. */
/***********************************************************************************
@ -279,46 +215,9 @@ int rrbb_get_len (rrbb_t b)
*
***********************************************************************************/
int rrbb_get_bit (rrbb_t b, unsigned int ind)
{
assert (b != NULL);
assert (b->magic1 == MAGIC1);
assert (b->magic2 == MAGIC2);
/* Definition in header file so it can be inlined. */
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

42
rrbb.h
View File

@ -4,10 +4,11 @@
#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.
@ -23,13 +24,14 @@ typedef short slice_t;
#define MAX_NUM_BITS (MAX_FRAME_LEN * 8 * 6 / 5)
#define SOI 32
typedef struct rrbb_s {
int magic1;
struct rrbb_s* nextp; /* Next pointer to maintain a queue. */
int chan; /* Radio channel from which it was received. */
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. */
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 prev_descram; /* Previous descrambled bit. */
unsigned int data[(MAX_NUM_BITS+SOI-1)/SOI];
unsigned int computed_data[MAX_NUM_BITS];
unsigned char fdata[MAX_NUM_BITS];
int magic2;
} *rrbb_t;
#else
/* Hide the implementation. */
typedef void *rrbb_t;
#endif
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);
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);
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_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_subchan (rrbb_t b);
int rrbb_get_slice (rrbb_t b);
void rrbb_set_audio_level (rrbb_t b, alevel_t alevel);
alevel_t rrbb_get_audio_level (rrbb_t b);