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