mirror of https://github.com/wb2osz/direwolf.git
Clean up fix-bits feature. New experimental demodulator.
This commit is contained in:
parent
95c22986ec
commit
178375f3ba
|
@ -1,6 +1,15 @@
|
||||||
|
|
||||||
# Revision History #
|
# Revision History #
|
||||||
|
|
||||||
|
----------
|
||||||
|
|
||||||
|
## Version 1.3 -- Development snapshot H -- November 2015 ##
|
||||||
|
|
||||||
|
### New Feature: ###
|
||||||
|
|
||||||
|
- New experimental demodulator. More details later.
|
||||||
|
|
||||||
|
|
||||||
----------
|
----------
|
||||||
|
|
||||||
## Version 1.3 -- Development snapshot G -- November 2015 ##
|
## Version 1.3 -- Development snapshot G -- November 2015 ##
|
||||||
|
|
|
@ -238,6 +238,7 @@ strlcat.o : misc/strlcat.c
|
||||||
check : dtest ttest tttexttest pftest tlmtest lltest enctest kisstest check-modem1200 check-modem300 check-modem9600
|
check : dtest ttest tttexttest pftest tlmtest lltest enctest kisstest check-modem1200 check-modem300 check-modem9600
|
||||||
|
|
||||||
# Can we encode and decode at popular data rates?
|
# Can we encode and decode at popular data rates?
|
||||||
|
# Verify that single bit fixup increases the count.
|
||||||
|
|
||||||
check-modem1200 : gen_packets atest
|
check-modem1200 : gen_packets atest
|
||||||
gen_packets -n 100 -o test1.wav
|
gen_packets -n 100 -o test1.wav
|
||||||
|
@ -361,14 +362,13 @@ demod_9600.o : tune.h
|
||||||
demod_afsk.o : tune.h
|
demod_afsk.o : tune.h
|
||||||
|
|
||||||
|
|
||||||
testagc : atest.c demod.c dsp.c demod_afsk.c demod_9600.c fsk_demod_agc.h \
|
testagc : atest.c demod.c dsp.c demod_afsk.c demod_9600.o fsk_demod_agc.h \
|
||||||
hdlc_rec.o hdlc_rec2.o multi_modem.o \
|
hdlc_rec.o hdlc_rec2.o multi_modem.o \
|
||||||
rrbb.o fcs_calc.o ax25_pad.o decode_aprs.o latlong.o symbols.o textcolor.o telemetry.o \
|
rrbb.o fcs_calc.o ax25_pad.o decode_aprs.o latlong.o symbols.o textcolor.o telemetry.o \
|
||||||
regex.a misc.a \
|
dwgpsnmea.o dwgps.o serial_port.o tt_text.o regex.a misc.a
|
||||||
|
|
||||||
rm -f atest.exe
|
rm -f atest.exe
|
||||||
$(CC) $(CFLAGS) -o atest $^
|
$(CC) $(CFLAGS) -o atest $^
|
||||||
./atest -P E ../02_Track_2.wav | grep "packets decoded in" >atest.out
|
./atest -P GGG- -F 0 ../02_Track_2.wav | grep "packets decoded in" >atest.out
|
||||||
echo " " > tune.h
|
echo " " > tune.h
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1561,7 +1561,7 @@ static void raw_tt_data_to_app (int chan, char *msg)
|
||||||
alevel.mark = -2;
|
alevel.mark = -2;
|
||||||
alevel.space = -2;
|
alevel.space = -2;
|
||||||
|
|
||||||
dlq_append (DLQ_REC_FRAME, chan, -1, pp, alevel, RETRY_NONE, "tt");
|
dlq_append (DLQ_REC_FRAME, chan, -1, 0, pp, alevel, RETRY_NONE, "tt");
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
text_color_set(DW_COLOR_ERROR);
|
text_color_set(DW_COLOR_ERROR);
|
||||||
|
|
58
atest.c
58
atest.c
|
@ -167,6 +167,10 @@ static void usage (void);
|
||||||
|
|
||||||
static int decode_only = 0; /* Set to 0 or 1 to decode only one channel. 2 for both. */
|
static int decode_only = 0; /* Set to 0 or 1 to decode only one channel. 2 for both. */
|
||||||
|
|
||||||
|
static int sample_number = -1; /* Sample number from the file. */
|
||||||
|
/* Incremented only for channel 0. */
|
||||||
|
/* Use to print timestamp, relative to beginning */
|
||||||
|
/* of file, when frame was decoded. */
|
||||||
|
|
||||||
int main (int argc, char *argv[])
|
int main (int argc, char *argv[])
|
||||||
{
|
{
|
||||||
|
@ -495,6 +499,8 @@ int main (int argc, char *argv[])
|
||||||
if (audio_sample >= 256 * 256)
|
if (audio_sample >= 256 * 256)
|
||||||
e_o_f = 1;
|
e_o_f = 1;
|
||||||
|
|
||||||
|
if (c == 0) sample_number++;
|
||||||
|
|
||||||
if (decode_only == 0 && c != 0) continue;
|
if (decode_only == 0 && c != 0) continue;
|
||||||
if (decode_only == 1 && c != 1) continue;
|
if (decode_only == 1 && c != 1) continue;
|
||||||
|
|
||||||
|
@ -572,16 +578,16 @@ int audio_get (int a)
|
||||||
|
|
||||||
void rdq_append (rrbb_t rrbb)
|
void rdq_append (rrbb_t rrbb)
|
||||||
{
|
{
|
||||||
int chan;
|
int chan, subchan, slice;
|
||||||
alevel_t alevel;
|
alevel_t alevel;
|
||||||
int subchan;
|
|
||||||
|
|
||||||
|
|
||||||
chan = rrbb_get_chan(rrbb);
|
chan = rrbb_get_chan(rrbb);
|
||||||
subchan = rrbb_get_subchan(rrbb);
|
subchan = rrbb_get_subchan(rrbb);
|
||||||
|
slice = rrbb_get_slice(rrbb);
|
||||||
alevel = rrbb_get_audio_level(rrbb);
|
alevel = rrbb_get_audio_level(rrbb);
|
||||||
|
|
||||||
hdlc_rec2_try_to_fix_later (rrbb, chan, subchan, alevel);
|
hdlc_rec2_try_to_fix_later (rrbb, chan, subchan, slice, alevel);
|
||||||
rrbb_delete (rrbb);
|
rrbb_delete (rrbb);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -590,7 +596,7 @@ void rdq_append (rrbb_t rrbb)
|
||||||
* This is called when we have a good frame.
|
* This is called when we have a good frame.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
void dlq_append (dlq_type_t type, int chan, int subchan, packet_t pp, alevel_t alevel, retry_t retries, char *spectrum)
|
void dlq_append (dlq_type_t type, int chan, int subchan, int slice, packet_t pp, alevel_t alevel, retry_t retries, char *spectrum)
|
||||||
{
|
{
|
||||||
|
|
||||||
char stemp[500];
|
char stemp[500];
|
||||||
|
@ -627,6 +633,15 @@ void dlq_append (dlq_type_t type, int chan, int subchan, packet_t pp, alevel_t a
|
||||||
text_color_set(DW_COLOR_DEBUG);
|
text_color_set(DW_COLOR_DEBUG);
|
||||||
dw_printf ("\n");
|
dw_printf ("\n");
|
||||||
dw_printf("DECODED[%d] ", packets_decoded );
|
dw_printf("DECODED[%d] ", packets_decoded );
|
||||||
|
|
||||||
|
/* Insert time stamp relative to start of file. */
|
||||||
|
|
||||||
|
double sec = (double)sample_number / my_audio_config.adev[0].samples_per_sec;
|
||||||
|
int min = (int)(sec / 60.);
|
||||||
|
sec -= min * 60;
|
||||||
|
|
||||||
|
dw_printf ("%d:%07.4f ", min, sec);
|
||||||
|
|
||||||
if (h != AX25_SOURCE) {
|
if (h != AX25_SOURCE) {
|
||||||
dw_printf ("Digipeater ");
|
dw_printf ("Digipeater ");
|
||||||
}
|
}
|
||||||
|
@ -641,27 +656,38 @@ void dlq_append (dlq_type_t type, int chan, int subchan, packet_t pp, alevel_t a
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined(EXPERIMENT_G) || defined(EXPERIMENT_H)
|
//#if defined(EXPERIMENT_G) || defined(EXPERIMENT_H)
|
||||||
int j;
|
// int j;
|
||||||
|
//
|
||||||
for (j=0; j<MAX_SUBCHANS; j++) {
|
// for (j=0; j<MAX_SUBCHANS; j++) {
|
||||||
if (spectrum[j] == '|') {
|
// if (spectrum[j] == '|') {
|
||||||
count[j]++;
|
// count[j]++;
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
#endif
|
//#endif
|
||||||
|
|
||||||
|
|
||||||
// Display non-APRS packets in a different color.
|
// Display non-APRS packets in a different color.
|
||||||
|
|
||||||
// TODO: display subchannel if appropriate.
|
// Display channel with subchannel/slice if applicable.
|
||||||
|
|
||||||
if (ax25_is_aprs(pp)) {
|
if (ax25_is_aprs(pp)) {
|
||||||
text_color_set(DW_COLOR_REC);
|
text_color_set(DW_COLOR_REC);
|
||||||
dw_printf ("[%d] ", chan);
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
text_color_set(DW_COLOR_DEBUG);
|
text_color_set(DW_COLOR_DEBUG);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (my_audio_config.achan[chan].num_subchan > 1 && my_audio_config.achan[chan].num_slicers == 1) {
|
||||||
|
dw_printf ("[%d.%d] ", chan, subchan);
|
||||||
|
}
|
||||||
|
else if (my_audio_config.achan[chan].num_subchan == 1 && my_audio_config.achan[chan].num_slicers > 1) {
|
||||||
|
dw_printf ("[%d.%d] ", chan, slice);
|
||||||
|
}
|
||||||
|
else if (my_audio_config.achan[chan].num_subchan > 1 && my_audio_config.achan[chan].num_slicers > 1) {
|
||||||
|
dw_printf ("[%d.%d.%d] ", chan, subchan, slice);
|
||||||
|
}
|
||||||
|
else {
|
||||||
dw_printf ("[%d] ", chan);
|
dw_printf ("[%d] ", chan);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -671,7 +697,7 @@ void dlq_append (dlq_type_t type, int chan, int subchan, packet_t pp, alevel_t a
|
||||||
|
|
||||||
ax25_delete (pp);
|
ax25_delete (pp);
|
||||||
|
|
||||||
} /* end app_process_rec_packet */
|
} /* end fake dlq_append */
|
||||||
|
|
||||||
|
|
||||||
void ptt_set (int ot, int chan, int ptt_signal)
|
void ptt_set (int ot, int chan, int ptt_signal)
|
||||||
|
|
38
audio.h
38
audio.h
|
@ -41,19 +41,12 @@ enum audio_in_type_e {
|
||||||
|
|
||||||
typedef enum retry_e {
|
typedef enum retry_e {
|
||||||
RETRY_NONE=0,
|
RETRY_NONE=0,
|
||||||
RETRY_SWAP_SINGLE=1,
|
RETRY_INVERT_SINGLE=1,
|
||||||
RETRY_SWAP_DOUBLE=2,
|
RETRY_INVERT_DOUBLE=2,
|
||||||
RETRY_SWAP_TRIPLE=3,
|
RETRY_INVERT_TRIPLE=3,
|
||||||
RETRY_REMOVE_SINGLE=4,
|
RETRY_INVERT_TWO_SEP=4,
|
||||||
RETRY_REMOVE_DOUBLE=5,
|
RETRY_MAX = 5} retry_t;
|
||||||
RETRY_REMOVE_TRIPLE=6,
|
|
||||||
RETRY_INSERT_SINGLE=7,
|
|
||||||
RETRY_INSERT_DOUBLE=8,
|
|
||||||
RETRY_SWAP_TWO_SEP=9,
|
|
||||||
RETRY_SWAP_MANY=10,
|
|
||||||
RETRY_REMOVE_MANY=11,
|
|
||||||
RETRY_REMOVE_TWO_SEP=12,
|
|
||||||
RETRY_MAX = 13} retry_t;
|
|
||||||
|
|
||||||
typedef enum sanity_e { SANITY_APRS, SANITY_AX25, SANITY_NONE } sanity_t;
|
typedef enum sanity_e { SANITY_APRS, SANITY_AX25, SANITY_NONE } sanity_t;
|
||||||
|
|
||||||
|
@ -126,6 +119,9 @@ struct audio_s {
|
||||||
int decimate; /* Reduce AFSK sample rate by this factor to */
|
int decimate; /* Reduce AFSK sample rate by this factor to */
|
||||||
/* decrease computational requirements. */
|
/* decrease computational requirements. */
|
||||||
|
|
||||||
|
int interleave; /* If > 1, interleave samples among multiple decoders. */
|
||||||
|
/* Quick hack for experiment. */
|
||||||
|
|
||||||
int mark_freq; /* Two tones for AFSK modulation, in Hz. */
|
int mark_freq; /* Two tones for AFSK modulation, in Hz. */
|
||||||
int space_freq; /* Standard tones are 1200 and 2200 for 1200 baud. */
|
int space_freq; /* Standard tones are 1200 and 2200 for 1200 baud. */
|
||||||
|
|
||||||
|
@ -140,19 +136,13 @@ struct audio_s {
|
||||||
|
|
||||||
int offset; /* Spacing between filter frequencies. */
|
int offset; /* Spacing between filter frequencies. */
|
||||||
|
|
||||||
/* Next two are derived from 3 above by demod_init. */
|
int num_slicers; /* Number of different threshold points to decide */
|
||||||
|
/* between mark or space. */
|
||||||
|
|
||||||
int num_demod; /* Number of different demodulators (filters). */
|
/* This is derived from above by demod_init. */
|
||||||
/* Previously this was same as num_subchan but we add */
|
|
||||||
/* a new variation in version 1.2 where a single modem */
|
|
||||||
/* can feed multiple slicers and HDLC decoders. */
|
|
||||||
|
|
||||||
/* num_slicers could be added to be more general but */
|
int num_subchan; /* Total number of modems for each channel. */
|
||||||
/* for the intial experiment, we can just examine profiles. */
|
|
||||||
|
|
||||||
int num_subchan; /* Total number of modems / hdlc decoders for each channel. */
|
|
||||||
/* Potentially it could be product of strlen(profiles) * num_freq. */
|
|
||||||
/* Currently can't use both at once. */
|
|
||||||
|
|
||||||
/* These are for dealing with imperfect frames. */
|
/* These are for dealing with imperfect frames. */
|
||||||
|
|
||||||
|
@ -267,7 +257,7 @@ struct audio_s {
|
||||||
|
|
||||||
#define DEFAULT_BITS_PER_SAMPLE 16
|
#define DEFAULT_BITS_PER_SAMPLE 16
|
||||||
|
|
||||||
#define DEFAULT_FIX_BITS RETRY_SWAP_SINGLE
|
#define DEFAULT_FIX_BITS RETRY_INVERT_SINGLE
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Standard for AFSK on VHF FM.
|
* Standard for AFSK on VHF FM.
|
||||||
|
|
2
beacon.c
2
beacon.c
|
@ -914,7 +914,7 @@ static void beacon_send (int j, dwgps_info_t *gpsinfo)
|
||||||
/* Simulated reception. */
|
/* Simulated reception. */
|
||||||
|
|
||||||
memset (&alevel, 0xff, sizeof(alevel));
|
memset (&alevel, 0xff, sizeof(alevel));
|
||||||
dlq_append (DLQ_REC_FRAME, g_misc_config_p->beacon[j].sendto_chan, 0, pp, alevel, 0, "");
|
dlq_append (DLQ_REC_FRAME, g_misc_config_p->beacon[j].sendto_chan, 0, 0, pp, alevel, 0, "");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
18
config.c
18
config.c
|
@ -1390,14 +1390,26 @@ void config_init (char *fname, struct audio_s *p_audio_config,
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
n = atoi(t);
|
n = atoi(t);
|
||||||
if (n >= RETRY_NONE && n <= RETRY_REMOVE_TWO_SEP) {
|
if (n >= RETRY_NONE && n < RETRY_MAX) { // MAX is actually last valid +1
|
||||||
p_audio_config->achan[channel].fix_bits = (retry_t)n;
|
p_audio_config->achan[channel].fix_bits = (retry_t)n;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
p_audio_config->achan[channel].fix_bits = DEFAULT_FIX_BITS;
|
p_audio_config->achan[channel].fix_bits = DEFAULT_FIX_BITS;
|
||||||
text_color_set(DW_COLOR_ERROR);
|
text_color_set(DW_COLOR_ERROR);
|
||||||
dw_printf ("Line %d: Invalid value for FIX_BITS. Using %d.\n",
|
dw_printf ("Line %d: Invalid value %d for FIX_BITS. Using default of %d.\n",
|
||||||
line, p_audio_config->achan[channel].fix_bits);
|
line, n, p_audio_config->achan[channel].fix_bits);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (p_audio_config->achan[channel].fix_bits > RETRY_INVERT_SINGLE) {
|
||||||
|
text_color_set(DW_COLOR_INFO);
|
||||||
|
dw_printf ("Line %d: Using a FIX_BITS value greater than %d is not recommended for normal operation.\n",
|
||||||
|
line, RETRY_INVERT_SINGLE);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (p_audio_config->achan[channel].fix_bits >= RETRY_INVERT_TWO_SEP) {
|
||||||
|
text_color_set(DW_COLOR_INFO);
|
||||||
|
dw_printf ("Line %d: Using a FIX_BITS value of %d will waste a lot of CPU power and produce wrong results.\n",
|
||||||
|
line, RETRY_INVERT_TWO_SEP);
|
||||||
}
|
}
|
||||||
|
|
||||||
t = split(NULL,0);
|
t = split(NULL,0);
|
||||||
|
|
133
demod.c
133
demod.c
|
@ -119,6 +119,21 @@ int demod_init (struct audio_s *pa)
|
||||||
int num_letters;
|
int num_letters;
|
||||||
int have_plus;
|
int have_plus;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* These are derived from config file parameters.
|
||||||
|
*
|
||||||
|
* num_subchan is number of demodulators.
|
||||||
|
* This can be increased by:
|
||||||
|
* Multiple frequencies.
|
||||||
|
* Multiple letters (not sure if I will continue this).
|
||||||
|
* New interleaved decoders.
|
||||||
|
*
|
||||||
|
* num_slicers is set to max by the "+" option.
|
||||||
|
*/
|
||||||
|
|
||||||
|
save_audio_config_p->achan[chan].num_subchan = 1;
|
||||||
|
save_audio_config_p->achan[chan].num_slicers = 1;
|
||||||
|
|
||||||
switch (save_audio_config_p->achan[chan].modem_type) {
|
switch (save_audio_config_p->achan[chan].modem_type) {
|
||||||
|
|
||||||
case MODEM_OFF:
|
case MODEM_OFF:
|
||||||
|
@ -234,8 +249,7 @@ int demod_init (struct audio_s *pa)
|
||||||
/* These can be increased later for the multi-frequency case. */
|
/* These can be increased later for the multi-frequency case. */
|
||||||
|
|
||||||
save_audio_config_p->achan[chan].num_subchan = num_letters;
|
save_audio_config_p->achan[chan].num_subchan = num_letters;
|
||||||
save_audio_config_p->achan[chan].num_demod = num_letters;
|
save_audio_config_p->achan[chan].num_slicers = 1;
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Some error checking - Can use only one of these:
|
* Some error checking - Can use only one of these:
|
||||||
|
@ -245,24 +259,10 @@ int demod_init (struct audio_s *pa)
|
||||||
* - Multiple frequencies.
|
* - Multiple frequencies.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
if (have_plus && num_letters > 1) {
|
|
||||||
|
|
||||||
text_color_set(DW_COLOR_ERROR);
|
|
||||||
dw_printf ("Channel %d: Demodulator + option can't be combined with multiple letters.\n", chan);
|
|
||||||
|
|
||||||
strlcpy (save_audio_config_p->achan[chan].profiles, "C+", sizeof(save_audio_config_p->achan[chan].profiles)); // Reduce to one letter.
|
|
||||||
num_letters = 1;
|
|
||||||
save_audio_config_p->achan[chan].num_demod = 1;
|
|
||||||
save_audio_config_p->achan[chan].num_subchan = 1; // Will be set higher later.
|
|
||||||
save_audio_config_p->achan[chan].num_freq = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (have_plus && save_audio_config_p->achan[chan].num_freq > 1) {
|
if (have_plus && save_audio_config_p->achan[chan].num_freq > 1) {
|
||||||
|
|
||||||
text_color_set(DW_COLOR_ERROR);
|
text_color_set(DW_COLOR_ERROR);
|
||||||
dw_printf ("Channel %d: Demodulator + option can't be combined with multiple frequencies.\n", chan);
|
dw_printf ("Channel %d: Demodulator + option can't be combined with multiple frequencies.\n", chan);
|
||||||
|
|
||||||
save_audio_config_p->achan[chan].num_demod = 1;
|
|
||||||
save_audio_config_p->achan[chan].num_subchan = 1; // Will be set higher later.
|
save_audio_config_p->achan[chan].num_subchan = 1; // Will be set higher later.
|
||||||
save_audio_config_p->achan[chan].num_freq = 1;
|
save_audio_config_p->achan[chan].num_freq = 1;
|
||||||
}
|
}
|
||||||
|
@ -302,6 +302,7 @@ int demod_init (struct audio_s *pa)
|
||||||
* We have 3 cases to consider.
|
* We have 3 cases to consider.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
// TODO1.3: revisit this logic now that it is less restrictive.
|
||||||
|
|
||||||
if (num_letters > 1) {
|
if (num_letters > 1) {
|
||||||
int d;
|
int d;
|
||||||
|
@ -311,17 +312,58 @@ int demod_init (struct audio_s *pa)
|
||||||
* Each one corresponds to a demodulator and subchannel.
|
* Each one corresponds to a demodulator and subchannel.
|
||||||
*
|
*
|
||||||
* An interesting experiment but probably not too useful.
|
* An interesting experiment but probably not too useful.
|
||||||
* Can't have multiple frequency pairs or the + option.
|
* Can't have multiple frequency pairs.
|
||||||
|
* In version 1.3 this can be combined with the + option.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
save_audio_config_p->achan[chan].num_subchan = num_letters;
|
save_audio_config_p->achan[chan].num_subchan = num_letters;
|
||||||
save_audio_config_p->achan[chan].num_demod = num_letters;
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Quick hack with special case for another experiment.
|
||||||
|
* Do this in a more general way if it turns out to be useful.
|
||||||
|
*/
|
||||||
|
save_audio_config_p->achan[chan].interleave = 1;
|
||||||
|
if (strcasecmp(save_audio_config_p->achan[chan].profiles, "EE") == 0) {
|
||||||
|
save_audio_config_p->achan[chan].interleave = 2;
|
||||||
|
save_audio_config_p->achan[chan].decimate = 1;
|
||||||
|
}
|
||||||
|
else if (strcasecmp(save_audio_config_p->achan[chan].profiles, "EEE") == 0) {
|
||||||
|
save_audio_config_p->achan[chan].interleave = 3;
|
||||||
|
save_audio_config_p->achan[chan].decimate = 1;
|
||||||
|
}
|
||||||
|
else if (strcasecmp(save_audio_config_p->achan[chan].profiles, "EEEE") == 0) {
|
||||||
|
save_audio_config_p->achan[chan].interleave = 4;
|
||||||
|
save_audio_config_p->achan[chan].decimate = 1;
|
||||||
|
}
|
||||||
|
else if (strcasecmp(save_audio_config_p->achan[chan].profiles, "EEEEE") == 0) {
|
||||||
|
save_audio_config_p->achan[chan].interleave = 5;
|
||||||
|
save_audio_config_p->achan[chan].decimate = 1;
|
||||||
|
}
|
||||||
|
else if (strcasecmp(save_audio_config_p->achan[chan].profiles, "GG") == 0) {
|
||||||
|
save_audio_config_p->achan[chan].interleave = 2;
|
||||||
|
save_audio_config_p->achan[chan].decimate = 1;
|
||||||
|
}
|
||||||
|
else if (strcasecmp(save_audio_config_p->achan[chan].profiles, "GGG") == 0) {
|
||||||
|
save_audio_config_p->achan[chan].interleave = 3;
|
||||||
|
save_audio_config_p->achan[chan].decimate = 1;
|
||||||
|
}
|
||||||
|
else if (strcasecmp(save_audio_config_p->achan[chan].profiles, "GGG+") == 0) {
|
||||||
|
save_audio_config_p->achan[chan].interleave = 3;
|
||||||
|
save_audio_config_p->achan[chan].decimate = 1;
|
||||||
|
}
|
||||||
|
else if (strcasecmp(save_audio_config_p->achan[chan].profiles, "GGGG") == 0) {
|
||||||
|
save_audio_config_p->achan[chan].interleave = 4;
|
||||||
|
save_audio_config_p->achan[chan].decimate = 1;
|
||||||
|
}
|
||||||
|
else if (strcasecmp(save_audio_config_p->achan[chan].profiles, "GGGGG") == 0) {
|
||||||
|
save_audio_config_p->achan[chan].interleave = 5;
|
||||||
|
save_audio_config_p->achan[chan].decimate = 1;
|
||||||
|
}
|
||||||
|
|
||||||
if (save_audio_config_p->achan[chan].num_demod != num_letters) {
|
if (save_audio_config_p->achan[chan].num_subchan != num_letters) {
|
||||||
text_color_set(DW_COLOR_ERROR);
|
text_color_set(DW_COLOR_ERROR);
|
||||||
dw_printf ("INTERNAL ERROR, %s:%d, chan=%d, num_demod(%d) != strlen(\"%s\")\n",
|
dw_printf ("INTERNAL ERROR, %s:%d, chan=%d, num_subchan(%d) != strlen(\"%s\")\n",
|
||||||
__FILE__, __LINE__, chan, save_audio_config_p->achan[chan].num_demod, save_audio_config_p->achan[chan].profiles);
|
__FILE__, __LINE__, chan, save_audio_config_p->achan[chan].num_subchan, save_audio_config_p->achan[chan].profiles);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (save_audio_config_p->achan[chan].num_freq != 1) {
|
if (save_audio_config_p->achan[chan].num_freq != 1) {
|
||||||
|
@ -330,8 +372,7 @@ int demod_init (struct audio_s *pa)
|
||||||
__FILE__, __LINE__, chan, save_audio_config_p->achan[chan].num_freq);
|
__FILE__, __LINE__, chan, save_audio_config_p->achan[chan].num_freq);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (d = 0; d < save_audio_config_p->achan[chan].num_demod; d++) {
|
for (d = 0; d < save_audio_config_p->achan[chan].num_subchan; d++) {
|
||||||
|
|
||||||
int mark, space;
|
int mark, space;
|
||||||
assert (d >= 0 && d < MAX_SUBCHANS);
|
assert (d >= 0 && d < MAX_SUBCHANS);
|
||||||
|
|
||||||
|
@ -342,18 +383,26 @@ int demod_init (struct audio_s *pa)
|
||||||
mark = save_audio_config_p->achan[chan].mark_freq;
|
mark = save_audio_config_p->achan[chan].mark_freq;
|
||||||
space = save_audio_config_p->achan[chan].space_freq;
|
space = save_audio_config_p->achan[chan].space_freq;
|
||||||
|
|
||||||
if (save_audio_config_p->achan[chan].num_demod != 1) {
|
if (save_audio_config_p->achan[chan].num_subchan != 1) {
|
||||||
text_color_set(DW_COLOR_DEBUG);
|
text_color_set(DW_COLOR_DEBUG);
|
||||||
dw_printf (" %d.%d: %c %d & %d\n", chan, d, profile, mark, space);
|
dw_printf (" %d.%d: %c %d & %d\n", chan, d, profile, mark, space);
|
||||||
}
|
}
|
||||||
|
|
||||||
demod_afsk_init (save_audio_config_p->adev[ACHAN2ADEV(chan)].samples_per_sec / save_audio_config_p->achan[chan].decimate,
|
demod_afsk_init (save_audio_config_p->adev[ACHAN2ADEV(chan)].samples_per_sec / (save_audio_config_p->achan[chan].decimate * save_audio_config_p->achan[chan].interleave),
|
||||||
save_audio_config_p->achan[chan].baud,
|
save_audio_config_p->achan[chan].baud,
|
||||||
mark,
|
mark,
|
||||||
space,
|
space,
|
||||||
profile,
|
profile,
|
||||||
D);
|
D);
|
||||||
|
|
||||||
|
if (have_plus) {
|
||||||
|
/* I'm not happy about putting this hack here. */
|
||||||
|
/* should pass in as a parameter rather than adding on later. */
|
||||||
|
|
||||||
|
save_audio_config_p->achan[chan].num_slicers = MAX_SLICERS;
|
||||||
|
D->num_slicers = MAX_SLICERS;
|
||||||
|
}
|
||||||
|
|
||||||
/* For siginal level reporting, we want a longer term view. */
|
/* For siginal level reporting, we want a longer term view. */
|
||||||
// TODO: Should probably move this into the init functions.
|
// TODO: Should probably move this into the init functions.
|
||||||
|
|
||||||
|
@ -364,7 +413,7 @@ int demod_init (struct audio_s *pa)
|
||||||
else if (have_plus) {
|
else if (have_plus) {
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* PLUS - which implies we have only one letter and one frequency pair.
|
* PLUS - which (formerly) implies we have only one letter and one frequency pair.
|
||||||
*
|
*
|
||||||
* One demodulator feeds multiple slicers, each a subchannel.
|
* One demodulator feeds multiple slicers, each a subchannel.
|
||||||
*/
|
*/
|
||||||
|
@ -381,21 +430,19 @@ int demod_init (struct audio_s *pa)
|
||||||
__FILE__, __LINE__, chan, save_audio_config_p->achan[chan].num_freq);
|
__FILE__, __LINE__, chan, save_audio_config_p->achan[chan].num_freq);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (save_audio_config_p->achan[chan].num_demod != save_audio_config_p->achan[chan].num_demod) {
|
if (save_audio_config_p->achan[chan].num_freq != save_audio_config_p->achan[chan].num_subchan) {
|
||||||
text_color_set(DW_COLOR_ERROR);
|
text_color_set(DW_COLOR_ERROR);
|
||||||
dw_printf ("INTERNAL ERROR, %s:%d, chan=%d, num_freq(%d) != num_demod(%d)\n",
|
dw_printf ("INTERNAL ERROR, %s:%d, chan=%d, num_freq(%d) != num_subchan(%d)\n",
|
||||||
__FILE__, __LINE__, chan, save_audio_config_p->achan[chan].num_freq, save_audio_config_p->achan[chan].num_demod);
|
__FILE__, __LINE__, chan, save_audio_config_p->achan[chan].num_freq, save_audio_config_p->achan[chan].num_subchan);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
struct demodulator_state_s *D;
|
struct demodulator_state_s *D;
|
||||||
D = &demodulator_state[chan][0];
|
D = &demodulator_state[chan][0];
|
||||||
|
|
||||||
/* I'm not happy about putting this hack here. */
|
/* I'm not happy about putting this hack here. */
|
||||||
/* This belongs in demod_afsk_init but it doesn't have access to the audio config. */
|
/* This belongs in demod_afsk_init but it doesn't have access to the audio config. */
|
||||||
|
|
||||||
save_audio_config_p->achan[chan].num_demod = 1;
|
save_audio_config_p->achan[chan].num_slicers = MAX_SLICERS;
|
||||||
save_audio_config_p->achan[chan].num_subchan = MAX_SUBCHANS;
|
|
||||||
|
|
||||||
demod_afsk_init (save_audio_config_p->adev[ACHAN2ADEV(chan)].samples_per_sec / save_audio_config_p->achan[chan].decimate,
|
demod_afsk_init (save_audio_config_p->adev[ACHAN2ADEV(chan)].samples_per_sec / save_audio_config_p->achan[chan].decimate,
|
||||||
save_audio_config_p->achan[chan].baud,
|
save_audio_config_p->achan[chan].baud,
|
||||||
|
@ -404,10 +451,13 @@ int demod_init (struct audio_s *pa)
|
||||||
save_audio_config_p->achan[chan].profiles[0],
|
save_audio_config_p->achan[chan].profiles[0],
|
||||||
D);
|
D);
|
||||||
|
|
||||||
|
if (have_plus) {
|
||||||
/* I'm not happy about putting this hack here. */
|
/* I'm not happy about putting this hack here. */
|
||||||
/* should pass in as a parameter rather than adding on later. */
|
/* should pass in as a parameter rather than adding on later. */
|
||||||
|
|
||||||
D->num_slicers = MAX_SUBCHANS;
|
save_audio_config_p->achan[chan].num_slicers = MAX_SLICERS;
|
||||||
|
D->num_slicers = MAX_SLICERS;
|
||||||
|
}
|
||||||
|
|
||||||
/* For siginal level reporting, we want a longer term view. */
|
/* For siginal level reporting, we want a longer term view. */
|
||||||
|
|
||||||
|
@ -427,7 +477,6 @@ int demod_init (struct audio_s *pa)
|
||||||
__FILE__, __LINE__, chan, save_audio_config_p->achan[chan].profiles);
|
__FILE__, __LINE__, chan, save_audio_config_p->achan[chan].profiles);
|
||||||
}
|
}
|
||||||
|
|
||||||
save_audio_config_p->achan[chan].num_demod = save_audio_config_p->achan[chan].num_freq;
|
|
||||||
save_audio_config_p->achan[chan].num_subchan = save_audio_config_p->achan[chan].num_freq;
|
save_audio_config_p->achan[chan].num_subchan = save_audio_config_p->achan[chan].num_freq;
|
||||||
|
|
||||||
for (d = 0; d < save_audio_config_p->achan[chan].num_freq; d++) {
|
for (d = 0; d < save_audio_config_p->achan[chan].num_freq; d++) {
|
||||||
|
@ -455,6 +504,14 @@ int demod_init (struct audio_s *pa)
|
||||||
profile,
|
profile,
|
||||||
D);
|
D);
|
||||||
|
|
||||||
|
if (have_plus) {
|
||||||
|
/* I'm not happy about putting this hack here. */
|
||||||
|
/* should pass in as a parameter rather than adding on later. */
|
||||||
|
|
||||||
|
save_audio_config_p->achan[chan].num_slicers = MAX_SLICERS;
|
||||||
|
D->num_slicers = MAX_SLICERS;
|
||||||
|
}
|
||||||
|
|
||||||
/* For siginal level reporting, we want a longer term view. */
|
/* For siginal level reporting, we want a longer term view. */
|
||||||
|
|
||||||
D->quick_attack = D->agc_fast_attack * 0.2;
|
D->quick_attack = D->agc_fast_attack * 0.2;
|
||||||
|
@ -495,15 +552,14 @@ int demod_init (struct audio_s *pa)
|
||||||
D = &demodulator_state[chan][0]; // first subchannel
|
D = &demodulator_state[chan][0]; // first subchannel
|
||||||
|
|
||||||
save_audio_config_p->achan[chan].num_subchan = 1;
|
save_audio_config_p->achan[chan].num_subchan = 1;
|
||||||
save_audio_config_p->achan[chan].num_demod = 1;
|
save_audio_config_p->achan[chan].num_slicers = 1;
|
||||||
|
|
||||||
if (strchr(save_audio_config_p->achan[chan].profiles, '+') != NULL) {
|
if (strchr(save_audio_config_p->achan[chan].profiles, '+') != NULL) {
|
||||||
|
|
||||||
/* I'm not happy about putting this hack here. */
|
/* I'm not happy about putting this hack here. */
|
||||||
/* This belongs in demod_9600_init but it doesn't have access to the audio config. */
|
/* This belongs in demod_9600_init but it doesn't have access to the audio config. */
|
||||||
|
|
||||||
save_audio_config_p->achan[chan].num_demod = 1;
|
save_audio_config_p->achan[chan].num_slicers = MAX_SLICERS;
|
||||||
save_audio_config_p->achan[chan].num_subchan = MAX_SUBCHANS;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
demod_9600_init (UPSAMPLE * save_audio_config_p->adev[ACHAN2ADEV(chan)].samples_per_sec, save_audio_config_p->achan[chan].baud, D);
|
demod_9600_init (UPSAMPLE * save_audio_config_p->adev[ACHAN2ADEV(chan)].samples_per_sec, save_audio_config_p->achan[chan].baud, D);
|
||||||
|
@ -513,7 +569,8 @@ int demod_init (struct audio_s *pa)
|
||||||
/* I'm not happy about putting this hack here. */
|
/* I'm not happy about putting this hack here. */
|
||||||
/* should pass in as a parameter rather than adding on later. */
|
/* should pass in as a parameter rather than adding on later. */
|
||||||
|
|
||||||
D->num_slicers = MAX_SUBCHANS;
|
save_audio_config_p->achan[chan].num_slicers = MAX_SLICERS;
|
||||||
|
D->num_slicers = MAX_SLICERS;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* For siginal level reporting, we want a longer term view. */
|
/* For siginal level reporting, we want a longer term view. */
|
||||||
|
|
41
demod_9600.c
41
demod_9600.c
|
@ -145,6 +145,7 @@ void demod_9600_init (int samples_per_sec, int baud, struct demodulator_state_s
|
||||||
int j;
|
int j;
|
||||||
|
|
||||||
memset (D, 0, sizeof(struct demodulator_state_s));
|
memset (D, 0, sizeof(struct demodulator_state_s));
|
||||||
|
D->num_slicers = 1;
|
||||||
|
|
||||||
//dw_printf ("demod_9600_init(rate=%d, baud=%d, D ptr)\n", samples_per_sec, baud);
|
//dw_printf ("demod_9600_init(rate=%d, baud=%d, D ptr)\n", samples_per_sec, baud);
|
||||||
|
|
||||||
|
@ -259,7 +260,7 @@ void demod_9600_init (int samples_per_sec, int baud, struct demodulator_state_s
|
||||||
*
|
*
|
||||||
*--------------------------------------------------------------------*/
|
*--------------------------------------------------------------------*/
|
||||||
|
|
||||||
static void nudge_pll (int chan, int subchan, int demod_data, struct demodulator_state_s *D);
|
static void inline nudge_pll (int chan, int subchan, int slice, int demod_data, struct demodulator_state_s *D);
|
||||||
|
|
||||||
__attribute__((hot))
|
__attribute__((hot))
|
||||||
void demod_9600_process_sample (int chan, int sam, struct demodulator_state_s *D)
|
void demod_9600_process_sample (int chan, int sam, struct demodulator_state_s *D)
|
||||||
|
@ -377,19 +378,16 @@ void demod_9600_process_sample (int chan, int sam, struct demodulator_state_s *D
|
||||||
/* AGC should generally keep this around -1 to +1 range. */
|
/* AGC should generally keep this around -1 to +1 range. */
|
||||||
|
|
||||||
demod_data = demod_out > 0;
|
demod_data = demod_out > 0;
|
||||||
|
nudge_pll (chan, subchan, 0, demod_data, D);
|
||||||
nudge_pll (chan, subchan, demod_data, D);
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
int s;
|
int slice;
|
||||||
|
|
||||||
assert (subchan == 0);
|
|
||||||
|
|
||||||
/* Multiple slicers each feeding its own HDLC decoder. */
|
/* Multiple slicers each feeding its own HDLC decoder. */
|
||||||
|
|
||||||
for (s=0; s<D->num_slicers; s++) {
|
for (slice=0; slice<D->num_slicers; slice++) {
|
||||||
demod_data = demod_out > slice_point[s];
|
demod_data = demod_out > slice_point[slice];
|
||||||
nudge_pll (chan, s, demod_data, D);
|
nudge_pll (chan, subchan, slice, demod_data, D);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -397,7 +395,7 @@ void demod_9600_process_sample (int chan, int sam, struct demodulator_state_s *D
|
||||||
|
|
||||||
|
|
||||||
__attribute__((hot))
|
__attribute__((hot))
|
||||||
static void nudge_pll (int chan, int subchan, int demod_data, struct demodulator_state_s *D)
|
static void inline nudge_pll (int chan, int subchan, int slice, int demod_data, struct demodulator_state_s *D)
|
||||||
{
|
{
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -425,10 +423,11 @@ static void nudge_pll (int chan, int subchan, int demod_data, struct demodulator
|
||||||
* This was optimized for 1200 baud AFSK. There might be some opportunity
|
* This was optimized for 1200 baud AFSK. There might be some opportunity
|
||||||
* for improvement here.
|
* for improvement here.
|
||||||
*/
|
*/
|
||||||
D->slicer[subchan].prev_d_c_pll = D->slicer[subchan].data_clock_pll;
|
|
||||||
D->slicer[subchan].data_clock_pll += D->pll_step_per_sample;
|
|
||||||
|
|
||||||
if (D->slicer[subchan].data_clock_pll < 0 && D->slicer[subchan].prev_d_c_pll > 0) {
|
D->slicer[slice].prev_d_c_pll = D->slicer[slice].data_clock_pll;
|
||||||
|
D->slicer[slice].data_clock_pll += D->pll_step_per_sample;
|
||||||
|
|
||||||
|
if (D->slicer[slice].data_clock_pll < 0 && D->slicer[slice].prev_d_c_pll > 0) {
|
||||||
|
|
||||||
/* Overflow. */
|
/* Overflow. */
|
||||||
|
|
||||||
|
@ -443,20 +442,20 @@ static void nudge_pll (int chan, int subchan, int demod_data, struct demodulator
|
||||||
// Warning: 'descram' set but not used.
|
// Warning: 'descram' set but not used.
|
||||||
// It's used in conditional debug code below.
|
// It's used in conditional debug code below.
|
||||||
// descram =
|
// descram =
|
||||||
descramble (demod_data, &(D->slicer[subchan].lfsr));
|
descramble (demod_data, &(D->slicer[slice].lfsr));
|
||||||
|
|
||||||
hdlc_rec_bit (chan, subchan, demod_data, 1, D->slicer[subchan].lfsr);
|
hdlc_rec_bit (chan, subchan, slice, demod_data, 1, D->slicer[slice].lfsr);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (demod_data != D->slicer[subchan].prev_demod_data) {
|
if (demod_data != D->slicer[slice].prev_demod_data) {
|
||||||
|
|
||||||
// Note: Test for this demodulator, not overall for channel.
|
// Note: Test for this demodulator, not overall for channel.
|
||||||
|
|
||||||
if (hdlc_rec_gathering (chan, subchan)) {
|
if (hdlc_rec_gathering (chan, subchan, slice)) {
|
||||||
D->slicer[subchan].data_clock_pll = (int)(D->slicer[subchan].data_clock_pll * D->pll_locked_inertia);
|
D->slicer[slice].data_clock_pll = (int)(D->slicer[slice].data_clock_pll * D->pll_locked_inertia);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
D->slicer[subchan].data_clock_pll = (int)(D->slicer[subchan].data_clock_pll * D->pll_searching_inertia);
|
D->slicer[slice].data_clock_pll = (int)(D->slicer[slice].data_clock_pll * D->pll_searching_inertia);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -464,7 +463,7 @@ static void nudge_pll (int chan, int subchan, int demod_data, struct demodulator
|
||||||
#if DEBUG5
|
#if DEBUG5
|
||||||
|
|
||||||
//if (chan == 0) {
|
//if (chan == 0) {
|
||||||
if (hdlc_rec_gathering (chan,subchan)) {
|
if (hdlc_rec_gathering (chan,subchan,slice)) {
|
||||||
|
|
||||||
char fname[30];
|
char fname[30];
|
||||||
|
|
||||||
|
@ -507,7 +506,7 @@ static void nudge_pll (int chan, int subchan, int demod_data, struct demodulator
|
||||||
* Remember demodulator output (pre-descrambling) so we can compare next time
|
* Remember demodulator output (pre-descrambling) so we can compare next time
|
||||||
* for the DPLL sync.
|
* for the DPLL sync.
|
||||||
*/
|
*/
|
||||||
D->slicer[subchan].prev_demod_data = demod_data;
|
D->slicer[slice].prev_demod_data = demod_data;
|
||||||
|
|
||||||
} /* end nudge_pll */
|
} /* end nudge_pll */
|
||||||
|
|
||||||
|
|
77
demod_afsk.c
77
demod_afsk.c
|
@ -198,6 +198,7 @@ void demod_afsk_init (int samples_per_sec, int baud, int mark_freq,
|
||||||
int j;
|
int j;
|
||||||
|
|
||||||
memset (D, 0, sizeof(struct demodulator_state_s));
|
memset (D, 0, sizeof(struct demodulator_state_s));
|
||||||
|
D->num_slicers = 1;
|
||||||
|
|
||||||
#if DEBUG1
|
#if DEBUG1
|
||||||
dw_printf ("demod_afsk_init (rate=%d, baud=%d, mark=%d, space=%d, profile=%c\n",
|
dw_printf ("demod_afsk_init (rate=%d, baud=%d, mark=%d, space=%d, profile=%c\n",
|
||||||
|
@ -332,7 +333,7 @@ void demod_afsk_init (int samples_per_sec, int baud, int mark_freq,
|
||||||
case 'E':
|
case 'E':
|
||||||
|
|
||||||
/* 1200 baud - Started out similar to C but add prefilter. */
|
/* 1200 baud - Started out similar to C but add prefilter. */
|
||||||
/* Version 1.2 - EXPERIMENTAL - Needs more fine tuning. */
|
/* Version 1.2 */
|
||||||
/* Enhancements: */
|
/* Enhancements: */
|
||||||
/* + Add prefilter. Previously used for 300 baud D, but not 1200. */
|
/* + Add prefilter. Previously used for 300 baud D, but not 1200. */
|
||||||
/* + Prefilter length now independent of M/S filters. */
|
/* + Prefilter length now independent of M/S filters. */
|
||||||
|
@ -366,6 +367,34 @@ void demod_afsk_init (int samples_per_sec, int baud, int mark_freq,
|
||||||
D->pll_searching_inertia = 0.50;
|
D->pll_searching_inertia = 0.50;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case 'G':
|
||||||
|
|
||||||
|
/* 1200 baud - Started out same as E but add 3 way interleave. */
|
||||||
|
/* Version 1.3 - EXPERIMENTAL - Needs more fine tuning. */
|
||||||
|
|
||||||
|
//D->bp_window = BP_WINDOW_COSINE; /* The name says BP but it is used for all of them. */
|
||||||
|
|
||||||
|
D->use_prefilter = 1; /* first, a bandpass filter. */
|
||||||
|
D->prefilter_baud = 0.15;
|
||||||
|
D->pre_filter_len_bits = 128 * 1200. / (44100. / 3.);
|
||||||
|
D->pre_window = BP_WINDOW_TRUNCATED;
|
||||||
|
|
||||||
|
D->ms_filter_len_bits = 25 * 1200. / (44100. / 3.);
|
||||||
|
D->ms_window = BP_WINDOW_COSINE;
|
||||||
|
|
||||||
|
D->lpf_use_fir = 1;
|
||||||
|
D->lpf_baud = 1.16;
|
||||||
|
D->lp_filter_len_bits = 21 * 1200. / (44100. / 3.);
|
||||||
|
D->lp_window = BP_WINDOW_TRUNCATED;
|
||||||
|
|
||||||
|
D->agc_fast_attack = 0.130;
|
||||||
|
D->agc_slow_decay = 0.00013;
|
||||||
|
D->hysteresis = 0.01;
|
||||||
|
|
||||||
|
D->pll_locked_inertia = 0.73;
|
||||||
|
D->pll_searching_inertia = 0.64;
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
|
||||||
text_color_set(DW_COLOR_ERROR);
|
text_color_set(DW_COLOR_ERROR);
|
||||||
|
@ -719,8 +748,8 @@ int main ()
|
||||||
modem.achan[0].mark_freq = DEFAULT_MARK_FREQ;
|
modem.achan[0].mark_freq = DEFAULT_MARK_FREQ;
|
||||||
modem.achan[0].space_freq = DEFAULT_SPACE_FREQ;
|
modem.achan[0].space_freq = DEFAULT_SPACE_FREQ;
|
||||||
modem.achan[0].baud = DEFAULT_BAUD;
|
modem.achan[0].baud = DEFAULT_BAUD;
|
||||||
modem.achan[0].num_demod = 1;
|
|
||||||
modem.achan[0].num_subchan = 1;
|
modem.achan[0].num_subchan = 1;
|
||||||
|
modem.achan[0].num_slicers = 1;
|
||||||
|
|
||||||
|
|
||||||
demod_afsk_init (modem.adev[0].samples_per_sec, modem.achan[0].baud,
|
demod_afsk_init (modem.adev[0].samples_per_sec, modem.achan[0].baud,
|
||||||
|
@ -792,7 +821,7 @@ int main ()
|
||||||
*
|
*
|
||||||
*--------------------------------------------------------------------*/
|
*--------------------------------------------------------------------*/
|
||||||
|
|
||||||
static void nudge_pll (int chan, int subchan, int demod_data, struct demodulator_state_s *D);
|
static void inline nudge_pll (int chan, int subchan, int slice, int demod_data, struct demodulator_state_s *D);
|
||||||
|
|
||||||
__attribute__((hot))
|
__attribute__((hot))
|
||||||
void demod_afsk_process_sample (int chan, int subchan, int sam, struct demodulator_state_s *D)
|
void demod_afsk_process_sample (int chan, int subchan, int sam, struct demodulator_state_s *D)
|
||||||
|
@ -1023,26 +1052,18 @@ void demod_afsk_process_sample (int chan, int subchan, int sam, struct demodulat
|
||||||
else {
|
else {
|
||||||
demod_data = D->slicer[subchan].prev_demod_data;
|
demod_data = D->slicer[subchan].prev_demod_data;
|
||||||
}
|
}
|
||||||
|
nudge_pll (chan, subchan, 0, demod_data, D);
|
||||||
nudge_pll (chan, subchan, demod_data, D);
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
int s;
|
int slice;
|
||||||
|
|
||||||
assert (subchan == 0);
|
for (slice=0; slice<D->num_slicers; slice++) {
|
||||||
|
demod_data = m_amp > s_amp * space_gain[slice];
|
||||||
/* "G" profile with one demodulator and multiple slicers */
|
nudge_pll (chan, subchan, slice, demod_data, D);
|
||||||
/* each feeding its own HDLC decoder. */
|
|
||||||
|
|
||||||
for (s=0; s<D->num_slicers; s++) {
|
|
||||||
demod_data = m_amp > s_amp * space_gain[s];
|
|
||||||
nudge_pll (chan, s, demod_data, D);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#if DEBUG4
|
#if DEBUG4
|
||||||
|
|
||||||
if (chan == 0) {
|
if (chan == 0) {
|
||||||
|
@ -1080,12 +1101,15 @@ void demod_afsk_process_sample (int chan, int subchan, int sam, struct demodulat
|
||||||
|
|
||||||
|
|
||||||
__attribute__((hot))
|
__attribute__((hot))
|
||||||
static void nudge_pll (int chan, int subchan, int demod_data, struct demodulator_state_s *D)
|
static void inline nudge_pll (int chan, int subchan, int slice, int demod_data, struct demodulator_state_s *D)
|
||||||
{
|
{
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Finally, a PLL is used to sample near the centers of the data bits.
|
* Finally, a PLL is used to sample near the centers of the data bits.
|
||||||
*
|
*
|
||||||
|
* D points to a demodulator for a channel/subchannel pair so we don't
|
||||||
|
* have to keep recalculating it.
|
||||||
|
*
|
||||||
* D->data_clock_pll is a SIGNED 32 bit variable.
|
* D->data_clock_pll is a SIGNED 32 bit variable.
|
||||||
* When it overflows from a large positive value to a negative value, we
|
* When it overflows from a large positive value to a negative value, we
|
||||||
* sample a data bit from the demodulated signal.
|
* sample a data bit from the demodulated signal.
|
||||||
|
@ -1109,33 +1133,34 @@ static void nudge_pll (int chan, int subchan, int demod_data, struct demodulator
|
||||||
* I don't think the optimal value will depend on the audio sample rate
|
* I don't think the optimal value will depend on the audio sample rate
|
||||||
* because this happens for each transition from the demodulator.
|
* because this happens for each transition from the demodulator.
|
||||||
*/
|
*/
|
||||||
D->slicer[subchan].prev_d_c_pll = D->slicer[subchan].data_clock_pll;
|
|
||||||
D->slicer[subchan].data_clock_pll += D->pll_step_per_sample;
|
D->slicer[slice].prev_d_c_pll = D->slicer[slice].data_clock_pll;
|
||||||
|
D->slicer[slice].data_clock_pll += D->pll_step_per_sample;
|
||||||
|
|
||||||
//text_color_set(DW_COLOR_DEBUG);
|
//text_color_set(DW_COLOR_DEBUG);
|
||||||
// dw_printf ("prev = %lx, new data clock pll = %lx\n" D->prev_d_c_pll, D->data_clock_pll);
|
// dw_printf ("prev = %lx, new data clock pll = %lx\n" D->prev_d_c_pll, D->data_clock_pll);
|
||||||
|
|
||||||
if (D->slicer[subchan].data_clock_pll < 0 && D->slicer[subchan].prev_d_c_pll > 0) {
|
if (D->slicer[slice].data_clock_pll < 0 && D->slicer[slice].prev_d_c_pll > 0) {
|
||||||
|
|
||||||
/* Overflow. */
|
/* Overflow. */
|
||||||
|
|
||||||
hdlc_rec_bit (chan, subchan, demod_data, 0, -1);
|
hdlc_rec_bit (chan, subchan, slice, demod_data, 0, -1);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (demod_data != D->slicer[subchan].prev_demod_data) {
|
if (demod_data != D->slicer[slice].prev_demod_data) {
|
||||||
|
|
||||||
if (hdlc_rec_gathering (chan, subchan)) {
|
if (hdlc_rec_gathering (chan, subchan, slice)) {
|
||||||
D->slicer[subchan].data_clock_pll = (int)(D->slicer[subchan].data_clock_pll * D->pll_locked_inertia);
|
D->slicer[slice].data_clock_pll = (int)(D->slicer[slice].data_clock_pll * D->pll_locked_inertia);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
D->slicer[subchan].data_clock_pll = (int)(D->slicer[subchan].data_clock_pll * D->pll_searching_inertia);
|
D->slicer[slice].data_clock_pll = (int)(D->slicer[slice].data_clock_pll * D->pll_searching_inertia);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Remember demodulator output so we can compare next time.
|
* Remember demodulator output so we can compare next time.
|
||||||
*/
|
*/
|
||||||
D->slicer[subchan].prev_demod_data = demod_data;
|
D->slicer[slice].prev_demod_data = demod_data;
|
||||||
|
|
||||||
} /* end nudge_pll */
|
} /* end nudge_pll */
|
||||||
|
|
||||||
|
|
16
direwolf.c
16
direwolf.c
|
@ -231,7 +231,7 @@ int main (int argc, char *argv[])
|
||||||
text_color_init(t_opt);
|
text_color_init(t_opt);
|
||||||
text_color_set(DW_COLOR_INFO);
|
text_color_set(DW_COLOR_INFO);
|
||||||
//dw_printf ("Dire Wolf version %d.%d (%s) Beta Test\n", MAJOR_VERSION, MINOR_VERSION, __DATE__);
|
//dw_printf ("Dire Wolf version %d.%d (%s) Beta Test\n", MAJOR_VERSION, MINOR_VERSION, __DATE__);
|
||||||
dw_printf ("Dire Wolf DEVELOPMENT version %d.%d %s (%s)\n", MAJOR_VERSION, MINOR_VERSION, "G", __DATE__);
|
dw_printf ("Dire Wolf DEVELOPMENT version %d.%d %s (%s)\n", MAJOR_VERSION, MINOR_VERSION, "H", __DATE__);
|
||||||
//dw_printf ("Dire Wolf version %d.%d\n", MAJOR_VERSION, MINOR_VERSION);
|
//dw_printf ("Dire Wolf version %d.%d\n", MAJOR_VERSION, MINOR_VERSION);
|
||||||
|
|
||||||
#if defined(ENABLE_GPSD) // later or hamlib ...
|
#if defined(ENABLE_GPSD) // later or hamlib ...
|
||||||
|
@ -721,6 +721,7 @@ int main (int argc, char *argv[])
|
||||||
* Inputs: chan - Audio channel number, 0 or 1.
|
* Inputs: chan - Audio channel number, 0 or 1.
|
||||||
* subchan - Which modem caught it.
|
* subchan - Which modem caught it.
|
||||||
* Special case -1 for DTMF decoder.
|
* Special case -1 for DTMF decoder.
|
||||||
|
* slice - Slicer which caught it.
|
||||||
* pp - Packet handle.
|
* pp - Packet handle.
|
||||||
* alevel - Audio level, range of 0 - 100.
|
* alevel - Audio level, range of 0 - 100.
|
||||||
* (Special case, use negative to skip
|
* (Special case, use negative to skip
|
||||||
|
@ -737,8 +738,7 @@ int main (int argc, char *argv[])
|
||||||
|
|
||||||
// TODO: Use only one printf per line so output doesn't get jumbled up with stuff from other threads.
|
// TODO: Use only one printf per line so output doesn't get jumbled up with stuff from other threads.
|
||||||
|
|
||||||
|
void app_process_rec_packet (int chan, int subchan, int slice, packet_t pp, alevel_t alevel, retry_t retries, char *spectrum)
|
||||||
void app_process_rec_packet (int chan, int subchan, packet_t pp, alevel_t alevel, retry_t retries, char *spectrum)
|
|
||||||
{
|
{
|
||||||
|
|
||||||
char stemp[500];
|
char stemp[500];
|
||||||
|
@ -751,6 +751,7 @@ void app_process_rec_packet (int chan, int subchan, packet_t pp, alevel_t alevel
|
||||||
|
|
||||||
assert (chan >= 0 && chan < MAX_CHANS);
|
assert (chan >= 0 && chan < MAX_CHANS);
|
||||||
assert (subchan >= -1 && subchan < MAX_SUBCHANS);
|
assert (subchan >= -1 && subchan < MAX_SUBCHANS);
|
||||||
|
assert (slice >= 0 && slice < MAX_SLICERS);
|
||||||
assert (pp != NULL); // 1.1J+
|
assert (pp != NULL); // 1.1J+
|
||||||
|
|
||||||
strlcpy (display_retries, "", sizeof(display_retries));
|
strlcpy (display_retries, "", sizeof(display_retries));
|
||||||
|
@ -848,9 +849,16 @@ void app_process_rec_packet (int chan, int subchan, packet_t pp, alevel_t alevel
|
||||||
else {
|
else {
|
||||||
text_color_set(DW_COLOR_DEBUG);
|
text_color_set(DW_COLOR_DEBUG);
|
||||||
}
|
}
|
||||||
if (audio_config.achan[chan].num_subchan > 1) {
|
|
||||||
|
if (audio_config.achan[chan].num_subchan > 1 && audio_config.achan[chan].num_slicers == 1) {
|
||||||
dw_printf ("[%d.%d] ", chan, subchan);
|
dw_printf ("[%d.%d] ", chan, subchan);
|
||||||
}
|
}
|
||||||
|
else if (audio_config.achan[chan].num_subchan == 1 && audio_config.achan[chan].num_slicers > 1) {
|
||||||
|
dw_printf ("[%d.%d] ", chan, slice);
|
||||||
|
}
|
||||||
|
else if (audio_config.achan[chan].num_subchan > 1 && audio_config.achan[chan].num_slicers > 1) {
|
||||||
|
dw_printf ("[%d.%d.%d] ", chan, subchan, slice);
|
||||||
|
}
|
||||||
else {
|
else {
|
||||||
dw_printf ("[%d] ", chan);
|
dw_printf ("[%d] ", chan);
|
||||||
}
|
}
|
||||||
|
|
11
direwolf.h
11
direwolf.h
|
@ -48,6 +48,17 @@
|
||||||
|
|
||||||
#define MAX_SUBCHANS 9
|
#define MAX_SUBCHANS 9
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Each one of these can have multiple slicers, at
|
||||||
|
* different levels, to compensate for different
|
||||||
|
* amplitudes of the AFSK tones.
|
||||||
|
* Intially used same number as subchannels but
|
||||||
|
* we could probably trim this down a little
|
||||||
|
* without impacting performance.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define MAX_SLICERS 9
|
||||||
|
|
||||||
|
|
||||||
#if __WIN32__
|
#if __WIN32__
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
|
|
18
dlq.c
18
dlq.c
|
@ -64,14 +64,15 @@ struct dlq_item_s {
|
||||||
/* Special case, -1 means DTMF decoder. */
|
/* Special case, -1 means DTMF decoder. */
|
||||||
/* Maybe we should have a different type in this case? */
|
/* Maybe we should have a different type in this case? */
|
||||||
|
|
||||||
|
int slice; /* Winning slicer. */
|
||||||
|
|
||||||
packet_t pp; /* Pointer to frame structure. */
|
packet_t pp; /* Pointer to frame structure. */
|
||||||
|
|
||||||
alevel_t alevel; /* Audio level. */
|
alevel_t alevel; /* Audio level. */
|
||||||
|
|
||||||
retry_t retries; /* Effort expended to get a valid CRC. */
|
retry_t retries; /* Effort expended to get a valid CRC. */
|
||||||
|
|
||||||
char spectrum[MAX_SUBCHANS+1]; /* "Spectrum" display for multi-decoders. */
|
char spectrum[MAX_SUBCHANS*MAX_SLICERS+1]; /* "Spectrum" display for multi-decoders. */
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -206,6 +207,8 @@ void dlq_init (void)
|
||||||
* subchan - Which modem caught it.
|
* subchan - Which modem caught it.
|
||||||
* Special case -1 for APRStt gateway.
|
* Special case -1 for APRStt gateway.
|
||||||
*
|
*
|
||||||
|
* slice - Which slice we picked.
|
||||||
|
*
|
||||||
* pp - Address of packet object.
|
* pp - Address of packet object.
|
||||||
* Caller should NOT make any references to
|
* Caller should NOT make any references to
|
||||||
* it after this point because it could
|
* it after this point because it could
|
||||||
|
@ -231,7 +234,7 @@ void dlq_init (void)
|
||||||
*
|
*
|
||||||
*--------------------------------------------------------------------*/
|
*--------------------------------------------------------------------*/
|
||||||
|
|
||||||
void dlq_append (dlq_type_t type, int chan, int subchan, packet_t pp, alevel_t alevel, retry_t retries, char *spectrum)
|
void dlq_append (dlq_type_t type, int chan, int subchan, int slice, packet_t pp, alevel_t alevel, retry_t retries, char *spectrum)
|
||||||
{
|
{
|
||||||
|
|
||||||
struct dlq_item_s *pnew;
|
struct dlq_item_s *pnew;
|
||||||
|
@ -271,6 +274,7 @@ void dlq_append (dlq_type_t type, int chan, int subchan, packet_t pp, alevel_t a
|
||||||
pnew->nextp = NULL;
|
pnew->nextp = NULL;
|
||||||
pnew->type = type;
|
pnew->type = type;
|
||||||
pnew->chan = chan;
|
pnew->chan = chan;
|
||||||
|
pnew->slice = slice;
|
||||||
pnew->subchan = subchan;
|
pnew->subchan = subchan;
|
||||||
pnew->pp = pp;
|
pnew->pp = pp;
|
||||||
pnew->alevel = alevel;
|
pnew->alevel = alevel;
|
||||||
|
@ -514,7 +518,8 @@ void dlq_wait_while_empty (void)
|
||||||
* Outputs: type - type of queue entry.
|
* Outputs: type - type of queue entry.
|
||||||
*
|
*
|
||||||
* chan - channel of received frame.
|
* chan - channel of received frame.
|
||||||
* subchan - which modem caught it.
|
* subchan - which demodulator caught it.
|
||||||
|
* slice - which slicer caught it.
|
||||||
*
|
*
|
||||||
* pp - pointer to packet object when type is DLQ_REC_FRAME.
|
* pp - pointer to packet object when type is DLQ_REC_FRAME.
|
||||||
* Caller should destroy it with ax25_delete when finished with it.
|
* Caller should destroy it with ax25_delete when finished with it.
|
||||||
|
@ -524,7 +529,8 @@ void dlq_wait_while_empty (void)
|
||||||
*
|
*
|
||||||
*--------------------------------------------------------------------*/
|
*--------------------------------------------------------------------*/
|
||||||
|
|
||||||
int dlq_remove (dlq_type_t *type, int *chan, int *subchan, packet_t *pp, alevel_t *alevel, retry_t *retries, char *spectrum, size_t spectrumsize)
|
|
||||||
|
int dlq_remove (dlq_type_t *type, int *chan, int *subchan, int *slice, packet_t *pp, alevel_t *alevel, retry_t *retries, char *spectrum, size_t spectrumsize)
|
||||||
{
|
{
|
||||||
|
|
||||||
struct dlq_item_s *phead;
|
struct dlq_item_s *phead;
|
||||||
|
@ -557,6 +563,7 @@ int dlq_remove (dlq_type_t *type, int *chan, int *subchan, packet_t *pp, alevel_
|
||||||
*type = -1;
|
*type = -1;
|
||||||
*chan = -1;
|
*chan = -1;
|
||||||
*subchan = -1;
|
*subchan = -1;
|
||||||
|
*slice = -1;
|
||||||
*pp = NULL;
|
*pp = NULL;
|
||||||
|
|
||||||
memset (alevel, 0xff, sizeof(*alevel));
|
memset (alevel, 0xff, sizeof(*alevel));
|
||||||
|
@ -573,6 +580,7 @@ int dlq_remove (dlq_type_t *type, int *chan, int *subchan, packet_t *pp, alevel_
|
||||||
*type = phead->type;
|
*type = phead->type;
|
||||||
*chan = phead->chan;
|
*chan = phead->chan;
|
||||||
*subchan = phead->subchan;
|
*subchan = phead->subchan;
|
||||||
|
*slice = phead->slice;
|
||||||
*pp = phead->pp;
|
*pp = phead->pp;
|
||||||
*alevel = phead->alevel;
|
*alevel = phead->alevel;
|
||||||
*retries = phead->retries;
|
*retries = phead->retries;
|
||||||
|
|
5
dlq.h
5
dlq.h
|
@ -18,12 +18,11 @@ void dlq_init (void);
|
||||||
|
|
||||||
typedef enum dlq_type_e {DLQ_REC_FRAME} dlq_type_t;
|
typedef enum dlq_type_e {DLQ_REC_FRAME} dlq_type_t;
|
||||||
|
|
||||||
void dlq_append (dlq_type_t type, int chan, int subchan, packet_t pp, alevel_t alevel, retry_t retries, char *spectrum);
|
void dlq_append (dlq_type_t type, int chan, int subchan, int slice, packet_t pp, alevel_t alevel, retry_t retries, char *spectrum);
|
||||||
|
|
||||||
void dlq_wait_while_empty (void);
|
void dlq_wait_while_empty (void);
|
||||||
|
|
||||||
int dlq_remove (dlq_type_t *type, int *chan, int *subchan, packet_t *pp, alevel_t *alevel, retry_t *retries, char *spectrum, size_t spectrumsize);
|
int dlq_remove (dlq_type_t *type, int *chan, int *subchan, int *slice, packet_t *pp, alevel_t *alevel, retry_t *retries, char *spectrum, size_t spectrumsize);
|
||||||
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
Binary file not shown.
Binary file not shown.
2
dtmf.c
2
dtmf.c
|
@ -273,7 +273,7 @@ char dtmf_sample (int c, float input)
|
||||||
|
|
||||||
// Update Data Carrier Detect Indicator.
|
// Update Data Carrier Detect Indicator.
|
||||||
|
|
||||||
dcd_change (c, MAX_SUBCHANS, decoded != ' ');
|
dcd_change (c, MAX_SUBCHANS, 0, decoded != ' ');
|
||||||
|
|
||||||
/* Reset timeout timer. */
|
/* Reset timeout timer. */
|
||||||
if (decoded != ' ') {
|
if (decoded != ' ') {
|
||||||
|
|
|
@ -180,7 +180,28 @@ struct demodulator_state_s
|
||||||
* Each slicer has its own PLL and HDLC decoder.
|
* Each slicer has its own PLL and HDLC decoder.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#if 1
|
/*
|
||||||
|
* Version 1.3: Clean up subchan vs. slicer.
|
||||||
|
*
|
||||||
|
* Originally some number of CHANNELS (originally 2, later 6)
|
||||||
|
* which can have multiple parallel demodulators called SUB-CHANNELS.
|
||||||
|
* This was originally for staggered frequencies for HF SSB.
|
||||||
|
* It can also be used for multiple demodulators with the same
|
||||||
|
* frequency but other differing parameters.
|
||||||
|
* Each subchannel has its own demodulator and HDLC decoder.
|
||||||
|
*
|
||||||
|
* In version 1.2 we added multiple SLICERS.
|
||||||
|
* The data structure, here, has multiple slicers per
|
||||||
|
* demodulator (subchannel). Due to fuzzy thinking or
|
||||||
|
* expediency, the multiple slicers got mapped into subchannels.
|
||||||
|
* This means we can't use both multiple decoders and
|
||||||
|
* multiple slicers at the same time.
|
||||||
|
*
|
||||||
|
* Clean this up in 1.3 and keep the concepts separate.
|
||||||
|
* This means adding a third variable many places
|
||||||
|
* we are passing around the origin.
|
||||||
|
*
|
||||||
|
*/
|
||||||
struct {
|
struct {
|
||||||
|
|
||||||
signed int data_clock_pll; // PLL for data clock recovery.
|
signed int data_clock_pll; // PLL for data clock recovery.
|
||||||
|
@ -197,25 +218,13 @@ struct demodulator_state_s
|
||||||
|
|
||||||
int lfsr; // Descrambler shift register.
|
int lfsr; // Descrambler shift register.
|
||||||
|
|
||||||
} slicer [MAX_SUBCHANS];
|
} slicer [MAX_SLICERS]; // Actual number in use is num_slicers.
|
||||||
|
// Should be in range 1 .. MAX_SLICERS,
|
||||||
#else
|
|
||||||
signed int data_clock_pll; // PLL for data clock recovery.
|
|
||||||
// It is incremented by pll_step_per_sample
|
|
||||||
// for each audio sample.
|
|
||||||
|
|
||||||
signed int prev_d_c_pll; // Previous value of above, before
|
|
||||||
// incrementing, to detect overflows.
|
|
||||||
|
|
||||||
int prev_demod_data; // Previous data bit detected.
|
|
||||||
// Used to look for transitions.
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Special for Rino decoder only.
|
* Special for Rino decoder only.
|
||||||
* One for each possible signal polarity.
|
* One for each possible signal polarity.
|
||||||
|
* The project showed promise but fell by the wayside.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#if 0
|
#if 0
|
||||||
|
|
124
hdlc_rec.c
124
hdlc_rec.c
|
@ -29,6 +29,7 @@
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
#include "direwolf.h"
|
#include "direwolf.h"
|
||||||
#include "demod.h"
|
#include "demod.h"
|
||||||
|
@ -105,13 +106,11 @@ struct hdlc_state_s {
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static struct hdlc_state_s hdlc_state[MAX_CHANS][MAX_SUBCHANS][MAX_SLICERS];
|
||||||
static struct hdlc_state_s hdlc_state[MAX_CHANS][MAX_SUBCHANS];
|
|
||||||
|
|
||||||
static int num_subchan[MAX_CHANS]; //TODO1.2 use ptr rather than copy.
|
static int num_subchan[MAX_CHANS]; //TODO1.2 use ptr rather than copy.
|
||||||
|
|
||||||
static int composite_dcd[MAX_CHANS];
|
static int composite_dcd[MAX_CHANS][MAX_SUBCHANS+1];
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/***********************************************************************************
|
/***********************************************************************************
|
||||||
|
@ -128,7 +127,7 @@ static int was_init = 0;
|
||||||
|
|
||||||
void hdlc_rec_init (struct audio_s *pa)
|
void hdlc_rec_init (struct audio_s *pa)
|
||||||
{
|
{
|
||||||
int j, k;
|
int ch, sub, slice;
|
||||||
struct hdlc_state_s *H;
|
struct hdlc_state_s *H;
|
||||||
|
|
||||||
//text_color_set(DW_COLOR_DEBUG);
|
//text_color_set(DW_COLOR_DEBUG);
|
||||||
|
@ -136,34 +135,33 @@ void hdlc_rec_init (struct audio_s *pa)
|
||||||
|
|
||||||
assert (pa != NULL);
|
assert (pa != NULL);
|
||||||
|
|
||||||
for (j=0; j<MAX_CHANS; j++)
|
memset (composite_dcd, 0, sizeof(composite_dcd));
|
||||||
|
|
||||||
|
for (ch = 0; ch < MAX_CHANS; ch++)
|
||||||
{
|
{
|
||||||
composite_dcd[j] = 0;
|
|
||||||
|
|
||||||
if (pa->achan[j].valid) {
|
if (pa->achan[ch].valid) {
|
||||||
|
|
||||||
num_subchan[j] = pa->achan[j].num_subchan;
|
num_subchan[ch] = pa->achan[ch].num_subchan;
|
||||||
|
|
||||||
assert (num_subchan[j] >= 1 && num_subchan[j] <= MAX_SUBCHANS);
|
assert (num_subchan[ch] >= 1 && num_subchan[ch] <= MAX_SUBCHANS);
|
||||||
|
|
||||||
for (k=0; k<MAX_SUBCHANS; k++)
|
for (sub = 0; sub < num_subchan[ch]; sub++)
|
||||||
{
|
{
|
||||||
H = &hdlc_state[j][k];
|
for (slice = 0; slice < MAX_SLICERS; slice++) {
|
||||||
|
|
||||||
|
H = &hdlc_state[ch][sub][slice];
|
||||||
|
|
||||||
H->prev_raw = 0;
|
|
||||||
H->lfsr = 0;
|
|
||||||
H->prev_descram = 0;
|
|
||||||
H->pat_det = 0;
|
|
||||||
H->flag4_det = 0;
|
|
||||||
H->olen = -1;
|
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
// 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);
|
hdlc_rec2_init (pa);
|
||||||
was_init = 1;
|
was_init = 1;
|
||||||
}
|
}
|
||||||
|
@ -178,14 +176,16 @@ void hdlc_rec_init (struct audio_s *pa)
|
||||||
*
|
*
|
||||||
* Inputs: chan - Channel number.
|
* Inputs: chan - Channel number.
|
||||||
*
|
*
|
||||||
* subchan - This allows multiple decoders per channel.
|
* subchan - This allows multiple demodulators per channel.
|
||||||
|
*
|
||||||
|
* slice - Allows multiple slicers per demodulator (subchannel).
|
||||||
*
|
*
|
||||||
* raw - One bit from the demodulator.
|
* raw - One bit from the demodulator.
|
||||||
* should be 0 or 1.
|
* should be 0 or 1.
|
||||||
*
|
*
|
||||||
* is_scrambled - Is the data scrambled?
|
* is_scrambled - Is the data scrambled?
|
||||||
*
|
*
|
||||||
* descram_state - Current descrambler state.
|
* descram_state - Current descrambler state. (not used - remove)
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* Description: This is called once for each received bit.
|
* Description: This is called once for each received bit.
|
||||||
|
@ -196,9 +196,7 @@ void hdlc_rec_init (struct audio_s *pa)
|
||||||
|
|
||||||
// TODO: int not_used_remove
|
// TODO: int not_used_remove
|
||||||
|
|
||||||
|
void hdlc_rec_bit (int chan, int subchan, int slice, int raw, int is_scrambled, int not_used_remove)
|
||||||
void hdlc_rec_bit (int chan, int subchan, int raw, int is_scrambled, int not_used_remove)
|
|
||||||
|
|
||||||
{
|
{
|
||||||
|
|
||||||
int dbit; /* Data bit after undoing NRZI. */
|
int dbit; /* Data bit after undoing NRZI. */
|
||||||
|
@ -210,10 +208,12 @@ void hdlc_rec_bit (int chan, int subchan, int raw, int is_scrambled, int not_use
|
||||||
assert (chan >= 0 && chan < MAX_CHANS);
|
assert (chan >= 0 && chan < MAX_CHANS);
|
||||||
assert (subchan >= 0 && subchan < MAX_SUBCHANS);
|
assert (subchan >= 0 && subchan < MAX_SUBCHANS);
|
||||||
|
|
||||||
|
assert (slice >= 0 && slice < MAX_SLICERS);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Different state information for each channel.
|
* Different state information for each channel / subchannel / slice.
|
||||||
*/
|
*/
|
||||||
H = &hdlc_state[chan][subchan];
|
H = &hdlc_state[chan][subchan][slice];
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Using NRZI encoding,
|
* Using NRZI encoding,
|
||||||
|
@ -284,7 +284,7 @@ void hdlc_rec_bit (int chan, int subchan, int raw, int is_scrambled, int not_use
|
||||||
|
|
||||||
if ( ! H->data_detect) {
|
if ( ! H->data_detect) {
|
||||||
H->data_detect = 1;
|
H->data_detect = 1;
|
||||||
dcd_change (chan, subchan, 1);
|
dcd_change (chan, subchan, slice, 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//else if (H->flag4_det == 0x7e000000) {
|
//else if (H->flag4_det == 0x7e000000) {
|
||||||
|
@ -293,7 +293,7 @@ void hdlc_rec_bit (int chan, int subchan, int raw, int is_scrambled, int not_use
|
||||||
|
|
||||||
if ( ! H->data_detect) {
|
if ( ! H->data_detect) {
|
||||||
H->data_detect = 1;
|
H->data_detect = 1;
|
||||||
dcd_change (chan, subchan, 1);
|
dcd_change (chan, subchan, slice, 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -308,7 +308,7 @@ void hdlc_rec_bit (int chan, int subchan, int raw, int is_scrambled, int not_use
|
||||||
|
|
||||||
if ( H->data_detect ) {
|
if ( H->data_detect ) {
|
||||||
H->data_detect = 0;
|
H->data_detect = 0;
|
||||||
dcd_change (chan, subchan, 0);
|
dcd_change (chan, subchan, slice, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -372,7 +372,7 @@ void hdlc_rec_bit (int chan, int subchan, int raw, int is_scrambled, int not_use
|
||||||
if (actual_fcs == expected_fcs) {
|
if (actual_fcs == expected_fcs) {
|
||||||
alevel_t alevel = demod_get_audio_level (chan, subchan);
|
alevel_t alevel = demod_get_audio_level (chan, subchan);
|
||||||
|
|
||||||
multi_modem_process_rec_frame (chan, subchan, H->frame_buf, H->frame_len - 2, alevel, RETRY_NONE); /* len-2 to remove FCS. */
|
multi_modem_process_rec_frame (chan, subchan, slice, H->frame_buf, H->frame_len - 2, alevel, RETRY_NONE); /* len-2 to remove FCS. */
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
|
||||||
|
@ -401,7 +401,8 @@ void hdlc_rec_bit (int chan, int subchan, int raw, int is_scrambled, int not_use
|
||||||
rrbb_set_audio_level (H->rrbb, alevel);
|
rrbb_set_audio_level (H->rrbb, alevel);
|
||||||
hdlc_rec2_block (H->rrbb);
|
hdlc_rec2_block (H->rrbb);
|
||||||
/* Now owned by someone else who will free it. */
|
/* Now owned by someone else who will free it. */
|
||||||
H->rrbb = rrbb_new (chan, subchan, is_scrambled, H->lfsr, H->prev_descram); /* Allocate a new one. */
|
|
||||||
|
H->rrbb = rrbb_new (chan, subchan, slice, is_scrambled, H->lfsr, H->prev_descram); /* Allocate a new one. */
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
rrbb_clear (H->rrbb, is_scrambled, H->lfsr, H->prev_descram);
|
rrbb_clear (H->rrbb, is_scrambled, H->lfsr, H->prev_descram);
|
||||||
|
@ -514,86 +515,81 @@ void hdlc_rec_bit (int chan, int subchan, int raw, int is_scrambled, int not_use
|
||||||
*
|
*
|
||||||
* Inputs: chan
|
* Inputs: chan
|
||||||
* subchan
|
* subchan
|
||||||
|
* slice
|
||||||
*
|
*
|
||||||
* Returns: True if we are currently gathering bits.
|
* Returns: True if we are currently gathering bits.
|
||||||
* In this case we want the PLL to have more inertia.
|
* In this case we want the PLL to have more inertia.
|
||||||
*
|
*
|
||||||
* Discussion: Originally I used the data carrier detect.
|
* Discussion: This simply returns the data carrier detect state.
|
||||||
* Later, it seemed like the we should be using "olen>=0" instead.
|
* A couple other variations were tried but turned out to
|
||||||
*
|
* be slightly worse.
|
||||||
* Seems to make no difference for Track 1 and the original
|
|
||||||
* way was a hair better for Track 2.
|
|
||||||
*
|
*
|
||||||
*--------------------------------------------------------------------*/
|
*--------------------------------------------------------------------*/
|
||||||
|
|
||||||
int hdlc_rec_gathering (int chan, int subchan)
|
int hdlc_rec_gathering (int chan, int subchan, int slice)
|
||||||
{
|
{
|
||||||
assert (chan >= 0 && chan < MAX_CHANS);
|
assert (chan >= 0 && chan < MAX_CHANS);
|
||||||
assert (subchan >= 0 && subchan < MAX_SUBCHANS);
|
assert (subchan >= 0 && subchan < MAX_SUBCHANS);
|
||||||
|
assert (slice >= 0 && slice < MAX_SLICERS);
|
||||||
|
|
||||||
// Counts from Track 1 & Track 2
|
// Counts from Track 1 & Track 2
|
||||||
// data_detect 992 988
|
// data_detect 992 988
|
||||||
// olen>=0 992 985
|
// olen>=0 992 985
|
||||||
// OR-ed 992 985
|
// OR-ed 992 985
|
||||||
|
|
||||||
|
return ( hdlc_state[chan][subchan][slice].data_detect );
|
||||||
return ( hdlc_state[chan][subchan].data_detect );
|
|
||||||
|
|
||||||
//return ( hdlc_state[chan][subchan].olen >= 0);
|
|
||||||
|
|
||||||
//return ( hdlc_state[chan][subchan].data_detect || hdlc_state[chan][subchan].olen >= 0 );
|
|
||||||
|
|
||||||
} /* end hdlc_rec_gathering */
|
} /* end hdlc_rec_gathering */
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*-------------------------------------------------------------------
|
/*-------------------------------------------------------------------
|
||||||
*
|
*
|
||||||
* Name: dcd_change
|
* Name: dcd_change
|
||||||
*
|
*
|
||||||
* Purpose: Combine DCD states of all subchannels into an overall
|
* Purpose: Combine DCD states of all subchannels/ into an overall
|
||||||
* state for the channel.
|
* state for the channel.
|
||||||
*
|
*
|
||||||
* Inputs: chan
|
* Inputs: chan
|
||||||
*
|
*
|
||||||
* subchan 0 to MAX_SUBCHANS-1 for HDLC.
|
* subchan 0 to MAX_SUBCHANS-1 for HDLC.
|
||||||
* MAX_SUBCHANS for DTMF decoder.
|
* SPECIAL CASE --> MAX_SUBCHANS for DTMF decoder.
|
||||||
|
*
|
||||||
|
* slice slicer number, 0 .. MAX_SLICERS - 1.
|
||||||
*
|
*
|
||||||
* state 1 for active, 0 for not.
|
* state 1 for active, 0 for not.
|
||||||
*
|
*
|
||||||
* Returns: None. Use ??? to retrieve result.
|
* Returns: None. Use hdlc_rec_data_detect_any to retrieve result.
|
||||||
*
|
*
|
||||||
* Description: DCD for the channel is active if ANY of the subchannels
|
* Description: DCD for the channel is active if ANY of the subchannels/slices
|
||||||
* is active. Update the DCD indicator.
|
* are active. Update the DCD indicator.
|
||||||
*
|
*
|
||||||
* version 1.3: Add DTMF detection into the final result.
|
* version 1.3: Add DTMF detection into the final result.
|
||||||
* This is now called from dtmf.c too.
|
* This is now called from dtmf.c too.
|
||||||
*
|
*
|
||||||
*--------------------------------------------------------------------*/
|
*--------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
void dcd_change (int chan, int subchan, int slice, int state)
|
||||||
void dcd_change (int chan, int subchan, int state)
|
|
||||||
{
|
{
|
||||||
int old, new;
|
int old, new;
|
||||||
|
|
||||||
assert (chan >= 0 && chan < MAX_CHANS);
|
assert (chan >= 0 && chan < MAX_CHANS);
|
||||||
assert (subchan >= 0 && subchan <= MAX_SUBCHANS);
|
assert (subchan >= 0 && subchan <= MAX_SUBCHANS);
|
||||||
|
assert (slice >= 0 && slice < MAX_SLICERS);
|
||||||
assert (state == 0 || state == 1);
|
assert (state == 0 || state == 1);
|
||||||
|
|
||||||
#if DEBUG3
|
#if DEBUG3
|
||||||
text_color_set(DW_COLOR_DEBUG);
|
text_color_set(DW_COLOR_DEBUG);
|
||||||
dw_printf ("DCD %d.%d = %d \n", chan, subchan, state);
|
dw_printf ("DCD %d.%d.%d = %d \n", chan, subchan, slice, state);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
old = hdlc_rec_data_detect_any(chan);
|
old = hdlc_rec_data_detect_any(chan);
|
||||||
|
|
||||||
if (state) {
|
if (state) {
|
||||||
composite_dcd[chan] |= (1 << subchan);
|
composite_dcd[chan][subchan] |= (1 << slice);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
composite_dcd[chan] &= ~ (1 << subchan);
|
composite_dcd[chan][subchan] &= ~ (1 << slice);
|
||||||
}
|
}
|
||||||
|
|
||||||
new = hdlc_rec_data_detect_any(chan);
|
new = hdlc_rec_data_detect_any(chan);
|
||||||
|
@ -634,13 +630,17 @@ void dcd_change (int chan, int subchan, int state)
|
||||||
|
|
||||||
int hdlc_rec_data_detect_any (int chan)
|
int hdlc_rec_data_detect_any (int chan)
|
||||||
{
|
{
|
||||||
|
int sc;
|
||||||
assert (chan >= 0 && chan < MAX_CHANS);
|
assert (chan >= 0 && chan < MAX_CHANS);
|
||||||
|
|
||||||
return (composite_dcd[chan] != 0);
|
for (sc = 0; sc < num_subchan[chan]; sc++) {
|
||||||
|
if (composite_dcd[chan][sc] != 0)
|
||||||
|
return (1);
|
||||||
|
}
|
||||||
|
return (0);
|
||||||
|
|
||||||
} /* end hdlc_rec_data_detect_any */
|
} /* end hdlc_rec_data_detect_any */
|
||||||
|
|
||||||
|
|
||||||
/* end hdlc_rec.c */
|
/* end hdlc_rec.c */
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -5,9 +5,7 @@
|
||||||
|
|
||||||
void hdlc_rec_init (struct audio_s *pa);
|
void hdlc_rec_init (struct audio_s *pa);
|
||||||
|
|
||||||
|
void hdlc_rec_bit (int chan, int subchan, int slice, int raw, int is_scrambled, int descram_state);
|
||||||
void hdlc_rec_bit (int chan, int subchan, int raw, int is_scrambled, int descram_state);
|
|
||||||
|
|
||||||
|
|
||||||
/* Provided elsewhere to process a complete frame. */
|
/* Provided elsewhere to process a complete frame. */
|
||||||
|
|
||||||
|
@ -18,11 +16,10 @@ void hdlc_rec_bit (int chan, int subchan, int raw, int is_scrambled, int descram
|
||||||
/* Similar to, but not exactly the same as, data carrier detect. */
|
/* Similar to, but not exactly the same as, data carrier detect. */
|
||||||
/* We use this to influence the PLL inertia. */
|
/* We use this to influence the PLL inertia. */
|
||||||
|
|
||||||
int hdlc_rec_gathering (int chan, int subchan);
|
int hdlc_rec_gathering (int chan, int subchan, int slice);
|
||||||
|
|
||||||
|
|
||||||
/* Transmit needs to know when someone else is transmitting. */
|
/* Transmit needs to know when someone else is transmitting. */
|
||||||
|
|
||||||
void dcd_change (int chan, int subchan, int state);
|
void dcd_change (int chan, int subchan, int slice, int state);
|
||||||
|
|
||||||
int hdlc_rec_data_detect_any (int chan);
|
int hdlc_rec_data_detect_any (int chan);
|
||||||
|
|
491
hdlc_rec2.c
491
hdlc_rec2.c
|
@ -76,6 +76,12 @@
|
||||||
* It was necessary to retain more initial state information after
|
* It was necessary to retain more initial state information after
|
||||||
* the start flag octet.
|
* the start flag octet.
|
||||||
*
|
*
|
||||||
|
* Version 1.3: Took out all of the "insert" and "remove" cases because they
|
||||||
|
* offer no benenfit.
|
||||||
|
*
|
||||||
|
* Took out the delayed processing and just do it realtime.
|
||||||
|
* Changed SWAP to INVERT because it is more descriptive.
|
||||||
|
*
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
@ -152,11 +158,11 @@ struct hdlc_state_s {
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#define MAX_RETRY_SWAP_BITS 24 /* Maximum number of contiguous bits to swap */
|
|
||||||
#define MAX_RETRY_REMOVE_SEPARATED_BITS 24 /* Maximum number of contiguous bits to remove */
|
|
||||||
|
|
||||||
static int try_decode (rrbb_t block, int chan, int subchan, alevel_t alevel, retry_conf_t retry_conf, int passall);
|
static int try_decode (rrbb_t block, int chan, int subchan, int slice, alevel_t alevel, retry_conf_t retry_conf, int passall);
|
||||||
static int try_to_fix_quick_now (rrbb_t block, int chan, int subchan, alevel_t alevel);
|
|
||||||
|
static int try_to_fix_quick_now (rrbb_t block, int chan, int subchan, int slice, alevel_t alevel);
|
||||||
|
|
||||||
static int sanity_check (unsigned char *buf, int blen, retry_t bits_flipped, enum sanity_e sanity_test);
|
static int sanity_check (unsigned char *buf, int blen, retry_t bits_flipped, enum sanity_e sanity_test);
|
||||||
|
|
||||||
|
|
||||||
|
@ -173,7 +179,7 @@ static int sanity_check (unsigned char *buf, int blen, retry_t bits_flipped, enu
|
||||||
* Level of effort to recover from
|
* Level of effort to recover from
|
||||||
* a bad FCS on the frame.
|
* a bad FCS on the frame.
|
||||||
* 0 = no effort
|
* 0 = no effort
|
||||||
* 1 = try fixing a single bit
|
* 1 = try inverting a single bit
|
||||||
* 2... = more techniques...
|
* 2... = more techniques...
|
||||||
*
|
*
|
||||||
* enum sanity_e sanity_test;
|
* enum sanity_e sanity_test;
|
||||||
|
@ -222,6 +228,7 @@ void hdlc_rec2_block (rrbb_t block)
|
||||||
{
|
{
|
||||||
int chan = rrbb_get_chan(block);
|
int chan = rrbb_get_chan(block);
|
||||||
int subchan = rrbb_get_subchan(block);
|
int subchan = rrbb_get_subchan(block);
|
||||||
|
int slice = rrbb_get_slice(block);
|
||||||
alevel_t alevel = rrbb_get_audio_level(block);
|
alevel_t alevel = rrbb_get_audio_level(block);
|
||||||
retry_t fix_bits = save_audio_config_p->achan[chan].fix_bits;
|
retry_t fix_bits = save_audio_config_p->achan[chan].fix_bits;
|
||||||
int passall = save_audio_config_p->achan[chan].passall;
|
int passall = save_audio_config_p->achan[chan].passall;
|
||||||
|
@ -245,11 +252,8 @@ void hdlc_rec2_block (rrbb_t block)
|
||||||
retry_cfg.retry = RETRY_NONE;
|
retry_cfg.retry = RETRY_NONE;
|
||||||
retry_cfg.u_bits.contig.nr_bits = 0;
|
retry_cfg.u_bits.contig.nr_bits = 0;
|
||||||
retry_cfg.u_bits.contig.bit_idx = 0;
|
retry_cfg.u_bits.contig.bit_idx = 0;
|
||||||
/* Prepare the decoded bits in an array for faster processing
|
|
||||||
*(at cost of memory but 1 or 2 kbytes is nothing compared to processing time ) */
|
|
||||||
rrbb_compute_bits(block);
|
|
||||||
ok = try_decode (block, chan, subchan, alevel, retry_cfg, passall & (fix_bits == RETRY_NONE));
|
|
||||||
|
|
||||||
|
ok = try_decode (block, chan, subchan, slice, alevel, retry_cfg, passall & (fix_bits == RETRY_NONE));
|
||||||
if (ok) {
|
if (ok) {
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
text_color_set(DW_COLOR_INFO);
|
text_color_set(DW_COLOR_INFO);
|
||||||
|
@ -261,29 +265,19 @@ void hdlc_rec2_block (rrbb_t block)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Not successful with frame in orginal form.
|
* Not successful with frame in orginal form.
|
||||||
* Try the quick techniques with time proportional to the frame length.
|
* See if we can "fix" it.
|
||||||
*/
|
*/
|
||||||
if (try_to_fix_quick_now (block, chan, subchan, alevel)) {
|
if (try_to_fix_quick_now (block, chan, subchan, slice, alevel)) {
|
||||||
rrbb_delete (block);
|
rrbb_delete (block);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* Not successful with the quick fix up attempts.
|
|
||||||
* Do we want to try the more aggressive techniques where processing
|
|
||||||
* time is proportional to the square of length?
|
|
||||||
* Rather than doing it now, we throw it in a queue for processing
|
|
||||||
* by a different thread.
|
|
||||||
*/
|
|
||||||
|
|
||||||
if (fix_bits >= RETRY_SWAP_TWO_SEP) {
|
if (passall) {
|
||||||
rdq_append (block);
|
|
||||||
}
|
|
||||||
else if (passall) {
|
|
||||||
/* Exhausted all desired fix up attempts. */
|
/* Exhausted all desired fix up attempts. */
|
||||||
/* Let thru even with bad CRC. Of course, it still */
|
/* Let thru even with bad CRC. Of course, it still */
|
||||||
/* needs to be a minimum number of whole octets. */
|
/* needs to be a minimum number of whole octets. */
|
||||||
ok = try_decode (block, chan, subchan, alevel, retry_cfg, 1);
|
ok = try_decode (block, chan, subchan, slice, alevel, retry_cfg, 1);
|
||||||
rrbb_delete (block);
|
rrbb_delete (block);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
@ -307,7 +301,7 @@ void hdlc_rec2_block (rrbb_t block)
|
||||||
* Global In: configuration fix_bits - Maximum level of fix up to attempt.
|
* Global In: configuration fix_bits - Maximum level of fix up to attempt.
|
||||||
*
|
*
|
||||||
* RETRY_NONE (0) - Don't try any.
|
* RETRY_NONE (0) - Don't try any.
|
||||||
* RETRY_SWAP_SINGLE (1) - Try inverting single bits.
|
* RETRY_INVERT_SINGLE (1) - Try inverting single bits.
|
||||||
* etc.
|
* etc.
|
||||||
*
|
*
|
||||||
* configuration passall - Let it thru with bad CRC after exhausting
|
* configuration passall - Let it thru with bad CRC after exhausting
|
||||||
|
@ -318,15 +312,19 @@ void hdlc_rec2_block (rrbb_t block)
|
||||||
* processing step.
|
* processing step.
|
||||||
* 0 for failure. Caller might continue with more aggressive attempts.
|
* 0 for failure. Caller might continue with more aggressive attempts.
|
||||||
*
|
*
|
||||||
* Description: Some of the attempted fix up techniques are quick.
|
* Original: Some of the attempted fix up techniques are quick.
|
||||||
* We will attempt them immediately after receiving the frame.
|
* We will attempt them immediately after receiving the frame.
|
||||||
* Others, that take time order N**2, will be done in a later section.
|
* Others, that take time order N**2, will be done in a later section.
|
||||||
*
|
*
|
||||||
* Version 1.2: Now works properly for G3RUH type scrambling.
|
* Version 1.2: Now works properly for G3RUH type scrambling.
|
||||||
*
|
*
|
||||||
|
* Version 1.3: Removed the extra cases that didn't help.
|
||||||
|
* The separated bit case is now handled immediately instead of
|
||||||
|
* being thrown in a queue for later processing.
|
||||||
|
*
|
||||||
***********************************************************************************/
|
***********************************************************************************/
|
||||||
|
|
||||||
static int try_to_fix_quick_now (rrbb_t block, int chan, int subchan, alevel_t alevel)
|
static int try_to_fix_quick_now (rrbb_t block, int chan, int subchan, int slice, alevel_t alevel)
|
||||||
{
|
{
|
||||||
int ok;
|
int ok;
|
||||||
int len, i,j;
|
int len, i,j;
|
||||||
|
@ -340,9 +338,9 @@ static int try_to_fix_quick_now (rrbb_t block, int chan, int subchan, alevel_t a
|
||||||
/* Will modify only contiguous bits*/
|
/* Will modify only contiguous bits*/
|
||||||
retry_cfg.mode = RETRY_MODE_CONTIGUOUS;
|
retry_cfg.mode = RETRY_MODE_CONTIGUOUS;
|
||||||
/*
|
/*
|
||||||
* Try fixing one bit.
|
* Try inverting one bit.
|
||||||
*/
|
*/
|
||||||
if (fix_bits < RETRY_SWAP_SINGLE) {
|
if (fix_bits < RETRY_INVERT_SINGLE) {
|
||||||
|
|
||||||
/* Stop before single bit fix up. */
|
/* Stop before single bit fix up. */
|
||||||
|
|
||||||
|
@ -350,13 +348,13 @@ static int try_to_fix_quick_now (rrbb_t block, int chan, int subchan, alevel_t a
|
||||||
}
|
}
|
||||||
/* Try to swap one bit */
|
/* Try to swap one bit */
|
||||||
retry_cfg.type = RETRY_TYPE_SWAP;
|
retry_cfg.type = RETRY_TYPE_SWAP;
|
||||||
retry_cfg.retry = RETRY_SWAP_SINGLE;
|
retry_cfg.retry = RETRY_INVERT_SINGLE;
|
||||||
retry_cfg.u_bits.contig.nr_bits = 1;
|
retry_cfg.u_bits.contig.nr_bits = 1;
|
||||||
|
|
||||||
for (i=0; i<len; i++) {
|
for (i=0; i<len; i++) {
|
||||||
/* Set the index of the bit to swap */
|
/* Set the index of the bit to swap */
|
||||||
retry_cfg.u_bits.contig.bit_idx = i;
|
retry_cfg.u_bits.contig.bit_idx = i;
|
||||||
ok = try_decode (block, chan, subchan, alevel, retry_cfg, 0);
|
ok = try_decode (block, chan, subchan, slice, alevel, retry_cfg, 0);
|
||||||
if (ok) {
|
if (ok) {
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
text_color_set(DW_COLOR_ERROR);
|
text_color_set(DW_COLOR_ERROR);
|
||||||
|
@ -367,19 +365,19 @@ static int try_to_fix_quick_now (rrbb_t block, int chan, int subchan, alevel_t a
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Try fixing two adjacent bits.
|
* Try inverting two adjacent bits.
|
||||||
*/
|
*/
|
||||||
if (fix_bits < RETRY_SWAP_DOUBLE) {
|
if (fix_bits < RETRY_INVERT_DOUBLE) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
/* Try to swap two contiguous bits */
|
/* Try to swap two contiguous bits */
|
||||||
retry_cfg.retry = RETRY_SWAP_DOUBLE;
|
retry_cfg.retry = RETRY_INVERT_DOUBLE;
|
||||||
retry_cfg.u_bits.contig.nr_bits = 2;
|
retry_cfg.u_bits.contig.nr_bits = 2;
|
||||||
|
|
||||||
|
|
||||||
for (i=0; i<len-1; i++) {
|
for (i=0; i<len-1; i++) {
|
||||||
retry_cfg.u_bits.contig.bit_idx = i;
|
retry_cfg.u_bits.contig.bit_idx = i;
|
||||||
ok = try_decode (block, chan, subchan, alevel, retry_cfg, 0);
|
ok = try_decode (block, chan, subchan, slice, alevel, retry_cfg, 0);
|
||||||
if (ok) {
|
if (ok) {
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
text_color_set(DW_COLOR_ERROR);
|
text_color_set(DW_COLOR_ERROR);
|
||||||
|
@ -390,18 +388,18 @@ static int try_to_fix_quick_now (rrbb_t block, int chan, int subchan, alevel_t a
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Try fixing adjacent three bits.
|
* Try inverting adjacent three bits.
|
||||||
*/
|
*/
|
||||||
if (fix_bits < RETRY_SWAP_TRIPLE) {
|
if (fix_bits < RETRY_INVERT_TRIPLE) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
/* Try to swap three contiguous bits */
|
/* Try to swap three contiguous bits */
|
||||||
retry_cfg.retry = RETRY_SWAP_TRIPLE;
|
retry_cfg.retry = RETRY_INVERT_TRIPLE;
|
||||||
retry_cfg.u_bits.contig.nr_bits = 3;
|
retry_cfg.u_bits.contig.nr_bits = 3;
|
||||||
|
|
||||||
for (i=0; i<len-2; i++) {
|
for (i=0; i<len-2; i++) {
|
||||||
retry_cfg.u_bits.contig.bit_idx = i;
|
retry_cfg.u_bits.contig.bit_idx = i;
|
||||||
ok = try_decode (block, chan, subchan, alevel, retry_cfg, 0);
|
ok = try_decode (block, chan, subchan, slice, alevel, retry_cfg, 0);
|
||||||
if (ok) {
|
if (ok) {
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
text_color_set(DW_COLOR_ERROR);
|
text_color_set(DW_COLOR_ERROR);
|
||||||
|
@ -411,204 +409,20 @@ static int try_to_fix_quick_now (rrbb_t block, int chan, int subchan, alevel_t a
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fix_bits < RETRY_REMOVE_SINGLE) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Try removing one bit.
|
|
||||||
*/
|
|
||||||
retry_cfg.type = RETRY_TYPE_REMOVE;
|
|
||||||
retry_cfg.retry = RETRY_REMOVE_SINGLE;
|
|
||||||
retry_cfg.u_bits.contig.nr_bits = 1;
|
|
||||||
|
|
||||||
for (i=0; i<len; i++) {
|
|
||||||
retry_cfg.u_bits.contig.bit_idx = i;
|
|
||||||
ok = try_decode (block, chan, subchan, alevel, retry_cfg, 0);
|
|
||||||
if (ok) {
|
|
||||||
#if DEBUG
|
|
||||||
text_color_set(DW_COLOR_ERROR);
|
|
||||||
dw_printf ("*** Success by removing SINGLE bit %d of %d ***\n", i, len);
|
|
||||||
#endif
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (fix_bits < RETRY_REMOVE_DOUBLE) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Try removing two contiguous bits.
|
|
||||||
*/
|
|
||||||
#if DEBUG
|
|
||||||
text_color_set(DW_COLOR_ERROR);
|
|
||||||
dw_printf ("*** Try removing DOUBLE bits *** for %d bits\n", len);
|
|
||||||
#endif
|
|
||||||
retry_cfg.retry = RETRY_REMOVE_DOUBLE;
|
|
||||||
retry_cfg.u_bits.contig.nr_bits = 2;
|
|
||||||
|
|
||||||
for (i=0; i<len-1; i++) {
|
|
||||||
retry_cfg.u_bits.contig.bit_idx = i;
|
|
||||||
ok = try_decode (block, chan, subchan, alevel, retry_cfg, 0);
|
|
||||||
if (ok) {
|
|
||||||
#if DEBUG
|
|
||||||
text_color_set(DW_COLOR_ERROR);
|
|
||||||
dw_printf ("*** Success by removing DOUBLE bits %d of %d ***\n", i, len);
|
|
||||||
#endif
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (fix_bits < RETRY_REMOVE_TRIPLE) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Try removing three contiguous bits.
|
|
||||||
*/
|
|
||||||
#if DEBUG
|
|
||||||
text_color_set(DW_COLOR_ERROR);
|
|
||||||
dw_printf ("*** Try removing TRIPLE bits *** for %d bits\n", len);
|
|
||||||
#endif
|
|
||||||
retry_cfg.retry = RETRY_REMOVE_TRIPLE;
|
|
||||||
retry_cfg.u_bits.contig.nr_bits = 3;
|
|
||||||
|
|
||||||
for (i=0; i<len-2; i++) {
|
|
||||||
retry_cfg.u_bits.contig.bit_idx = i;
|
|
||||||
ok = try_decode (block, chan, subchan, alevel, retry_cfg, 0);
|
|
||||||
if (ok) {
|
|
||||||
#if DEBUG
|
|
||||||
text_color_set(DW_COLOR_ERROR);
|
|
||||||
dw_printf ("*** Success by removing TRIPLE bits %d of %d ***\n", i, len);
|
|
||||||
#endif
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (fix_bits < RETRY_INSERT_SINGLE) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Try inserting one bit (two values possibles for this inserted bit).
|
|
||||||
*/
|
|
||||||
#if DEBUG
|
|
||||||
text_color_set(DW_COLOR_ERROR);
|
|
||||||
dw_printf ("*** Try inserting SINGLE bit *** for %d bits\n", len);
|
|
||||||
#endif
|
|
||||||
retry_cfg.type = RETRY_TYPE_INSERT;
|
|
||||||
retry_cfg.retry = RETRY_INSERT_SINGLE;
|
|
||||||
retry_cfg.u_bits.contig.nr_bits = 1;
|
|
||||||
|
|
||||||
|
|
||||||
for (i=0; i<len; i++) {
|
|
||||||
retry_cfg.u_bits.contig.bit_idx = i;
|
|
||||||
retry_cfg.insert_value=0;
|
|
||||||
ok = try_decode (block, chan, subchan, alevel, retry_cfg, 0);
|
|
||||||
if (!ok) {
|
|
||||||
retry_cfg.insert_value=1;
|
|
||||||
ok = try_decode (block, chan, subchan, alevel, retry_cfg, 0);
|
|
||||||
}
|
|
||||||
if (ok) {
|
|
||||||
#if DEBUG
|
|
||||||
text_color_set(DW_COLOR_ERROR);
|
|
||||||
dw_printf ("*** Success by inserting SINGLE bit %d of %d ***\n", i, len);
|
|
||||||
#endif
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (fix_bits < RETRY_INSERT_DOUBLE) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Try inserting two contiguous bits (4 possible values for two bits).
|
|
||||||
*/
|
|
||||||
#if DEBUG
|
|
||||||
text_color_set(DW_COLOR_ERROR);
|
|
||||||
dw_printf ("*** Try inserting DOUBLE bits *** for %d bits\n", len);
|
|
||||||
#endif
|
|
||||||
retry_cfg.retry = RETRY_INSERT_DOUBLE;
|
|
||||||
retry_cfg.u_bits.contig.nr_bits = 2;
|
|
||||||
|
|
||||||
|
|
||||||
for (i=0; i<len-1; i++) {
|
|
||||||
retry_cfg.u_bits.contig.bit_idx = i;
|
|
||||||
for (j=0;j<4;j++) {
|
|
||||||
retry_cfg.insert_value=j;
|
|
||||||
ok = try_decode (block, chan, subchan, alevel, retry_cfg, 0);
|
|
||||||
|
|
||||||
if (ok) {
|
|
||||||
#if DEBUG
|
|
||||||
text_color_set(DW_COLOR_ERROR);
|
|
||||||
dw_printf ("*** Success by inserting DOUBLE bits %d of %d ***\n", i, len);
|
|
||||||
#endif
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/***********************************************************************************
|
|
||||||
*
|
|
||||||
* Name: hdlc_rec2_try_to_fix_later
|
|
||||||
*
|
|
||||||
* Purpose: Attempt some more time-consuming techniques.
|
|
||||||
* Rather than trying these immediately, the information is
|
|
||||||
* put into a queue and processed by another thread.
|
|
||||||
*
|
|
||||||
* Inputs: block - Stream of bits that might be a frame.
|
|
||||||
* chan - Radio channel from which it was received.
|
|
||||||
* subchan - Which demodulator when more than one per channel.
|
|
||||||
* alevel - Audio level for later reporting.
|
|
||||||
*
|
|
||||||
* Global In: configuration fix_bits - Maximum level of fix up to attempt.
|
|
||||||
*
|
|
||||||
* RETRY_NONE (0) - Don't try any.
|
|
||||||
* RETRY_SWAP_SINGLE (1) - Try inverting single bits.
|
|
||||||
* etc.
|
|
||||||
*
|
|
||||||
* configuration passall - Let it thru with bad CRC after exhausting
|
|
||||||
* all fixup attempts.
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* Returns: 1 for success. "try_decode" has passed the result along to the
|
|
||||||
* processing step.
|
|
||||||
* 0 for failure. Caller might try again if "passall" option specified.
|
|
||||||
*
|
|
||||||
***********************************************************************************/
|
|
||||||
|
|
||||||
|
|
||||||
int hdlc_rec2_try_to_fix_later (rrbb_t block, int chan, int subchan, alevel_t alevel)
|
|
||||||
{
|
|
||||||
int ok;
|
|
||||||
int len, i, j;
|
|
||||||
retry_t fix_bits = save_audio_config_p->achan[chan].fix_bits;
|
|
||||||
int passall = save_audio_config_p->achan[chan].passall;
|
|
||||||
#if DEBUG_LATER
|
|
||||||
double tstart, tend;
|
|
||||||
#endif
|
|
||||||
retry_conf_t retry_cfg;
|
|
||||||
len = rrbb_get_len(block);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if (fix_bits < RETRY_SWAP_TWO_SEP) {
|
|
||||||
goto failure;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
retry_cfg.mode = RETRY_MODE_SEPARATED;
|
|
||||||
/*
|
/*
|
||||||
* Two non-adjacent ("separated") single bits.
|
* Two non-adjacent ("separated") single bits.
|
||||||
* It chews up a lot of CPU time. Test takes 4 times longer to run.
|
* It chews up a lot of CPU time. Usual test takes 4 times longer to run.
|
||||||
*
|
*
|
||||||
* Ran up to xx seconds (TODO check again with optimized code) seconds for 1040 bits before giving up .
|
|
||||||
* Processing time is order N squared so time goes up rapidly with larger frames.
|
* Processing time is order N squared so time goes up rapidly with larger frames.
|
||||||
*/
|
*/
|
||||||
|
if (fix_bits < RETRY_INVERT_TWO_SEP) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
retry_cfg.mode = RETRY_MODE_SEPARATED;
|
||||||
retry_cfg.type = RETRY_TYPE_SWAP;
|
retry_cfg.type = RETRY_TYPE_SWAP;
|
||||||
retry_cfg.retry = RETRY_SWAP_TWO_SEP;
|
retry_cfg.retry = RETRY_INVERT_TWO_SEP;
|
||||||
retry_cfg.u_bits.sep.bit_idx_c = -1;
|
retry_cfg.u_bits.sep.bit_idx_c = -1;
|
||||||
|
|
||||||
#ifdef DEBUG_LATER
|
#ifdef DEBUG_LATER
|
||||||
|
@ -623,7 +437,7 @@ int hdlc_rec2_try_to_fix_later (rrbb_t block, int chan, int subchan, alevel_t al
|
||||||
ok = 0;
|
ok = 0;
|
||||||
for (j=i+2; j<len; j++) {
|
for (j=i+2; j<len; j++) {
|
||||||
retry_cfg.u_bits.sep.bit_idx_b = j;
|
retry_cfg.u_bits.sep.bit_idx_b = j;
|
||||||
ok = try_decode (block, chan, subchan, alevel, retry_cfg, 0);
|
ok = try_decode (block, chan, subchan, slice, alevel, retry_cfg, 0);
|
||||||
if (ok) {
|
if (ok) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -637,128 +451,28 @@ int hdlc_rec2_try_to_fix_later (rrbb_t block, int chan, int subchan, alevel_t al
|
||||||
return (1);
|
return (1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#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) {
|
return 0;
|
||||||
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);
|
// 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
|
||||||
|
double tstart, tend;
|
||||||
#endif
|
#endif
|
||||||
|
retry_conf_t retry_cfg;
|
||||||
len = rrbb_get_len(block);
|
len = rrbb_get_len(block);
|
||||||
for (i=0; i<len; i++) {
|
|
||||||
for (j=1; j<len-i && j < MAX_RETRY_SWAP_BITS;j++) {
|
|
||||||
retry_cfg.u_bits.contig.bit_idx = i;
|
|
||||||
retry_cfg.u_bits.contig.nr_bits = j;
|
|
||||||
// dw_printf ("*** Trying swapping %d bits starting at %d of %d ***\n", j,i, len);
|
|
||||||
ok = try_decode (block, chan, subchan, alevel, retry_cfg, 0);
|
|
||||||
if (ok) {
|
|
||||||
#if DEBUG
|
|
||||||
text_color_set(DW_COLOR_ERROR);
|
|
||||||
dw_printf ("*** Success by swapping %d bits starting at %d of %d ***\n", j,i, len);
|
|
||||||
#endif
|
|
||||||
return (1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#if DEBUG_LATER
|
|
||||||
tend = dtime_now();
|
|
||||||
text_color_set(DW_COLOR_ERROR);
|
|
||||||
dw_printf ("*** No luck swapping many bits for len %d in %.3f sec.\n",len, tend-tstart);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (fix_bits < RETRY_REMOVE_MANY) {
|
|
||||||
goto failure;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* Try to remove many contiguous bits */
|
|
||||||
retry_cfg.type = RETRY_TYPE_REMOVE;
|
|
||||||
retry_cfg.retry = RETRY_REMOVE_MANY;
|
|
||||||
#ifdef DEBUG_LATER
|
|
||||||
tstart = dtime_now();
|
|
||||||
dw_printf ("*** Trying removing many bits for len\n", len);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
len = rrbb_get_len(block);
|
|
||||||
for (i=0; i<2; i++) {
|
|
||||||
for (j=1; j<len-i && j<len/2;j++) {
|
|
||||||
retry_cfg.u_bits.contig.bit_idx = i;
|
|
||||||
retry_cfg.u_bits.contig.nr_bits = j;
|
|
||||||
#ifdef DEBUG
|
|
||||||
dw_printf ("*** Trying removing %d bits starting at %d of %d ***\n", j,i, len);
|
|
||||||
#endif
|
|
||||||
ok = try_decode (block, chan, subchan, alevel, retry_cfg, 0);
|
|
||||||
if (ok) {
|
|
||||||
#if DEBUG
|
|
||||||
text_color_set(DW_COLOR_ERROR);
|
|
||||||
dw_printf ("*** Success by removing %d bits starting at %d of %d ***\n", j,i, len);
|
|
||||||
#endif
|
|
||||||
return (1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#if DEBUG_LATER
|
|
||||||
tend = dtime_now();
|
|
||||||
text_color_set(DW_COLOR_ERROR);
|
|
||||||
dw_printf ("*** No luck removing many bits for len %d *** in %.3f sec.\n", len, tend-tstart);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (fix_bits < RETRY_REMOVE_TWO_SEP) {
|
|
||||||
goto failure;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Try to remove Two non-adjacent ("separated") single bits.
|
|
||||||
*/
|
|
||||||
retry_cfg.mode = RETRY_MODE_SEPARATED;
|
|
||||||
retry_cfg.type = RETRY_TYPE_REMOVE;
|
|
||||||
retry_cfg.retry = RETRY_REMOVE_TWO_SEP;
|
|
||||||
retry_cfg.u_bits.sep.bit_idx_c = -1;
|
|
||||||
|
|
||||||
#if DEBUG_LATER
|
|
||||||
tstart = dtime_now();
|
|
||||||
dw_printf ("*** Try removing TWO SEPARATED BITS %d bits\n", len);
|
|
||||||
#endif
|
|
||||||
len = rrbb_get_len(block);
|
|
||||||
for (i=0; i<len-2; i++) {
|
|
||||||
retry_cfg.u_bits.sep.bit_idx_a = i;
|
|
||||||
int j;
|
|
||||||
ok = 0;
|
|
||||||
for (j=i+2; j<len && j - i < MAX_RETRY_REMOVE_SEPARATED_BITS; j++) {
|
|
||||||
retry_cfg.u_bits.sep.bit_idx_b = j;
|
|
||||||
ok = try_decode (block, chan, subchan, alevel, retry_cfg, 0);
|
|
||||||
if (ok) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
if (ok) {
|
|
||||||
#if DEBUG_LATER
|
|
||||||
text_color_set(DW_COLOR_ERROR);
|
|
||||||
dw_printf ("*** Success by removing TWO SEPARATED bits %d and %d of %d \n", i, j, len);
|
|
||||||
#endif
|
|
||||||
return (1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#if DEBUG_LATER
|
|
||||||
tend = dtime_now();
|
|
||||||
text_color_set(DW_COLOR_ERROR);
|
|
||||||
dw_printf ("*** No luck removing TWO SEPARATED bits of %d *** %.3f sec.\n", len, tend-tstart);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
failure:
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* All fix up attempts have failed.
|
* All fix up attempts have failed.
|
||||||
|
@ -772,8 +486,7 @@ failure:
|
||||||
retry_cfg.retry = RETRY_NONE;
|
retry_cfg.retry = RETRY_NONE;
|
||||||
retry_cfg.u_bits.contig.nr_bits = 0;
|
retry_cfg.u_bits.contig.nr_bits = 0;
|
||||||
retry_cfg.u_bits.contig.bit_idx = 0;
|
retry_cfg.u_bits.contig.bit_idx = 0;
|
||||||
|
ok = try_decode (block, chan, subchan, slice, alevel, retry_cfg, passall);
|
||||||
ok = try_decode (block, chan, subchan, alevel, retry_cfg, passall);
|
|
||||||
return (ok);
|
return (ok);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -782,6 +495,7 @@ failure:
|
||||||
} /* end hdlc_rec2_try_to_fix_later */
|
} /* end hdlc_rec2_try_to_fix_later */
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Check if the specified index of bit has been modified with the current type of configuration
|
* Check if the specified index of bit has been modified with the current type of configuration
|
||||||
* Provide a specific implementation for contiguous mode to optimize number of tests done in the loop
|
* Provide a specific implementation for contiguous mode to optimize number of tests done in the loop
|
||||||
|
@ -811,14 +525,7 @@ inline static char is_sep_bit_modified(int bit_idx, retry_conf_t retry_conf) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* Get the bit value from a precalculated array to optimize access time in the loop
|
|
||||||
*/
|
|
||||||
|
|
||||||
inline static unsigned int get_bit (const rrbb_t b,const unsigned int ind)
|
|
||||||
{
|
|
||||||
return b->computed_data[ind];
|
|
||||||
}
|
|
||||||
|
|
||||||
/***********************************************************************************
|
/***********************************************************************************
|
||||||
*
|
*
|
||||||
|
@ -835,20 +542,12 @@ inline static unsigned int get_bit (const rrbb_t b,const unsigned int ind)
|
||||||
* retry_conf - Controls changes that will be attempted to get a good CRC.
|
* retry_conf - Controls changes that will be attempted to get a good CRC.
|
||||||
*
|
*
|
||||||
* retry:
|
* retry:
|
||||||
* Level of effort to recover from A bad FCS on the frame.
|
* Level of effort to recover from a bad FCS on the frame.
|
||||||
* RETRY_NONE=0,
|
* RETRY_NONE = 0
|
||||||
* RETRY_SWAP_SINGLE=1,
|
* RETRY_INVERT_SINGLE = 1
|
||||||
* RETRY_SWAP_DOUBLE=2,
|
* RETRY_INVERT_DOUBLE = 2
|
||||||
* RETRY_SWAP_TRIPLE=3,
|
* RETRY_INVERT_TRIPLE = 3
|
||||||
* RETRY_REMOVE_SINGLE=4,
|
* RETRY_INVERT_TWO_SEP = 4
|
||||||
* RETRY_REMOVE_DOUBLE=5,
|
|
||||||
* RETRY_REMOVE_TRIPLE=6,
|
|
||||||
* RETRY_INSERT_SINGLE=7,
|
|
||||||
* RETRY_INSERT_DOUBLE=8,
|
|
||||||
* RETRY_SWAP_TWO_SEP=9,
|
|
||||||
* RETRY_SWAP_MANY=10,
|
|
||||||
* RETRY_REMOVE_MANY=11,
|
|
||||||
* RETRY_REMOVE_TWO_SEP=12,
|
|
||||||
*
|
*
|
||||||
* mode: RETRY_MODE_CONTIGUOUS - change adjacent bits.
|
* mode: RETRY_MODE_CONTIGUOUS - change adjacent bits.
|
||||||
* contig.bit_idx - first bit position
|
* contig.bit_idx - first bit position
|
||||||
|
@ -861,8 +560,6 @@ inline static unsigned int get_bit (const rrbb_t b,const unsigned int ind)
|
||||||
*
|
*
|
||||||
* type: RETRY_TYPE_NONE - Make no changes.
|
* type: RETRY_TYPE_NONE - Make no changes.
|
||||||
* RETRY_TYPE_SWAP - Try inverting.
|
* RETRY_TYPE_SWAP - Try inverting.
|
||||||
* RETRY_TYPE_REMOVE - Try removing.
|
|
||||||
* RETRY_TYPE_INSERT - Try inserting.
|
|
||||||
*
|
*
|
||||||
* passall - All it thru even with bad CRC.
|
* passall - All it thru even with bad CRC.
|
||||||
* Valid only when no changes make. i.e.
|
* Valid only when no changes make. i.e.
|
||||||
|
@ -873,8 +570,7 @@ inline static unsigned int get_bit (const rrbb_t b,const unsigned int ind)
|
||||||
*
|
*
|
||||||
***********************************************************************************/
|
***********************************************************************************/
|
||||||
|
|
||||||
|
static int try_decode (rrbb_t block, int chan, int subchan, int slice, alevel_t alevel, retry_conf_t retry_conf, int passall)
|
||||||
static int try_decode (rrbb_t block, int chan, int subchan, alevel_t alevel, retry_conf_t retry_conf, int passall)
|
|
||||||
{
|
{
|
||||||
struct hdlc_state_s H;
|
struct hdlc_state_s H;
|
||||||
int blen; /* Block length in bits. */
|
int blen; /* Block length in bits. */
|
||||||
|
@ -891,7 +587,7 @@ static int try_decode (rrbb_t block, int chan, int subchan, alevel_t alevel, ret
|
||||||
H.is_scrambled = rrbb_get_is_scrambled (block);
|
H.is_scrambled = rrbb_get_is_scrambled (block);
|
||||||
H.prev_descram = rrbb_get_prev_descram (block);
|
H.prev_descram = rrbb_get_prev_descram (block);
|
||||||
H.lfsr = rrbb_get_descram_state (block);
|
H.lfsr = rrbb_get_descram_state (block);
|
||||||
H.prev_raw = get_bit (block, 0); /* Actually last bit of the */
|
H.prev_raw = rrbb_get_bit (block, 0); /* Actually last bit of the */
|
||||||
/* opening flag so we can derive the */
|
/* opening flag so we can derive the */
|
||||||
/* first data bit. */
|
/* first data bit. */
|
||||||
|
|
||||||
|
@ -911,9 +607,6 @@ static int try_decode (rrbb_t block, int chan, int subchan, alevel_t alevel, ret
|
||||||
H.frame_len = 0;
|
H.frame_len = 0;
|
||||||
|
|
||||||
blen = rrbb_get_len(block);
|
blen = rrbb_get_len(block);
|
||||||
/* Prepare space for the inserted bits in contiguous mode (separated mode for insert is not supported yet) */
|
|
||||||
if (retry_conf.type == RETRY_TYPE_INSERT && retry_conf.mode == RETRY_MODE_CONTIGUOUS)
|
|
||||||
blen+=retry_conf.u_bits.contig.nr_bits;
|
|
||||||
|
|
||||||
#if DEBUGx
|
#if DEBUGx
|
||||||
text_color_set(DW_COLOR_DEBUG);
|
text_color_set(DW_COLOR_DEBUG);
|
||||||
|
@ -922,43 +615,16 @@ static int try_decode (rrbb_t block, int chan, int subchan, alevel_t alevel, ret
|
||||||
#endif
|
#endif
|
||||||
for (i=1; i<blen; i++) {
|
for (i=1; i<blen; i++) {
|
||||||
/* Get the value for the current bit */
|
/* Get the value for the current bit */
|
||||||
raw = get_bit (block, i);
|
raw = rrbb_get_bit (block, i);
|
||||||
/* If swap two sep mode , swap the bit if needed */
|
/* If swap two sep mode , swap the bit if needed */
|
||||||
if (retry_conf_retry == RETRY_SWAP_TWO_SEP) {
|
if (retry_conf_retry == RETRY_INVERT_TWO_SEP) {
|
||||||
if (is_sep_bit_modified(i, retry_conf))
|
if (is_sep_bit_modified(i, retry_conf))
|
||||||
raw = ! raw;
|
raw = ! raw;
|
||||||
/* Else if remove two sep bits mode , remove the bit if needed */
|
|
||||||
} else if (retry_conf_retry == RETRY_REMOVE_TWO_SEP) {
|
|
||||||
if (is_sep_bit_modified(i, retry_conf))
|
|
||||||
//Remove (ignore) this bit from the buffer!
|
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
/* Else handle all the others contiguous modes */
|
/* Else handle all the others contiguous modes */
|
||||||
else if (retry_conf_mode == RETRY_MODE_CONTIGUOUS) {
|
else if (retry_conf_mode == RETRY_MODE_CONTIGUOUS) {
|
||||||
/* If contiguous remove, ignore this bit from the buffer */
|
|
||||||
if (retry_conf_type == RETRY_TYPE_REMOVE) {
|
if (retry_conf_type == RETRY_TYPE_SWAP) {
|
||||||
if ( is_contig_bit_modified(i, retry_conf))
|
|
||||||
//Remove (ignore) this bit from the buffer!
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
/* If insert bits mode */
|
|
||||||
else if (retry_conf_type == RETRY_TYPE_INSERT) {
|
|
||||||
int nr_bits = retry_conf.u_bits.contig.nr_bits;
|
|
||||||
int bit_idx = retry_conf.u_bits.contig.bit_idx;
|
|
||||||
/* If bit is after the index to insert, use the existing bit value (but shifted from the array) */
|
|
||||||
if (i >= bit_idx + nr_bits)
|
|
||||||
raw = get_bit (block, i-nr_bits);
|
|
||||||
/* Else if this is a bit to insert, calculate the value of the bit from insert_value */
|
|
||||||
else if (is_contig_bit_modified(i, retry_conf)) {
|
|
||||||
raw = (retry_conf.insert_value >> (i-bit_idx)) & 1;
|
|
||||||
/* dw_printf ("raw is %d for i %d bit_idx %d insert_value %d\n",
|
|
||||||
raw, i, bit_idx, retry_conf.insert_value);*/
|
|
||||||
/* Else use the original bit value from the buffer */
|
|
||||||
} else {
|
|
||||||
/* Already set before */
|
|
||||||
}
|
|
||||||
/* If in swap mode */
|
|
||||||
} else if (retry_conf_type == RETRY_TYPE_SWAP) {
|
|
||||||
/* If this is the bit to swap */
|
/* If this is the bit to swap */
|
||||||
if (is_contig_bit_modified(i, retry_conf))
|
if (is_contig_bit_modified(i, retry_conf))
|
||||||
raw = ! raw;
|
raw = ! raw;
|
||||||
|
@ -1097,8 +763,7 @@ static int try_decode (rrbb_t block, int chan, int subchan, alevel_t alevel, ret
|
||||||
|
|
||||||
assert (rrbb_get_chan(block) == chan);
|
assert (rrbb_get_chan(block) == chan);
|
||||||
assert (rrbb_get_subchan(block) == subchan);
|
assert (rrbb_get_subchan(block) == subchan);
|
||||||
|
multi_modem_process_rec_frame (chan, subchan, slice, H.frame_buf, H.frame_len - 2, alevel, retry_conf.retry); /* len-2 to remove FCS. */
|
||||||
multi_modem_process_rec_frame (chan, subchan, H.frame_buf, H.frame_len - 2, alevel, retry_conf.retry); /* len-2 to remove FCS. */
|
|
||||||
return 1; /* success */
|
return 1; /* success */
|
||||||
|
|
||||||
} else if (passall) {
|
} else if (passall) {
|
||||||
|
@ -1107,7 +772,7 @@ static int try_decode (rrbb_t block, int chan, int subchan, alevel_t alevel, ret
|
||||||
//text_color_set(DW_COLOR_ERROR);
|
//text_color_set(DW_COLOR_ERROR);
|
||||||
//dw_printf ("ATTEMPTING PASSALL PROCESSING\n");
|
//dw_printf ("ATTEMPTING PASSALL PROCESSING\n");
|
||||||
|
|
||||||
multi_modem_process_rec_frame (chan, subchan, H.frame_buf, H.frame_len - 2, alevel, RETRY_MAX); /* len-2 to remove FCS. */
|
multi_modem_process_rec_frame (chan, subchan, slice, H.frame_buf, H.frame_len - 2, alevel, RETRY_MAX); /* len-2 to remove FCS. */
|
||||||
return 1; /* success */
|
return 1; /* success */
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
@ -1186,7 +851,9 @@ end:
|
||||||
*
|
*
|
||||||
* Returns: 1 if it passes the sanity test.
|
* Returns: 1 if it passes the sanity test.
|
||||||
*
|
*
|
||||||
* Description:
|
* Description: This is NOT a validity check.
|
||||||
|
* We don't know if modifying the frame fixed the problem or made it worse.
|
||||||
|
* We can only test if it looks reasonable.
|
||||||
*
|
*
|
||||||
***********************************************************************************/
|
***********************************************************************************/
|
||||||
|
|
||||||
|
|
17
hdlc_rec2.h
17
hdlc_rec2.h
|
@ -17,9 +17,7 @@ typedef enum retry_mode_e {
|
||||||
|
|
||||||
typedef enum retry_type_e {
|
typedef enum retry_type_e {
|
||||||
RETRY_TYPE_NONE=0,
|
RETRY_TYPE_NONE=0,
|
||||||
RETRY_TYPE_SWAP=1,
|
RETRY_TYPE_SWAP=1 } retry_type_t;
|
||||||
RETRY_TYPE_REMOVE=2,
|
|
||||||
RETRY_TYPE_INSERT=3} retry_type_t;
|
|
||||||
|
|
||||||
typedef struct retry_conf_s {
|
typedef struct retry_conf_s {
|
||||||
retry_t retry;
|
retry_t retry;
|
||||||
|
@ -52,15 +50,7 @@ static const char * retry_text[] = {
|
||||||
"SINGLE",
|
"SINGLE",
|
||||||
"DOUBLE",
|
"DOUBLE",
|
||||||
"TRIPLE",
|
"TRIPLE",
|
||||||
"REMOVE_SINGLE",
|
|
||||||
"REMOVE_DOUBLE",
|
|
||||||
"REMOVE_TRIPLE",
|
|
||||||
"INSERT_SINGLE",
|
|
||||||
"INSERT_DOUBLE",
|
|
||||||
"TWO_SEP",
|
"TWO_SEP",
|
||||||
"MANY",
|
|
||||||
"REMOVE_MANY",
|
|
||||||
"REMOVE_SEP",
|
|
||||||
"PASSALL" };
|
"PASSALL" };
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -68,11 +58,10 @@ void hdlc_rec2_init (struct audio_s *audio_config_p);
|
||||||
|
|
||||||
void hdlc_rec2_block (rrbb_t block);
|
void hdlc_rec2_block (rrbb_t block);
|
||||||
|
|
||||||
int hdlc_rec2_try_to_fix_later (rrbb_t block, int chan, int subchan, alevel_t alevel);
|
int hdlc_rec2_try_to_fix_later (rrbb_t block, int chan, int subchan, int slice, alevel_t alevel);
|
||||||
|
|
||||||
/* Provided by the top level application to process a complete frame. */
|
/* Provided by the top level application to process a complete frame. */
|
||||||
|
|
||||||
void app_process_rec_packet (int chan, int subchan, packet_t pp, alevel_t level, retry_t retries, char *spectrum);
|
void app_process_rec_packet (int chan, int subchan, int slice, packet_t pp, alevel_t level, retry_t retries, char *spectrum);
|
||||||
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
297
multi_modem.c
297
multi_modem.c
|
@ -98,23 +98,15 @@ static struct audio_s *save_audio_config_p;
|
||||||
// Candidates for further processing.
|
// Candidates for further processing.
|
||||||
|
|
||||||
static struct {
|
static struct {
|
||||||
|
|
||||||
packet_t packet_p;
|
packet_t packet_p;
|
||||||
alevel_t alevel;
|
alevel_t alevel;
|
||||||
retry_t retries;
|
retry_t retries;
|
||||||
int age;
|
int age;
|
||||||
unsigned int crc;
|
unsigned int crc;
|
||||||
int score;
|
int score;
|
||||||
|
} candidate[MAX_CHANS][MAX_SUBCHANS][MAX_SLICERS];
|
||||||
|
|
||||||
} candidate[MAX_CHANS][MAX_SUBCHANS];
|
|
||||||
#define MAX_STORED_CRC 256
|
|
||||||
|
|
||||||
typedef struct crc_s {
|
|
||||||
struct crc_s* nextp; /* Next pointer to maintain a queue. */
|
|
||||||
unsigned int crc;
|
|
||||||
} *crc_t;
|
|
||||||
|
|
||||||
static crc_t crc_queue_of_last_to_app[MAX_CHANS];
|
|
||||||
|
|
||||||
#define PROCESS_AFTER_BITS 2
|
#define PROCESS_AFTER_BITS 2
|
||||||
|
|
||||||
|
@ -163,12 +155,14 @@ void multi_modem_init (struct audio_s *pa)
|
||||||
save_audio_config_p->achan[chan].baud = DEFAULT_BAUD;
|
save_audio_config_p->achan[chan].baud = DEFAULT_BAUD;
|
||||||
}
|
}
|
||||||
process_age[chan] = PROCESS_AFTER_BITS * save_audio_config_p->adev[ACHAN2ADEV(chan)].samples_per_sec / save_audio_config_p->achan[chan].baud;
|
process_age[chan] = PROCESS_AFTER_BITS * save_audio_config_p->adev[ACHAN2ADEV(chan)].samples_per_sec / save_audio_config_p->achan[chan].baud;
|
||||||
crc_queue_of_last_to_app[chan] = NULL;
|
//crc_queue_of_last_to_app[chan] = NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
|
||||||
//Add a crc to the end of the queue and returns the numbers of CRC stored in the queue
|
//Add a crc to the end of the queue and returns the numbers of CRC stored in the queue
|
||||||
int crc_queue_append (unsigned int crc, unsigned int chan) {
|
int crc_queue_append (unsigned int crc, unsigned int chan) {
|
||||||
crc_t plast;
|
crc_t plast;
|
||||||
|
@ -257,6 +251,7 @@ unsigned char is_crc_in_queue(unsigned int chan, unsigned int crc) {
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
#endif /* if 0 */
|
||||||
|
|
||||||
/*------------------------------------------------------------------------------
|
/*------------------------------------------------------------------------------
|
||||||
*
|
*
|
||||||
|
@ -282,6 +277,11 @@ unsigned char is_crc_in_queue(unsigned int chan, unsigned int crc) {
|
||||||
* We now have a separate variable, num_demod, which could be 1
|
* We now have a separate variable, num_demod, which could be 1
|
||||||
* while num_subchan is larger.
|
* while num_subchan is larger.
|
||||||
*
|
*
|
||||||
|
* Version 1.3: Go back to num_subchan with single meaning of number of demodulators.
|
||||||
|
* We now have separate independent variable, num_slicers, for the
|
||||||
|
* mark/space imbalance compensation.
|
||||||
|
* num_demod, while probably more descriptive, should not exist anymore.
|
||||||
|
*
|
||||||
*------------------------------------------------------------------------------*/
|
*------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
|
||||||
|
@ -290,25 +290,48 @@ void multi_modem_process_sample (int chan, int audio_sample)
|
||||||
{
|
{
|
||||||
int d;
|
int d;
|
||||||
int subchan;
|
int subchan;
|
||||||
|
static int i = 0; /* for interleaving among multiple demodulators. */
|
||||||
|
|
||||||
|
// TODO: temp debug, remove this.
|
||||||
|
|
||||||
|
assert (save_audio_config_p->achan[chan].num_subchan > 0 && save_audio_config_p->achan[chan].num_subchan <= MAX_SUBCHANS);
|
||||||
|
assert (save_audio_config_p->achan[chan].num_slicers > 0 && save_audio_config_p->achan[chan].num_slicers <= MAX_SLICERS);
|
||||||
|
|
||||||
|
|
||||||
/* Formerly one loop. */
|
/* Formerly one loop. */
|
||||||
/* 1.2: We can feed one demodulator but end up with multiple outputs. */
|
/* 1.2: We can feed one demodulator but end up with multiple outputs. */
|
||||||
|
|
||||||
|
|
||||||
for (d = 0; d < save_audio_config_p->achan[chan].num_demod; d++) {
|
if (save_audio_config_p->achan[chan].interleave > 1) {
|
||||||
|
|
||||||
|
// 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);
|
demod_process_sample(chan, d, audio_sample);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for (subchan = 0; subchan < save_audio_config_p->achan[chan].num_subchan; subchan++) {
|
for (subchan = 0; subchan < save_audio_config_p->achan[chan].num_subchan; subchan++) {
|
||||||
|
int slice;
|
||||||
|
|
||||||
if (candidate[chan][subchan].packet_p != NULL) {
|
for (slice = 0; slice < save_audio_config_p->achan[chan].num_slicers; slice++) {
|
||||||
candidate[chan][subchan].age++;
|
|
||||||
if (candidate[chan][subchan].age > process_age[chan]) {
|
if (candidate[chan][subchan][slice].packet_p != NULL) {
|
||||||
|
candidate[chan][subchan][slice].age++;
|
||||||
|
if (candidate[chan][subchan][slice].age > process_age[chan]) {
|
||||||
pick_best_candidate (chan);
|
pick_best_candidate (chan);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -320,7 +343,8 @@ void multi_modem_process_sample (int chan, int audio_sample)
|
||||||
* FCS and acceptable size.
|
* FCS and acceptable size.
|
||||||
*
|
*
|
||||||
* Inputs: chan - Audio channel number, 0 or 1.
|
* Inputs: chan - Audio channel number, 0 or 1.
|
||||||
* subchan - Which modem/decoder found it.
|
* subchan - Which modem found it.
|
||||||
|
* slice - Which slice found it.
|
||||||
* fbuf - Pointer to first byte in HDLC frame.
|
* fbuf - Pointer to first byte in HDLC frame.
|
||||||
* flen - Number of bytes excluding the FCS.
|
* flen - Number of bytes excluding the FCS.
|
||||||
* alevel - Audio level, range of 0 - 100.
|
* alevel - Audio level, range of 0 - 100.
|
||||||
|
@ -414,73 +438,31 @@ void multi_modem_process_sample (int chan, int audio_sample)
|
||||||
than one.
|
than one.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
void multi_modem_process_rec_frame (int chan, int subchan, unsigned char *fbuf, int flen, alevel_t alevel, retry_t retries)
|
void multi_modem_process_rec_frame (int chan, int subchan, int slice, unsigned char *fbuf, int flen, alevel_t alevel, retry_t retries)
|
||||||
{
|
{
|
||||||
packet_t pp;
|
packet_t pp;
|
||||||
|
|
||||||
|
|
||||||
assert (chan >= 0 && chan < MAX_CHANS);
|
assert (chan >= 0 && chan < MAX_CHANS);
|
||||||
assert (subchan >= 0 && subchan < MAX_SUBCHANS);
|
assert (subchan >= 0 && subchan < MAX_SUBCHANS);
|
||||||
|
assert (slice >= 0 && slice < MAX_SUBCHANS);
|
||||||
|
|
||||||
pp = ax25_from_frame (fbuf, flen, alevel);
|
pp = ax25_from_frame (fbuf, flen, alevel);
|
||||||
|
|
||||||
if (pp == NULL) {
|
if (pp == NULL) {
|
||||||
|
text_color_set(DW_COLOR_ERROR);
|
||||||
|
dw_printf ("Unexpected internal problem, %s %d\n", __FILE__, __LINE__);
|
||||||
return; /* oops! why would it fail? */
|
return; /* oops! why would it fail? */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If single modem/deocder, push it thru and forget about all this foolishness.
|
* If only one demodulator/slicer, push it thru and forget about all this foolishness.
|
||||||
*/
|
*/
|
||||||
if (save_audio_config_p->achan[chan].num_subchan == 1) {
|
if (save_audio_config_p->achan[chan].num_subchan == 1 &&
|
||||||
dlq_append (DLQ_REC_FRAME, chan, subchan, pp, alevel, retries, "");
|
save_audio_config_p->achan[chan].num_slicers == 1) {
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
dlq_append (DLQ_REC_FRAME, chan, subchan, slice, pp, alevel, retries, "");
|
||||||
* Special handing for two separated bit errors.
|
|
||||||
* See description earlier.
|
|
||||||
*
|
|
||||||
* Not combined with others to find the best score.
|
|
||||||
* Either pass it along or drop if duplicate.
|
|
||||||
*/
|
|
||||||
|
|
||||||
if (retries >= RETRY_SWAP_TWO_SEP) {
|
|
||||||
int mycrc;
|
|
||||||
char spectrum[MAX_SUBCHANS+1];
|
|
||||||
int dropped = 0;
|
|
||||||
|
|
||||||
memset (spectrum, 0, sizeof(spectrum));
|
|
||||||
memset (spectrum, '_', (size_t)save_audio_config_p->achan[chan].num_subchan);
|
|
||||||
spectrum[subchan] = '.';
|
|
||||||
|
|
||||||
mycrc = ax25_m_m_crc(pp);
|
|
||||||
/* Smetimes recovered packet is not the latest one send to the app:
|
|
||||||
* It can be a packet sent to the app before the latest one because of the processing time ...
|
|
||||||
* So we check if the crc of current packet has already been received in the queue of others crc
|
|
||||||
*/
|
|
||||||
dropped = is_crc_in_queue(chan, mycrc);
|
|
||||||
|
|
||||||
#if DEBUG
|
|
||||||
text_color_set(DW_COLOR_DEBUG);
|
|
||||||
dw_printf ("\n%s\n%d.%d: ptr=%p, retry=%d, age=, crc=%04x, score= , dropped =%d\n",
|
|
||||||
spectrum, chan, subchan, pp, (int)retries, mycrc,dropped);
|
|
||||||
#endif
|
|
||||||
if (dropped) {
|
|
||||||
/* Same as last one. Drop it. */
|
|
||||||
ax25_delete (pp);
|
|
||||||
#if DEBUG
|
|
||||||
dw_printf ("Drop duplicate.\n");
|
|
||||||
#endif
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
#if DEBUG
|
|
||||||
dw_printf ("Send the best one along.\n");
|
|
||||||
#endif
|
|
||||||
dlq_append (DLQ_REC_FRAME, chan, subchan, pp, alevel, retries, spectrum);
|
|
||||||
if (crc_queue_append(mycrc, chan) > MAX_STORED_CRC)
|
|
||||||
crc_queue_remove(chan);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -488,17 +470,17 @@ void multi_modem_process_rec_frame (int chan, int subchan, unsigned char *fbuf,
|
||||||
/*
|
/*
|
||||||
* Otherwise, save them up for a few bit times so we can pick the best.
|
* Otherwise, save them up for a few bit times so we can pick the best.
|
||||||
*/
|
*/
|
||||||
if (candidate[chan][subchan].packet_p != NULL) {
|
if (candidate[chan][subchan][slice].packet_p != NULL) {
|
||||||
/* Oops! Didn't expect it to be there. */
|
/* Oops! Didn't expect it to be there. */
|
||||||
ax25_delete (candidate[chan][subchan].packet_p);
|
ax25_delete (candidate[chan][subchan][slice].packet_p);
|
||||||
candidate[chan][subchan].packet_p = NULL;
|
candidate[chan][subchan][slice].packet_p = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
candidate[chan][subchan].packet_p = pp;
|
candidate[chan][subchan][slice].packet_p = pp;
|
||||||
candidate[chan][subchan].alevel = alevel;
|
candidate[chan][subchan][slice].alevel = alevel;
|
||||||
candidate[chan][subchan].retries = retries;
|
candidate[chan][subchan][slice].retries = retries;
|
||||||
candidate[chan][subchan].age = 0;
|
candidate[chan][subchan][slice].age = 0;
|
||||||
candidate[chan][subchan].crc = ax25_m_m_crc(pp);
|
candidate[chan][subchan][slice].crc = ax25_m_m_crc(pp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -519,56 +501,82 @@ void multi_modem_process_rec_frame (int chan, int subchan, unsigned char *fbuf,
|
||||||
*
|
*
|
||||||
*--------------------------------------------------------------------*/
|
*--------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
/* This is a suitable order for interleaved "G" demodulators. */
|
||||||
|
/* Opposite order would be suitable for multi-frequency although */
|
||||||
|
/* multiple slicers are of questionable value for HF SSB. */
|
||||||
|
|
||||||
|
#define subchan_from_n(x) ((x) % save_audio_config_p->achan[chan].num_subchan)
|
||||||
|
#define slice_from_n(x) ((x) / save_audio_config_p->achan[chan].num_subchan)
|
||||||
|
|
||||||
|
|
||||||
static void pick_best_candidate (int chan)
|
static void pick_best_candidate (int chan)
|
||||||
{
|
{
|
||||||
int subchan;
|
int best_n, best_score;
|
||||||
int best_subchan, best_score;
|
char spectrum[MAX_SUBCHANS*MAX_SLICERS+1];
|
||||||
char spectrum[MAX_SUBCHANS+1];
|
int n, j, k;
|
||||||
int k;
|
int num_bars = save_audio_config_p->achan[chan].num_slicers * save_audio_config_p->achan[chan].num_subchan;
|
||||||
|
|
||||||
memset (spectrum, 0, sizeof(spectrum));
|
memset (spectrum, 0, sizeof(spectrum));
|
||||||
|
|
||||||
for (subchan = 0; subchan < save_audio_config_p->achan[chan].num_subchan; subchan++) {
|
for (n = 0; n < num_bars; n++) {
|
||||||
|
j = subchan_from_n(n);
|
||||||
|
k = slice_from_n(n);
|
||||||
|
|
||||||
/* Build the spectrum display. */
|
/* Build the spectrum display. */
|
||||||
|
|
||||||
if (candidate[chan][subchan].packet_p == NULL) {
|
if (candidate[chan][j][k].packet_p == NULL) {
|
||||||
spectrum[subchan] = '_';
|
spectrum[n] = '_';
|
||||||
}
|
}
|
||||||
else if (candidate[chan][subchan].retries == RETRY_NONE) {
|
else if (candidate[chan][j][k].retries == RETRY_NONE) {
|
||||||
spectrum[subchan] = '|';
|
spectrum[n] = '|';
|
||||||
}
|
}
|
||||||
else if (candidate[chan][subchan].retries == RETRY_SWAP_SINGLE) {
|
else if (candidate[chan][j][k].retries == RETRY_INVERT_SINGLE) {
|
||||||
spectrum[subchan] = ':';
|
spectrum[n] = ':';
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
spectrum[subchan] = '.';
|
spectrum[n] = '.';
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Begining score depends on effort to get a valid frame CRC. */
|
/* Begining score depends on effort to get a valid frame CRC. */
|
||||||
|
|
||||||
candidate[chan][subchan].score = RETRY_MAX * 1000 - ((int)candidate[chan][subchan].retries * 1000);
|
candidate[chan][j][k].score = RETRY_MAX * 1000 - ((int)candidate[chan][j][k].retries * 1000);
|
||||||
|
}
|
||||||
|
|
||||||
/* Bump it up slightly if others nearby have the same CRC. */
|
/* Bump it up slightly if others nearby have the same CRC. */
|
||||||
|
|
||||||
for (k = 0; k < save_audio_config_p->achan[chan].num_subchan; k++) {
|
for (n = 0; n < num_bars; n++) {
|
||||||
if (k != subchan && candidate[chan][k].packet_p != NULL) {
|
int m;
|
||||||
if (candidate[chan][k].crc == candidate[chan][subchan].crc) {
|
|
||||||
candidate[chan][subchan].score += (MAX_SUBCHANS+1) - abs(subchan-k);
|
j = subchan_from_n(n);
|
||||||
|
k = slice_from_n(n);
|
||||||
|
|
||||||
|
if (candidate[chan][j][k].packet_p != NULL) {
|
||||||
|
|
||||||
|
for (m = 0; m < num_bars; m++) {
|
||||||
|
|
||||||
|
int mj = subchan_from_n(m);
|
||||||
|
int mk = slice_from_n(m);
|
||||||
|
|
||||||
|
if (m != n && candidate[chan][mj][mk].packet_p != NULL) {
|
||||||
|
if (candidate[chan][j][k].crc == candidate[chan][mj][mk].crc) {
|
||||||
|
candidate[chan][j][k].score += (num_bars+1) - abs(m-n);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
best_subchan = 0;
|
best_n = 0;
|
||||||
best_score = 0;
|
best_score = 0;
|
||||||
|
|
||||||
for (subchan = 0; subchan < save_audio_config_p->achan[chan].num_subchan; subchan++) {
|
for (n = 0; n < num_bars; n++) {
|
||||||
if (candidate[chan][subchan].packet_p != NULL) {
|
j = subchan_from_n(n);
|
||||||
if (candidate[chan][subchan].score > best_score) {
|
k = slice_from_n(n);
|
||||||
best_score = candidate[chan][subchan].score;
|
|
||||||
best_subchan = subchan;
|
if (candidate[chan][j][k].packet_p != NULL) {
|
||||||
|
if (candidate[chan][j][k].score > best_score) {
|
||||||
|
best_score = candidate[chan][j][k].score;
|
||||||
|
best_n = n;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -577,20 +585,22 @@ static void pick_best_candidate (int chan)
|
||||||
text_color_set(DW_COLOR_DEBUG);
|
text_color_set(DW_COLOR_DEBUG);
|
||||||
dw_printf ("\n%s\n", spectrum);
|
dw_printf ("\n%s\n", spectrum);
|
||||||
|
|
||||||
for (subchan = 0; subchan < save_audio_config_p->achan[chan].num_subchan; subchan++) {
|
for (n = 0; n < num_bars; n++) {
|
||||||
|
j = subchan_from_n(n);
|
||||||
|
k = slice_from_n(n);
|
||||||
|
|
||||||
if (candidate[chan][subchan].packet_p == NULL) {
|
if (candidate[chan][j][k].packet_p == NULL) {
|
||||||
dw_printf ("%d.%d: ptr=%p\n", chan, subchan,
|
dw_printf ("%d.%d.%d: ptr=%p\n", chan, j, k,
|
||||||
candidate[chan][subchan].packet_p);
|
candidate[chan][j][k].packet_p);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
dw_printf ("%d.%d: ptr=%p, retry=%d, age=%3d, crc=%04x, score=%d %s\n", chan, subchan,
|
dw_printf ("%d.%d.%d: ptr=%p, retry=%d, age=%3d, crc=%04x, score=%d %s\n", chan, j, k,
|
||||||
candidate[chan][subchan].packet_p,
|
candidate[chan][j][k].packet_p,
|
||||||
(int)(candidate[chan][subchan].retries),
|
(int)(candidate[chan][j][k].retries),
|
||||||
candidate[chan][subchan].age,
|
candidate[chan][j][k].age,
|
||||||
candidate[chan][subchan].crc,
|
candidate[chan][j][k].crc,
|
||||||
candidate[chan][subchan].score,
|
candidate[chan][j][k].score,
|
||||||
subchan == best_subchan ? "***" : "");
|
(n == best_n) ? "***" : "");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
@ -599,75 +609,38 @@ static void pick_best_candidate (int chan)
|
||||||
* send the best one along.
|
* send the best one along.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#if 1 // v1.2 dev F, Reverse original order. Delete rejects THEN process the best one.
|
|
||||||
|
|
||||||
|
|
||||||
/* Delete those not chosen. */
|
/* Delete those not chosen. */
|
||||||
|
|
||||||
for (subchan = 0; subchan < save_audio_config_p->achan[chan].num_subchan; subchan++) {
|
for (n = 0; n < num_bars; n++) {
|
||||||
if (subchan != best_subchan && candidate[chan][subchan].packet_p != NULL) {
|
j = subchan_from_n(n);
|
||||||
ax25_delete (candidate[chan][subchan].packet_p);
|
k = slice_from_n(n);
|
||||||
candidate[chan][subchan].packet_p = NULL;
|
if (n != best_n && candidate[chan][j][k].packet_p != NULL) {
|
||||||
|
ax25_delete (candidate[chan][j][k].packet_p);
|
||||||
|
candidate[chan][j][k].packet_p = NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Pass along one. */
|
/* Pass along one. */
|
||||||
|
|
||||||
dlq_append (DLQ_REC_FRAME, chan, best_subchan,
|
|
||||||
candidate[chan][best_subchan].packet_p,
|
j = subchan_from_n(best_n);
|
||||||
candidate[chan][best_subchan].alevel,
|
k = slice_from_n(best_n);
|
||||||
(int)(candidate[chan][best_subchan].retries),
|
|
||||||
|
dlq_append (DLQ_REC_FRAME, chan, j, k,
|
||||||
|
candidate[chan][j][k].packet_p,
|
||||||
|
candidate[chan][j][k].alevel,
|
||||||
|
(int)(candidate[chan][j][k].retries),
|
||||||
spectrum);
|
spectrum);
|
||||||
if (crc_queue_append(candidate[chan][best_subchan].crc, chan) > MAX_STORED_CRC)
|
|
||||||
crc_queue_remove(chan);
|
|
||||||
|
|
||||||
/* Someone else owns it now and will delete it later. */
|
/* Someone else owns it now and will delete it later. */
|
||||||
candidate[chan][best_subchan].packet_p = NULL;
|
candidate[chan][j][k].packet_p = NULL;
|
||||||
|
|
||||||
/* Clear in preparation for next time. */
|
/* Clear in preparation for next time. */
|
||||||
|
|
||||||
for (subchan = 0; subchan < save_audio_config_p->achan[chan].num_subchan; subchan++) {
|
memset (candidate, 0, sizeof(candidate));
|
||||||
|
|
||||||
candidate[chan][subchan].alevel.rec = 0;
|
} /* end pick_best_candidate */
|
||||||
candidate[chan][subchan].alevel.mark = 0;
|
|
||||||
candidate[chan][subchan].alevel.space = 0;
|
|
||||||
|
|
||||||
candidate[chan][subchan].retries = 0;
|
|
||||||
candidate[chan][subchan].age = 0;
|
|
||||||
candidate[chan][subchan].crc = 0;
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
|
|
||||||
dlq_append (DLQ_REC_FRAME, chan, best_subchan,
|
|
||||||
candidate[chan][best_subchan].packet_p,
|
|
||||||
candidate[chan][best_subchan].alevel,
|
|
||||||
(int)(candidate[chan][best_subchan].retries),
|
|
||||||
spectrum);
|
|
||||||
if (crc_queue_append(candidate[chan][best_subchan].crc, chan) > MAX_STORED_CRC)
|
|
||||||
crc_queue_remove(chan);
|
|
||||||
/* Someone else will delete so don't do it below. */
|
|
||||||
candidate[chan][best_subchan].packet_p = NULL;
|
|
||||||
|
|
||||||
/* Clear out in preparation for next time. */
|
|
||||||
|
|
||||||
for (subchan = 0; subchan < save_audio_config_p->achan[chan].num_subchan; subchan++) {
|
|
||||||
if (candidate[chan][subchan].packet_p != NULL) {
|
|
||||||
ax25_delete (candidate[chan][subchan].packet_p);
|
|
||||||
candidate[chan][subchan].packet_p = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
candidate[chan][subchan].alevel.rec = 0;
|
|
||||||
candidate[chan][subchan].alevel.mark = 0;
|
|
||||||
candidate[chan][subchan].alevel.space = 0;
|
|
||||||
|
|
||||||
candidate[chan][subchan].retries = 0;
|
|
||||||
candidate[chan][subchan].age = 0;
|
|
||||||
candidate[chan][subchan].crc = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* end multi_modem.c */
|
/* end multi_modem.c */
|
||||||
|
|
|
@ -14,7 +14,6 @@ void multi_modem_init (struct audio_s *pmodem);
|
||||||
|
|
||||||
void multi_modem_process_sample (int c, int audio_sample);
|
void multi_modem_process_sample (int c, int audio_sample);
|
||||||
|
|
||||||
void multi_modem_process_rec_frame (int chan, int subchan, unsigned char *fbuf, int flen, alevel_t alevel, retry_t retries);
|
void multi_modem_process_rec_frame (int chan, int subchan, int slice, unsigned char *fbuf, int flen, alevel_t alevel, retry_t retries);
|
||||||
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
9
recv.c
9
recv.c
|
@ -287,11 +287,11 @@ void recv_process (void)
|
||||||
dlq_type_t type;
|
dlq_type_t type;
|
||||||
int chan;
|
int chan;
|
||||||
int subchan;
|
int subchan;
|
||||||
|
int slice;
|
||||||
packet_t pp;
|
packet_t pp;
|
||||||
alevel_t alevel;
|
alevel_t alevel;
|
||||||
retry_t retries;
|
retry_t retries;
|
||||||
char spectrum[MAX_SUBCHANS+1];
|
char spectrum[MAX_SUBCHANS*MAX_SLICERS+1];
|
||||||
|
|
||||||
|
|
||||||
while (1) {
|
while (1) {
|
||||||
|
|
||||||
|
@ -301,16 +301,15 @@ void recv_process (void)
|
||||||
dw_printf ("recv_process: woke up\n");
|
dw_printf ("recv_process: woke up\n");
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
ok = dlq_remove (&type, &chan, &subchan, &slice, &pp, &alevel, &retries, spectrum, sizeof(spectrum));
|
||||||
|
|
||||||
ok = dlq_remove (&type, &chan, &subchan, &pp, &alevel, &retries, spectrum, sizeof(spectrum));
|
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
text_color_set(DW_COLOR_DEBUG);
|
text_color_set(DW_COLOR_DEBUG);
|
||||||
dw_printf ("recv_process: dlq_remove() returned ok=%d, type=%d, chan=%d, pp=%p\n",
|
dw_printf ("recv_process: dlq_remove() returned ok=%d, type=%d, chan=%d, pp=%p\n",
|
||||||
ok, (int)type, chan, pp);
|
ok, (int)type, chan, pp);
|
||||||
#endif
|
#endif
|
||||||
if (ok) {
|
if (ok) {
|
||||||
|
app_process_rec_packet (chan, subchan, slice, pp, alevel, retries, spectrum);
|
||||||
app_process_rec_packet (chan, subchan, pp, alevel, retries, spectrum);
|
|
||||||
}
|
}
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
else {
|
else {
|
||||||
|
|
|
@ -98,6 +98,8 @@ static void * redecode_thread (void *arg);
|
||||||
void redecode_init (struct audio_s *p_audio_config)
|
void redecode_init (struct audio_s *p_audio_config)
|
||||||
{
|
{
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
|
||||||
#if __WIN32__
|
#if __WIN32__
|
||||||
HANDLE redecode_th;
|
HANDLE redecode_th;
|
||||||
#else
|
#else
|
||||||
|
@ -150,7 +152,7 @@ void redecode_init (struct audio_s *p_audio_config)
|
||||||
text_color_set(DW_COLOR_DEBUG);
|
text_color_set(DW_COLOR_DEBUG);
|
||||||
dw_printf ("redecode_init: finished \n");
|
dw_printf ("redecode_init: finished \n");
|
||||||
#endif
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
} /* end redecode_init */
|
} /* end redecode_init */
|
||||||
|
|
||||||
|
@ -174,6 +176,8 @@ void redecode_init (struct audio_s *p_audio_config)
|
||||||
*
|
*
|
||||||
*--------------------------------------------------------------------*/
|
*--------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
|
||||||
#if __WIN32__
|
#if __WIN32__
|
||||||
static unsigned redecode_thread (void *arg)
|
static unsigned redecode_thread (void *arg)
|
||||||
#else
|
#else
|
||||||
|
@ -241,7 +245,7 @@ static void * redecode_thread (void *arg)
|
||||||
|
|
||||||
} /* end redecode_thread */
|
} /* end redecode_thread */
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
145
rrbb.c
145
rrbb.c
|
@ -23,17 +23,14 @@
|
||||||
* File: rrbb.c
|
* File: rrbb.c
|
||||||
*
|
*
|
||||||
* Purpose: Raw Received Bit Buffer.
|
* Purpose: Raw Received Bit Buffer.
|
||||||
* Implementation of an array of bits used to hold data out of
|
* An array of bits used to hold data out of
|
||||||
* the demodulator before feeding it into the HLDC decoding.
|
* the demodulator before feeding it into the HLDC decoding.
|
||||||
*
|
*
|
||||||
* Version 1.0: Let's try something new.
|
|
||||||
* Rather than storing a single bit from the demodulator
|
|
||||||
* output, let's store a value which we can try later
|
|
||||||
* comparing to threshold values besides 0.
|
|
||||||
*
|
|
||||||
* Version 1.2: Save initial state of 9600 baud descrambler so we can
|
* Version 1.2: Save initial state of 9600 baud descrambler so we can
|
||||||
* attempt bit fix up on G3RUH/K9NG scrambled data.
|
* attempt bit fix up on G3RUH/K9NG scrambled data.
|
||||||
*
|
*
|
||||||
|
* Version 1.3: Store as bytes rather than packing 8 bits per byte.
|
||||||
|
*
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
|
|
||||||
#define RRBB_C
|
#define RRBB_C
|
||||||
|
@ -49,44 +46,9 @@
|
||||||
#include "rrbb.h"
|
#include "rrbb.h"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#define MAGIC1 0x12344321
|
#define MAGIC1 0x12344321
|
||||||
#define MAGIC2 0x56788765
|
#define MAGIC2 0x56788765
|
||||||
|
|
||||||
static const unsigned int masks[SOI] = {
|
|
||||||
0x00000001,
|
|
||||||
0x00000002,
|
|
||||||
0x00000004,
|
|
||||||
0x00000008,
|
|
||||||
0x00000010,
|
|
||||||
0x00000020,
|
|
||||||
0x00000040,
|
|
||||||
0x00000080,
|
|
||||||
0x00000100,
|
|
||||||
0x00000200,
|
|
||||||
0x00000400,
|
|
||||||
0x00000800,
|
|
||||||
0x00001000,
|
|
||||||
0x00002000,
|
|
||||||
0x00004000,
|
|
||||||
0x00008000,
|
|
||||||
0x00010000,
|
|
||||||
0x00020000,
|
|
||||||
0x00040000,
|
|
||||||
0x00080000,
|
|
||||||
0x00100000,
|
|
||||||
0x00200000,
|
|
||||||
0x00400000,
|
|
||||||
0x00800000,
|
|
||||||
0x01000000,
|
|
||||||
0x02000000,
|
|
||||||
0x04000000,
|
|
||||||
0x08000000,
|
|
||||||
0x10000000,
|
|
||||||
0x20000000,
|
|
||||||
0x40000000,
|
|
||||||
0x80000000 };
|
|
||||||
|
|
||||||
static int new_count = 0;
|
static int new_count = 0;
|
||||||
static int delete_count = 0;
|
static int delete_count = 0;
|
||||||
|
@ -102,6 +64,8 @@ static int delete_count = 0;
|
||||||
*
|
*
|
||||||
* subchan - Which demodulator of the channel.
|
* subchan - Which demodulator of the channel.
|
||||||
*
|
*
|
||||||
|
* slice - multiple thresholds per demodulator.
|
||||||
|
*
|
||||||
* is_scrambled - Is data scrambled? (true, false)
|
* is_scrambled - Is data scrambled? (true, false)
|
||||||
*
|
*
|
||||||
* descram_state - State of data descrambler.
|
* descram_state - State of data descrambler.
|
||||||
|
@ -114,21 +78,20 @@ static int delete_count = 0;
|
||||||
*
|
*
|
||||||
***********************************************************************************/
|
***********************************************************************************/
|
||||||
|
|
||||||
rrbb_t rrbb_new (int chan, int subchan, int is_scrambled, int descram_state, int prev_descram)
|
rrbb_t rrbb_new (int chan, int subchan, int slice, int is_scrambled, int descram_state, int prev_descram)
|
||||||
{
|
{
|
||||||
rrbb_t result;
|
rrbb_t result;
|
||||||
|
|
||||||
assert (SOI == 8 * sizeof(unsigned int));
|
|
||||||
|
|
||||||
assert (chan >= 0 && chan < MAX_CHANS);
|
assert (chan >= 0 && chan < MAX_CHANS);
|
||||||
assert (subchan >= 0 && subchan < MAX_SUBCHANS);
|
assert (subchan >= 0 && subchan < MAX_SUBCHANS);
|
||||||
|
assert (slice >= 0 && slice < MAX_SLICERS);
|
||||||
|
|
||||||
result = malloc(sizeof(struct rrbb_s));
|
result = malloc(sizeof(struct rrbb_s));
|
||||||
|
|
||||||
result->magic1 = MAGIC1;
|
result->magic1 = MAGIC1;
|
||||||
result->chan = chan;
|
result->chan = chan;
|
||||||
result->subchan = subchan;
|
result->subchan = subchan;
|
||||||
|
result->slice = slice;
|
||||||
result->magic2 = MAGIC2;
|
result->magic2 = MAGIC2;
|
||||||
|
|
||||||
new_count++;
|
new_count++;
|
||||||
|
@ -181,6 +144,7 @@ void rrbb_clear (rrbb_t b, int is_scrambled, int descram_state, int prev_descram
|
||||||
b->prev_descram = prev_descram;
|
b->prev_descram = prev_descram;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/***********************************************************************************
|
/***********************************************************************************
|
||||||
*
|
*
|
||||||
* Name: rrbb_append_bit
|
* Name: rrbb_append_bit
|
||||||
|
@ -192,35 +156,7 @@ void rrbb_clear (rrbb_t b, int is_scrambled, int descram_state, int prev_descram
|
||||||
*
|
*
|
||||||
***********************************************************************************/
|
***********************************************************************************/
|
||||||
|
|
||||||
|
/* Definition in header file so it can be inlined. */
|
||||||
// TODO: Forget about bit packing and just use bytes.
|
|
||||||
// We have hundreds of MB. Why waste time to save a couple KB?
|
|
||||||
|
|
||||||
|
|
||||||
void rrbb_append_bit (rrbb_t b, int val)
|
|
||||||
{
|
|
||||||
unsigned int di, mi;
|
|
||||||
|
|
||||||
assert (b != NULL);
|
|
||||||
assert (b->magic1 == MAGIC1);
|
|
||||||
assert (b->magic2 == MAGIC2);
|
|
||||||
|
|
||||||
if (b->len >= MAX_NUM_BITS) {
|
|
||||||
return; /* Silently discard if full. */
|
|
||||||
}
|
|
||||||
|
|
||||||
di = b->len / SOI;
|
|
||||||
mi = b->len % SOI;
|
|
||||||
|
|
||||||
if (val) {
|
|
||||||
b->data[di] |= masks[mi];
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
b->data[di] &= ~ masks[mi];
|
|
||||||
}
|
|
||||||
|
|
||||||
b->len++;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/***********************************************************************************
|
/***********************************************************************************
|
||||||
|
@ -279,46 +215,9 @@ int rrbb_get_len (rrbb_t b)
|
||||||
*
|
*
|
||||||
***********************************************************************************/
|
***********************************************************************************/
|
||||||
|
|
||||||
int rrbb_get_bit (rrbb_t b, unsigned int ind)
|
/* Definition in header file so it can be inlined. */
|
||||||
{
|
|
||||||
assert (b != NULL);
|
|
||||||
assert (b->magic1 == MAGIC1);
|
|
||||||
assert (b->magic2 == MAGIC2);
|
|
||||||
|
|
||||||
assert (ind < b->len);
|
|
||||||
|
|
||||||
if (b->data[ind / SOI] & masks[ind % SOI]) {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned int rrbb_get_computed_bit (rrbb_t b, unsigned int ind)
|
|
||||||
{
|
|
||||||
return b->computed_data[ind];
|
|
||||||
}
|
|
||||||
|
|
||||||
int rrbb_compute_bits (rrbb_t b)
|
|
||||||
{
|
|
||||||
unsigned int i,val;
|
|
||||||
|
|
||||||
assert (b != NULL);
|
|
||||||
assert (b->magic1 == MAGIC1);
|
|
||||||
assert (b->magic2 == MAGIC2);
|
|
||||||
|
|
||||||
for (i=0;i<b->len;i++) {
|
|
||||||
if (b->data[i / SOI] & masks[i % SOI]) {
|
|
||||||
val = 1;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
val = 0;
|
|
||||||
}
|
|
||||||
b->computed_data[i] = val;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/***********************************************************************************
|
/***********************************************************************************
|
||||||
|
@ -457,6 +356,28 @@ int rrbb_get_subchan (rrbb_t b)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/***********************************************************************************
|
||||||
|
*
|
||||||
|
* Name: rrbb_get_slice
|
||||||
|
*
|
||||||
|
* Purpose: Get slice number from which bit buffer was received.
|
||||||
|
*
|
||||||
|
* Inputs: b Handle for bit array.
|
||||||
|
*
|
||||||
|
***********************************************************************************/
|
||||||
|
|
||||||
|
int rrbb_get_slice (rrbb_t b)
|
||||||
|
{
|
||||||
|
assert (b != NULL);
|
||||||
|
assert (b->magic1 == MAGIC1);
|
||||||
|
assert (b->magic2 == MAGIC2);
|
||||||
|
|
||||||
|
assert (b->slice >= 0 && b->slice < MAX_SLICERS);
|
||||||
|
|
||||||
|
return (b->slice);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/***********************************************************************************
|
/***********************************************************************************
|
||||||
*
|
*
|
||||||
* Name: rrbb_set_audio_level
|
* Name: rrbb_set_audio_level
|
||||||
|
|
42
rrbb.h
42
rrbb.h
|
@ -4,10 +4,11 @@
|
||||||
#define RRBB_H
|
#define RRBB_H
|
||||||
|
|
||||||
|
|
||||||
typedef short slice_t;
|
#define FASTER13 1 // Don't pack 8 samples per byte.
|
||||||
|
|
||||||
|
|
||||||
#ifdef RRBB_C
|
//typedef short slice_t;
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Maximum size (in bytes) of an AX.25 frame including the 2 octet FCS.
|
* Maximum size (in bytes) of an AX.25 frame including the 2 octet FCS.
|
||||||
|
@ -23,13 +24,14 @@ typedef short slice_t;
|
||||||
|
|
||||||
#define MAX_NUM_BITS (MAX_FRAME_LEN * 8 * 6 / 5)
|
#define MAX_NUM_BITS (MAX_FRAME_LEN * 8 * 6 / 5)
|
||||||
|
|
||||||
#define SOI 32
|
|
||||||
|
|
||||||
typedef struct rrbb_s {
|
typedef struct rrbb_s {
|
||||||
int magic1;
|
int magic1;
|
||||||
struct rrbb_s* nextp; /* Next pointer to maintain a queue. */
|
struct rrbb_s* nextp; /* Next pointer to maintain a queue. */
|
||||||
|
|
||||||
int chan; /* Radio channel from which it was received. */
|
int chan; /* Radio channel from which it was received. */
|
||||||
int subchan; /* Which modem when more than one per channel. */
|
int subchan; /* Which modem when more than one per channel. */
|
||||||
|
int slice; /* Which slicer. */
|
||||||
|
|
||||||
alevel_t alevel; /* Received audio level at time of frame capture. */
|
alevel_t alevel; /* Received audio level at time of frame capture. */
|
||||||
unsigned int len; /* Current number of samples in array. */
|
unsigned int len; /* Current number of samples in array. */
|
||||||
|
|
||||||
|
@ -37,38 +39,37 @@ typedef struct rrbb_s {
|
||||||
int descram_state; /* Descrambler state before first data bit of frame. */
|
int descram_state; /* Descrambler state before first data bit of frame. */
|
||||||
int prev_descram; /* Previous descrambled bit. */
|
int prev_descram; /* Previous descrambled bit. */
|
||||||
|
|
||||||
unsigned int data[(MAX_NUM_BITS+SOI-1)/SOI];
|
unsigned char fdata[MAX_NUM_BITS];
|
||||||
unsigned int computed_data[MAX_NUM_BITS];
|
|
||||||
|
|
||||||
int magic2;
|
int magic2;
|
||||||
} *rrbb_t;
|
} *rrbb_t;
|
||||||
|
|
||||||
#else
|
|
||||||
|
|
||||||
/* Hide the implementation. */
|
|
||||||
|
|
||||||
typedef void *rrbb_t;
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
|
rrbb_t rrbb_new (int chan, int subchan, int slice, int is_scrambled, int descram_state, int prev_descram);
|
||||||
rrbb_t rrbb_new (int chan, int subchan, int is_scrambled, int descram_state, int prev_descram);
|
|
||||||
|
|
||||||
void rrbb_clear (rrbb_t b, int is_scrambled, int descram_state, int prev_descram);
|
void rrbb_clear (rrbb_t b, int is_scrambled, int descram_state, int prev_descram);
|
||||||
|
|
||||||
|
|
||||||
void rrbb_append_bit (rrbb_t b, int val);
|
static __attribute__((always_inline)) void rrbb_append_bit (rrbb_t b, const unsigned char val)
|
||||||
|
{
|
||||||
|
if (b->len >= MAX_NUM_BITS) {
|
||||||
|
return; /* Silently discard if full. */
|
||||||
|
}
|
||||||
|
b->fdata[b->len] = val;
|
||||||
|
b->len++;
|
||||||
|
}
|
||||||
|
|
||||||
|
static __attribute__((always_inline)) unsigned char rrbb_get_bit (const rrbb_t b, const int ind)
|
||||||
|
{
|
||||||
|
return (b->fdata[ind]);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void rrbb_chop8 (rrbb_t b);
|
void rrbb_chop8 (rrbb_t b);
|
||||||
|
|
||||||
int rrbb_get_len (rrbb_t b);
|
int rrbb_get_len (rrbb_t b);
|
||||||
|
|
||||||
int rrbb_get_bit (rrbb_t b, unsigned int ind);
|
|
||||||
unsigned int rrbb_get_computed_bit (rrbb_t b, unsigned int ind);
|
|
||||||
int rrbb_compute_bits (rrbb_t b);
|
|
||||||
|
|
||||||
//void rrbb_flip_bit (rrbb_t b, unsigned int ind);
|
//void rrbb_flip_bit (rrbb_t b, unsigned int ind);
|
||||||
|
|
||||||
void rrbb_delete (rrbb_t b);
|
void rrbb_delete (rrbb_t b);
|
||||||
|
@ -78,6 +79,7 @@ rrbb_t rrbb_get_nextp (rrbb_t b);
|
||||||
|
|
||||||
int rrbb_get_chan (rrbb_t b);
|
int rrbb_get_chan (rrbb_t b);
|
||||||
int rrbb_get_subchan (rrbb_t b);
|
int rrbb_get_subchan (rrbb_t b);
|
||||||
|
int rrbb_get_slice (rrbb_t b);
|
||||||
|
|
||||||
void rrbb_set_audio_level (rrbb_t b, alevel_t alevel);
|
void rrbb_set_audio_level (rrbb_t b, alevel_t alevel);
|
||||||
alevel_t rrbb_get_audio_level (rrbb_t b);
|
alevel_t rrbb_get_audio_level (rrbb_t b);
|
||||||
|
|
Loading…
Reference in New Issue