Issue 271 - DCD dropping at wrong time.

This commit is contained in:
wb2osz 2020-05-21 21:37:34 -04:00
parent 74a5c34a94
commit 0661e23f21
12 changed files with 219 additions and 162 deletions

View File

@ -82,7 +82,7 @@
#include "ptt.h"
#include "dtime_now.h"
#include "fx25.h"
#include "hdlc_rec.h"
#if 0 /* Typical but not flexible enough. */
@ -188,6 +188,9 @@ static int J_opt = 0; /* 2400 bps PSK compatible MFJ-2400 and maybe others. */
static int h_opt = 0; // Hexadecimal display of received packet.
static char P_opt[16] = ""; // Demodulator profiles.
static int d_x_opt = 1; // FX.25 debug.
static int d_o_opt = 0; // "-d o" option for DCD output control. */
static int dcd_count = 0;
static int dcd_missing_errors = 0;
int main (int argc, char *argv[])
@ -382,6 +385,7 @@ int main (int argc, char *argv[])
for (char *p=optarg; *p!='\0'; p++) {
switch (*p) {
case 'x': d_x_opt++; break; // FX.25
case 'o': d_o_opt++; break; // DCD output control
default: break;
}
}
@ -686,6 +690,10 @@ int main (int argc, char *argv[])
elapsed = dtime_now() - start_time;
dw_printf ("%d packets decoded in %.3f seconds. %.1f x realtime\n", packets_decoded_total, elapsed, total_filetime/elapsed);
if (d_o_opt) {
dw_printf ("DCD count = %d\n", dcd_count);
dw_printf ("DCD missing erors = %d\n", dcd_missing_errors);
}
if (error_if_less_than != -1 && packets_decoded_total < error_if_less_than) {
text_color_set(DW_COLOR_ERROR);
@ -744,6 +752,7 @@ void dlq_rec_frame (int chan, int subchan, int slice, packet_t pp, alevel_t alev
char alevel_text[AX25_ALEVEL_TO_TEXT_SIZE];
packets_decoded_one++;
if ( ! hdlc_rec_data_detect_any(chan)) dcd_missing_errors++;
ax25_format_addrs (pp, stemp);
@ -777,7 +786,7 @@ void dlq_rec_frame (int chan, int subchan, int slice, packet_t pp, alevel_t alev
int min = (int)(sec / 60.);
sec -= min * 60;
dw_printf ("%d:%07.4f ", min, sec);
dw_printf ("%d:%06.3f ", min, sec);
if (h != AX25_SOURCE) {
dw_printf ("Digipeater ");
@ -876,6 +885,38 @@ void dlq_rec_frame (int chan, int subchan, int slice, packet_t pp, alevel_t alev
void ptt_set (int ot, int chan, int ptt_signal)
{
// Should only get here for DCD output control.
static double dcd_start_time[MAX_CHANS];
if (d_o_opt) {
double t = (double)sample_number / my_audio_config.adev[0].samples_per_sec;
double sec1, sec2;
int min1, min2;
text_color_set(DW_COLOR_INFO);
if (ptt_signal) {
//sec1 = t;
//min1 = (int)(sec1 / 60.);
//sec1 -= min1 * 60;
//dw_printf ("DCD[%d] = ON %d:%06.3f\n", chan, min1, sec1);
dcd_count++;
dcd_start_time[chan] = t;
}
else {
//dw_printf ("DCD[%d] = off %d:%06.3f %3.0f\n", chan, min, sec, (t - dcd_start_time[chan]) * 1000.);
sec1 = dcd_start_time[chan];
min1 = (int)(sec1 / 60.);
sec1 -= min1 * 60;
sec2 = t;
min2 = (int)(sec2 / 60.);
sec2 -= min2 * 60;
dw_printf ("DCD[%d] %d:%06.3f - %d:%06.3f = %3.0f\n", chan, min1, sec1, min2, sec2, (t - dcd_start_time[chan]) * 1000.);
}
}
return;
}

View File

@ -44,14 +44,24 @@
#include <assert.h>
#include <ctype.h>
// Fine tuning for different demodulator types.
#define DCD_THRESH_ON 32 // Hysteresis: Can miss 0 out of 32 for detecting lock.
// This is best for actual on-the-air signals.
// Still too many brief false matches.
#define DCD_THRESH_OFF 8 // Might want a little more fine tuning.
#define DCD_GOOD_WIDTH 1024 // No more than 1024!!!
#include "fsk_demod_state.h" // Values above override defaults.
#include "tune.h"
#include "fsk_demod_state.h"
#include "hdlc_rec.h"
#include "demod_9600.h"
#include "textcolor.h"
#include "dsp.h"
static float slice_point[MAX_SUBCHANS];
@ -407,7 +417,7 @@ void demod_9600_process_sample (int chan, int sam, struct demodulator_state_s *D
if (chan == 0) {
if (1) {
//if (hdlc_rec_gathering (chan, subchan, slice)) {
//if (D->slicer[slice].data_detect) {
char fname[30];
int slice = 0;
@ -516,6 +526,7 @@ inline static void nudge_pll (int chan, int subchan, int slice, float demod_out_
/* Overflow. Was large positive, wrapped around, now large negative. */
hdlc_rec_bit (chan, subchan, slice, demod_out_f > 0, D->modem_type == MODEM_SCRAMBLE, D->slicer[slice].lfsr);
pll_dcd_each_symbol2 (D, chan, subchan, slice);
}
/*
@ -526,11 +537,11 @@ inline static void nudge_pll (int chan, int subchan, int slice, float demod_out_
// Note: Test for this demodulator, not overall for channel.
float target = 0;
pll_dcd_signal_transition2 (D, slice, D->slicer[slice].data_clock_pll);
target = D->pll_step_per_sample * demod_out_f / (demod_out_f - D->slicer[slice].prev_demod_out_f);
float target = D->pll_step_per_sample * demod_out_f / (demod_out_f - D->slicer[slice].prev_demod_out_f);
if (hdlc_rec_gathering (chan, subchan, slice)) {
if (D->slicer[slice].data_detect) {
D->slicer[slice].data_clock_pll = (int)(D->slicer[slice].data_clock_pll * D->pll_locked_inertia + target * (1.0f - D->pll_locked_inertia) );
}
else {
@ -542,7 +553,7 @@ inline static void nudge_pll (int chan, int subchan, int slice, float demod_out_
#if DEBUG5
//if (chan == 0) {
if (hdlc_rec_gathering (chan,subchan,slice)) {
if (D->slicer[slice].data_detect) {
char fname[30];

View File

@ -762,7 +762,7 @@ void demod_afsk_process_sample (int chan, int subchan, int sam, struct demodulat
#if DEBUG4
if (chan == 0) {
if (hdlc_rec_gathering (chan, subchan)) {
if (D->slicer[slice].data_detect) {
char fname[30];
@ -839,16 +839,18 @@ inline static void nudge_pll (int chan, int subchan, int slice, int demod_data,
if (D->slicer[slice].data_clock_pll < 0 && D->slicer[slice].prev_d_c_pll > 0) {
/* Overflow. */
/* Overflow - this is where we sample. */
hdlc_rec_bit (chan, subchan, slice, demod_data, 0, -1);
pll_dcd_each_symbol2 (D, chan, subchan, slice);
}
// Even if we used alternative method to extract the data bit,
// we still use the low pass output for the PLL.
// Transitions nudge the DPLL phase toward the incoming signal.
if (demod_data != D->slicer[slice].prev_demod_data) {
if (hdlc_rec_gathering (chan, subchan, slice)) {
pll_dcd_signal_transition2 (D, slice, D->slicer[slice].data_clock_pll);
if (D->slicer[slice].data_detect) {
D->slicer[slice].data_clock_pll = (int)(D->slicer[slice].data_clock_pll * D->pll_locked_inertia);
}
else {

View File

@ -86,16 +86,25 @@
#include <assert.h>
#include <ctype.h>
// Fine tuning for different demodulator types.
#define DCD_THRESH_ON 30 // Hysteresis: Can miss 2 out of 32 for detecting lock.
#define DCD_THRESH_OFF 6 // Might want a little more fine tuning.
#define DCD_GOOD_WIDTH 512
#include "fsk_demod_state.h" // Values above override defaults.
#include "audio.h"
#include "tune.h"
#include "fsk_demod_state.h"
#include "fsk_gen_filter.h"
#include "hdlc_rec.h"
#include "textcolor.h"
#include "demod_psk.h"
#include "dsp.h"
static const int phase_to_gray_v26[4] = {0, 1, 3, 2};
static const int phase_to_gray_v27[8] = {1, 0, 2, 3, 7, 6, 4, 5};
@ -800,6 +809,7 @@ static void nudge_pll (int chan, int subchan, int slice, int demod_bits, struct
hdlc_rec_bit (chan, subchan, slice, (gray >> 1) & 1, 0, bit_quality[1]);
hdlc_rec_bit (chan, subchan, slice, gray & 1, 0, bit_quality[0]);
}
pll_dcd_each_symbol2 (D, chan, subchan, slice);
}
/*
@ -813,7 +823,9 @@ static void nudge_pll (int chan, int subchan, int slice, int demod_bits, struct
if (demod_bits != D->slicer[slice].prev_demod_data) {
if (hdlc_rec_gathering (chan, subchan, slice)) {
pll_dcd_signal_transition2 (D, slice, D->slicer[slice].data_clock_pll);
if (D->slicer[slice].data_detect) {
D->slicer[slice].data_clock_pll = (int)floorf((float)(D->slicer[slice].data_clock_pll) * D->pll_locked_inertia);
}
else {

View File

@ -226,6 +226,8 @@ struct demodulator_state_s
signed int data_clock_pll; // PLL for data clock recovery.
// It is incremented by pll_step_per_sample
// for each audio sample.
// Must be 32 bits!!!
// So far, this is the case for every compiler used.
signed int prev_d_c_pll; // Previous value of above, before
// incrementing, to detect overflows.
@ -238,6 +240,18 @@ struct demodulator_state_s
int lfsr; // Descrambler shift register.
// This is for detecting phase lock to incoming signal.
int good_flag; // Set if transition is near where expected,
// i.e. at a good time.
int bad_flag; // Set if transition is not where expected,
// i.e. at a bad time.
unsigned char good_hist; // History of good transitions for past octet.
unsigned char bad_hist; // History of bad transitions for past octet.
unsigned int score; // History of whether good triumphs over bad
// for past 32 symbols.
int data_detect; // True when locked on to signal.
} slicer [MAX_SLICERS]; // Actual number in use is num_slicers.
// Should be in range 1 .. MAX_SLICERS,
/*
@ -336,5 +350,100 @@ struct demodulator_state_s
};
/*-------------------------------------------------------------------
*
* Name: pll_dcd_signal_transition2
* dcd_each_symbol2
*
* Purpose: New DCD strategy for 1.6.
*
* Inputs: D Pointer to demodulator state.
*
* chan Radio channel: 0 to MAX_CHANS - 1
*
* subchan Which of multiple demodulators: 0 to MAX_SUBCHANS - 1
*
* slice Slicer number: 0 to MAX_SLICERS - 1.
*
* dpll_phase Signed 32 bit counter for DPLL phase.
* Wraparound is where data is sampled.
* Ideally transitions would occur close to 0.
*
* Output: D->slicer[slice].data_detect - true when PLL is locked to incoming signal.
*
* Description: From the beginning, DCD was based on finding several flag octets
* in a row and dropping when eight bits with no transitions.
* It was less than ideal but we limped along with it all these years.
* This fell apart when FX.25 came along and a couple of the
* correlation tags have eight "1" bits in a row.
*
* Our new strategy is to keep a running score of how well demodulator
* output transitions match to where expected.
*
*--------------------------------------------------------------------*/
#include "hdlc_rec.h" // for dcd_change
// These are good for 1200 bps AFSK.
// Might want to override for other modems.
#ifndef DCD_THRESH_ON
#define DCD_THRESH_ON 30 // Hysteresis: Can miss 2 out of 32 for detecting lock.
// 31 is best for TNC Test CD. 30 almost as good.
// 30 better for 1200 regression test.
#endif
#ifndef DCD_THRESH_OFF
#define DCD_THRESH_OFF 6 // Might want a little more fine tuning.
#endif
#ifndef DCD_GOOD_WIDTH
#define DCD_GOOD_WIDTH 512 // No more than 1024!!!
#endif
__attribute__((always_inline))
inline static void pll_dcd_signal_transition2 (struct demodulator_state_s *D, int slice, int dpll_phase)
{
if (dpll_phase > - DCD_GOOD_WIDTH * 1024 * 1024 && dpll_phase < DCD_GOOD_WIDTH * 1024 * 1024) {
D->slicer[slice].good_flag = 1;
}
else {
D->slicer[slice].bad_flag = 1;
}
}
__attribute__((always_inline))
inline static void pll_dcd_each_symbol2 (struct demodulator_state_s *D, int chan, int subchan, int slice)
{
D->slicer[slice].good_hist <<= 1;
D->slicer[slice].good_hist |= D->slicer[slice].good_flag;
D->slicer[slice].good_flag = 0;
D->slicer[slice].bad_hist <<= 1;
D->slicer[slice].bad_hist |= D->slicer[slice].bad_flag;
D->slicer[slice].bad_flag = 0;
D->slicer[slice].score <<= 1;
// 2 is to detect 'flag' patterns with 2 transitions per octet.
D->slicer[slice].score |= (signed)__builtin_popcount(D->slicer[slice].good_hist)
- (signed)__builtin_popcount(D->slicer[slice].bad_hist) >= 2;
int s = __builtin_popcount(D->slicer[slice].score);
if (s >= DCD_THRESH_ON) {
if (D->slicer[slice].data_detect == 0) {
D->slicer[slice].data_detect = 1;
dcd_change (chan, subchan, slice, D->slicer[slice].data_detect);
}
}
else if (s <= DCD_THRESH_OFF) {
if (D->slicer[slice].data_detect != 0) {
D->slicer[slice].data_detect = 0;
dcd_change (chan, subchan, slice, D->slicer[slice].data_detect);
}
}
}
#define FSK_DEMOD_STATE_H 1
#endif
#endif

View File

@ -117,6 +117,11 @@ static void send_packet (char *str)
}
else {
pp = ax25_from_text (str, 1);
if (pp == NULL) {
text_color_set(DW_COLOR_ERROR);
dw_printf ("\"%s\" is not valid TNC2 monitoring format.\n", str);
return;
}
flen = ax25_pack (pp, fbuf);
for (c=0; c<modem.adev[0].num_channels; c++)
{
@ -124,8 +129,7 @@ static void send_packet (char *str)
#if 1
int samples_per_symbol, n, j;
/* Insert random amount of quiet time, approx. 0 to 10 symbol times, to test */
/* how well the clock recovery PLL can regain lock after random phase shifts. */
// Insert random amount of quiet time.
if (modem.achan[c].modem_type == MODEM_QPSK) {
samples_per_symbol = modem.adev[0].samples_per_sec / (modem.achan[c].baud / 2);
@ -137,14 +141,19 @@ static void send_packet (char *str)
samples_per_symbol = modem.adev[0].samples_per_sec / modem.achan[c].baud;
}
// for 1200 baud, 44100/sec, this should be 0 to 360.
n = samples_per_symbol * 10 * (float)my_rand() / (float)MY_RAND_MAX;
// Provide enough time for the DCD to drop.
// Then throw in a random amount of time so that receiving
// DPLL will need to adjust to a new phase.
n = samples_per_symbol * (32 + (float)my_rand() / (float)MY_RAND_MAX );
//dw_printf ("Random 0-360 = %d\n", n);
for (j=0; j<n; j++) {
gen_tone_put_sample (c, 0, 0);
}
#endif
hdlc_send_flags (c, 8, 0);
hdlc_send_flags (c, 8, 0);
hdlc_send_flags (c, 8, 0);
hdlc_send_flags (c, 8, 0);
hdlc_send_frame (c, fbuf, flen, 0, modem.fx25_xmit_enable);
hdlc_send_flags (c, 2, 1);

View File

@ -101,10 +101,6 @@ struct hdlc_state_s {
int frame_len; /* Number of octets in frame_buf. */
/* Should be in range of 0 .. MAX_FRAME_LEN. */
int data_detect; /* True when HDLC data is detected. */
/* This will not be triggered by voice or other */
/* noise or even tones. */
rrbb_t rrbb; /* Handle for bit array for raw received bits. */
};
@ -130,6 +126,7 @@ static int was_init = 0;
static struct audio_s *g_audio_p;
void hdlc_rec_init (struct audio_s *pa)
{
int ch, sub, slice;
@ -203,6 +200,7 @@ static int my_rand (void) {
* is_scrambled - Is the data scrambled?
*
* descram_state - Current descrambler state. (not used - remove)
* Not so fast - plans to add new parameter. PSK already provides it.
*
*
* Description: This is called once for each received bit.
@ -285,94 +283,6 @@ void hdlc_rec_bit (int chan, int subchan, int slice, int raw, int is_scrambled,
H->flag4_det |= 0x80000000;
}
/*
* "Data Carrier detect" function based on data patterns rather than
* audio signal strength.
*
* Idle time, at beginning of transmission should be filled
* with the special "flag" characters.
*
* Idle time of all zero bits (alternating tones at maximum rate)
* has also been observed rarely. It is easy to understand the reasoning.
* The tones alternate at the maximum rate, making it symmetrical and providing
* the most opportunity for the PLL to lock on to the edges.
* It also violates the published protocol spec.
*
* Recognize zero(s) followed by a single flag even though it violates the spec.
*
* It has been reported that the TinyTrak4 does this.
* https://groups.yahoo.com/neo/groups/direwolf_packet/conversations/messages/1207
*/
/*
* Originally, this looked for 4 flags in a row or 3 zeros and a flag.
* Is that too fussy?
* Here are the numbers of start of DCD for our favorite Track 2 test.
*
* 7e7e7e7e 504 7e000000 32
* 7e7e7e-- 513 7e0000-- 33
* 7e7e---- 555 7e00---- 42
* 7e------ 2088
*
* I don't think we want to look for a single flag because that would
* make DCD too sensitive to noise and it would interfere with waiting for a
* clear channel to transmit. Even a two byte match causes a lot of flickering
* when listening to live signals. Let's try 3 and see how that works out.
*/
//if (H->flag4_det == 0x7e7e7e7e) {
if ((H->flag4_det & 0xffffff00) == 0x7e7e7e00) { // three seems good
//if ((H->flag4_det & 0xffff0000) == 0x7e7e0000) { // two in a row
//if ((H->flag4_det & 0xff000000) == 0x7e000000) { // single flag
if ( ! H->data_detect) {
H->data_detect = 1;
dcd_change (chan, subchan, slice, 1);
}
}
//else if (H->flag4_det == 0x7e000000) {
else if ((H->flag4_det & 0xffffff00) == 0x7e000000) {
//else if ((H->flag4_det & 0xffff0000) == 0x7e000000) {
if ( ! H->data_detect) {
H->data_detect = 1;
dcd_change (chan, subchan, slice, 1);
}
}
/*
* Loss of signal should result in lack of transitions.
* (all '1' bits) for at least a little while.
*
* When this was written, I was only concerned about 1200 baud.
* For 9600, added later, there is a (de)scrambling function.
* So if there is no change in the signal, we would get pseudo random bits here.
* Maybe we need to put in another check earlier so DCD is not held on too long
* after loss of signal for 9600.
* No, that would not be a good idea. part of a valid frame, when scrambled,
* could have seven or more "1" bits in a row.
* Needs more study.
*/
if (H->pat_det == 0xff) {
if ( H->data_detect ) {
H->data_detect = 0;
dcd_change (chan, subchan, slice, 0);
}
}
/*
* End of data carrier detect.
*
* The rest is concerned with framing.
*/
rrbb_append_bit (H->rrbb, raw);
if (H->pat_det == 0x7e) {
@ -554,47 +464,10 @@ void hdlc_rec_bit (int chan, int subchan, int slice, int raw, int is_scrambled,
}
}
/*-------------------------------------------------------------------
*
* Name: hdlc_rec_gathering
*
* Purpose: Report whether bits are currently being gathered into a frame.
* This is used to influence the PLL inertia.
* The idea is that the PLL should be a little more agreeable to
* synchronize with the incoming data stream when not in a frame
* and resist changing a little more when capturing a frame.
*
* Inputs: chan
* subchan
* slice
*
* Returns: True if we are currently gathering bits.
* In this case we want the PLL to have more inertia.
*
* 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 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][slice].data_detect );
} /* end hdlc_rec_gathering */
// TODO: Data Carrier Detect (DCD) is now based on DPLL lock
// rather than data patterns found here.
// It would make sense to move the next 2 functions to demod.c
// because this is done at the modem level, rather than HDLC decoder.
/*-------------------------------------------------------------------
*

View File

@ -3,5 +3,5 @@
@GEN_PACKETS_BIN@ -r 96000 -B19200 -a 170 -o test19.wav
@ATEST_BIN@ -B19200 -F0 -L4 test19.wav
@GEN_PACKETS_BIN@ -r 96000 -B19200 -n 100 -o test19.wav
@ATEST_BIN@ -B19200 -F0 -L60 -G64 test19.wav
@ATEST_BIN@ -B19200 -F1 -L64 -G68 test19.wav
@ATEST_BIN@ -B19200 -F0 -L60 -G66 test19.wav
@ATEST_BIN@ -B19200 -F1 -L64 -G69 test19.wav

View File

@ -1,5 +1,5 @@
@CUSTOM_SHELL_SHABANG@
@GEN_PACKETS_BIN@ -B2400 -j -n 100 -o test24-a.wav
@ATEST_BIN@ -B2400 -j -F0 -L76 -G80 test24-a.wav
@ATEST_BIN@ -B2400 -j -F1 -L84 -G88 test24-a.wav
@ATEST_BIN@ -B2400 -j -F0 -L76 -G83 test24-a.wav
@ATEST_BIN@ -B2400 -j -F1 -L84 -G89 test24-a.wav

View File

@ -1,5 +1,5 @@
@CUSTOM_SHELL_SHABANG@
@GEN_PACKETS_BIN@ -B2400 -J -n 100 -o test24-b.wav
@ATEST_BIN@ -B2400 -J -F0 -L84 -G88 test24-b.wav
@ATEST_BIN@ -B2400 -J -F0 -L81 -G88 test24-b.wav
@ATEST_BIN@ -B2400 -J -F1 -L86 -G90 test24-b.wav

View File

@ -1,4 +1,4 @@
@CUSTOM_SHELL_SHABANG@
@GEN_PACKETS_BIN@ -B2400 -g -n 100 -o test24-g.wav
@ATEST_BIN@ -B2400 -g -F0 -L99 -G100 test24-g.wav
@ATEST_BIN@ -B2400 -g -F0 -L99 -G101 test24-g.wav

View File

@ -1,5 +1,5 @@
@CUSTOM_SHELL_SHABANG@
@GEN_PACKETS_BIN@ -B4800 -n 100 -o test48.wav
@ATEST_BIN@ -B4800 -F0 -L70 -G74 test48.wav
@ATEST_BIN@ -B4800 -F1 -L79 -G84 test48.wav
@ATEST_BIN@ -B4800 -F0 -L68 -G74 test48.wav
@ATEST_BIN@ -B4800 -F1 -L72 -G84 test48.wav