//
// This file is part of Dire Wolf, an amateur radio packet TNC.
//
// Copyright (C) 2011,2012,2013 John Langner, WB2OSZ
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see .
//
/********************************************************************************
*
* File: hdlc_rec.c
*
* Purpose: Extract HDLC frames from a stream of bits.
*
*******************************************************************************/
#include
#include
#include "direwolf.h"
#include "demod.h"
#include "hdlc_rec.h"
#include "hdlc_rec2.h"
#include "fcs_calc.h"
#include "textcolor.h"
#include "ax25_pad.h"
#include "rrbb.h"
#include "multi_modem.h"
//#define TEST 1 /* Define for unit testing. */
//#define DEBUG3 1 /* monitor the data detect signal. */
/*
* Minimum & maximum sizes of an AX.25 frame including the 2 octet FCS.
*/
#define MIN_FRAME_LEN ((AX25_MIN_PACKET_LEN) + 2)
#define MAX_FRAME_LEN ((AX25_MAX_PACKET_LEN) + 2)
/*
* This is the current state of the HDLC decoder.
*
* It is possible to run multiple decoders concurrently by
* having a separate set of state variables for each.
*
* Should have a reset function instead of initializations here.
*/
struct hdlc_state_s {
int prev_raw; /* Keep track of previous bit so */
/* we can look for transitions. */
/* Should be only 0 or 1. */
unsigned char pat_det; /* 8 bit pattern detector shift register. */
/* See below for more details. */
unsigned int flag4_det; /* Last 32 raw bits to look for 4 */
/* flag patterns in a row. */
unsigned char oacc; /* Accumulator for building up an octet. */
int olen; /* Number of bits in oacc. */
/* When this reaches 8, oacc is copied */
/* to the frame buffer and olen is zeroed. */
/* The value of -1 is a special case meaning */
/* bits should not be accumulated. */
unsigned char frame_buf[MAX_FRAME_LEN];
/* One frame is kept here. */
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. */
enum retry_e fix_bits; /* Level of effort to recover from */
/* a bad FCS on the frame. */
rrbb_t rrbb; /* Handle for bit array for raw received bits. */
};
static struct hdlc_state_s hdlc_state[MAX_CHANS][MAX_SUBCHANS];
static int num_subchan[MAX_CHANS];
/***********************************************************************************
*
* Name: hdlc_rec_init
*
* Purpose: Call once at the beginning to initialize.
*
* Inputs: None.
*
***********************************************************************************/
static int was_init = 0;
void hdlc_rec_init (struct audio_s *pa)
{
int j, k;
struct hdlc_state_s *H;
//text_color_set(DW_COLOR_DEBUG);
//dw_printf ("hdlc_rec_init (%p) \n", pa);
assert (pa != NULL);
for (j=0; jnum_channels; j++)
{
num_subchan[j] = pa->num_subchan[j];
assert (num_subchan[j] >= 1 && num_subchan[j] < MAX_SUBCHANS);
for (k=0; kprev_raw = 0;
H->pat_det = 0;
H->flag4_det = 0;
H->olen = -1;
H->frame_len = 0;
H->data_detect = 0;
H->fix_bits = pa->fix_bits;
H->rrbb = rrbb_new(j, k, pa->modem_type[j] == SCRAMBLE, -1);
}
}
was_init = 1;
}
/***********************************************************************************
*
* Name: hdlc_rec_bit
*
* Purpose: Extract HDLC frames from a stream of bits.
*
* Inputs: chan - Channel number.
*
* subchan - This allows multiple decoders per channel.
*
* raw - One bit from the demodulator.
* should be 0 or 1.
*
* is_scrambled - Is the data scrambled?
*
* descram_state - Current descrambler state.
*
* sam - Possible future: Sample from demodulator output.
* Should nominally be in roughly -1 to +1 range.
*
* Description: This is called once for each received bit.
* For each valid frame, process_rec_frame()
* is called for further processing.
*
***********************************************************************************/
#if SLICENDICE
void hdlc_rec_bit_sam (int chan, int subchan, int raw, float demod_out)
#else
void hdlc_rec_bit (int chan, int subchan, int raw, int is_scrambled, int descram_state)
#endif
{
int dbit; /* Data bit after undoing NRZI. */
/* Should be only 0 or 1. */
struct hdlc_state_s *H;
assert (was_init == 1);
assert (chan >= 0 && chan < MAX_CHANS);
assert (subchan >= 0 && subchan < MAX_SUBCHANS);
/*
* Different state information for each channel.
*/
H = &hdlc_state[chan][subchan];
/*
* Using NRZI encoding,
* A '0' bit is represented by an inversion since previous bit.
* A '1' bit is represented by no change.
*/
dbit = (raw == H->prev_raw);
H->prev_raw = raw;
/*
* Octets are sent LSB first.
* Shift the most recent 8 bits thru the pattern detector.
*/
H->pat_det >>= 1;
if (dbit) {
H->pat_det |= 0x80;
}
H->flag4_det >>= 1;
if (dbit) {
H->flag4_det |= 0x80000000;
}
/*
* "Data Carrier detect" function based on data rather than
* tones from a modem.
*
* 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.
*/
if (H->flag4_det == 0x7e7e7e7e) {
if ( ! H->data_detect) {
H->data_detect = 1;
#if DEBUG3
text_color_set(DW_COLOR_DEBUG);
dw_printf ("DCD%d = 1 flags\n", chan);
#endif
}
}
if (H->flag4_det == 0x7e000000) {
if ( ! H->data_detect) {
H->data_detect = 1;
#if DEBUG3
text_color_set(DW_COLOR_DEBUG);
dw_printf ("DCD%d = 1 zero fill\n", chan);
#endif
}
}
/*
* Loss of signal should result in lack of transitions.
* (all '1' bits) for at least a little while.
*/
if (H->pat_det == 0xff) {
if ( H->data_detect ) {
H->data_detect = 0;
#if DEBUG3
text_color_set(DW_COLOR_DEBUG);
dw_printf ("DCD%d = 0\n", chan);
#endif
}
}
/*
* End of data carrier detect.
*
* The rest is concerned with framing.
*/
#if SLICENDICE
rrbb2_append_bit (H->rrbb, demod_out);
#else
rrbb_append_bit (H->rrbb, raw);
#endif
if (H->pat_det == 0x7e) {
rrbb_chop8 (H->rrbb);
/*
* The special pattern 01111110 indicates beginning and ending of a frame.
* If we have an adequate number of whole octets, it is a candidate for
* further processing.
*
* It might look odd that olen is being tested for 7 instead of 0.
* This is because oacc would already have 7 bits from the special
* "flag" pattern before it is detected here.
*/
#if OLD_WAY
#if TEST
text_color_set(DW_COLOR_DEBUG);
dw_printf ("\nfound flag, olen = %d, frame_len = %d\n", olen, frame_len);
#endif
if (H->olen == 7 && H->frame_len >= MIN_FRAME_LEN) {
unsigned short actual_fcs, expected_fcs;
#if TEST
int j;
dw_printf ("TRADITIONAL: frame len = %d\n", H->frame_len);
for (j=0; jframe_len; j++) {
dw_printf (" %02x", H->frame_buf[j]);
}
dw_printf ("\n");
#endif
/* Check FCS, low byte first, and process... */
/* Alternatively, it is possible to include the two FCS bytes */
/* in the CRC calculation and look for a magic constant. */
/* That would be easier in the case where the CRC is being */
/* accumulated along the way as the octets are received. */
/* I think making a second pass over it and comparing is */
/* easier to understand. */
actual_fcs = H->frame_buf[H->frame_len-2] | (H->frame_buf[H->frame_len-1] << 8);
expected_fcs = fcs_calc (H->frame_buf, H->frame_len - 2);
if (actual_fcs == expected_fcs) {
int 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. */
}
else {
#if TEST
dw_printf ("*** actual fcs = %04x, expected fcs = %04x ***\n", actual_fcs, expected_fcs);
#endif
}
}
#else
/*
* New way - Decode the raw bits in later step.
*/
#if TEST
text_color_set(DW_COLOR_DEBUG);
dw_printf ("\nfound flag, %d bits in frame\n", rrbb_get_len(H->rrbb) - 1);
#endif
if (rrbb_get_len(H->rrbb) >= MIN_FRAME_LEN * 8) {
int alevel = demod_get_audio_level (chan, subchan);
rrbb_set_audio_level (H->rrbb, alevel);
hdlc_rec2_block (H->rrbb, H->fix_bits);
/* Now owned by someone else who will free it. */
H->rrbb = rrbb_new (chan, subchan, is_scrambled, descram_state); /* Allocate a new one. */
}
else {
rrbb_clear (H->rrbb, is_scrambled, descram_state);
}
H->olen = 0; /* Allow accumulation of octets. */
H->frame_len = 0;
#if SLICENDICE
rrbb2_append_bit (H->rrbb, H->prev_raw ? 1.0 : -1.0); /* Last bit of flag. Needed to get first data bit. */
#else
rrbb_append_bit (H->rrbb, H->prev_raw); /* Last bit of flag. Needed to get first data bit. */
#endif
#endif
}
else if (H->pat_det == 0xfe) {
/*
* Valid data will never have 7 one bits in a row.
*
* 11111110
*
* This indicates loss of signal.
*/
H->olen = -1; /* Stop accumulating octets. */
H->frame_len = 0; /* Discard anything in progress. */
rrbb_clear (H->rrbb, is_scrambled, descram_state);
#if SLICENDICE
rrbb2_append_bit (H->rrbb, H->prev_raw ? 1.0 : -1.0); /* Last bit of flag. Needed to get first data bit. */
#else
rrbb_append_bit (H->rrbb, H->prev_raw); /* Last bit of flag. Needed to get first data bit. */
#endif
}
else if ( (H->pat_det & 0xfc) == 0x7c ) {
/*
* If we have five '1' bits in a row, followed by a '0' bit,
*
* 0111110xx
*
* the current '0' bit should be discarded because it was added for
* "bit stuffing."
*/
;
} else {
/*
* In all other cases, accumulate bits into octets, and complete octets
* into the frame buffer.
*/
if (H->olen >= 0) {
H->oacc >>= 1;
if (dbit) {
H->oacc |= 0x80;
}
H->olen++;
if (H->olen == 8) {
H->olen = 0;
if (H->frame_len < MAX_FRAME_LEN) {
H->frame_buf[H->frame_len] = H->oacc;
H->frame_len++;
}
}
}
}
}
/*-------------------------------------------------------------------
*
* Name: hdlc_rec_data_detect_1
* hdlc_rec_data_detect_any
*
* Purpose: Determine if the radio channel is curently busy
* with packet data.
* This version doesn't care about voice or other sounds.
* This is used by the transmit logic to transmit only
* when the channel is clear.
*
* Inputs: chan - Audio channel. 0 for left, 1 for right.
*
* Returns: True if channel is busy (data detected) or
* false if OK to transmit.
*
*
* Description: We have two different versions here.
*
* hdlc_rec_data_detect_1 tests a single decoder (subchan)
* and is used by the DPLL to determine how much inertia
* to use when trying to follow the incoming signal.
*
* hdlc_rec_data_detect_any sees if ANY of the decoders
* for this channel are receving a signal. This is
* used to determine whether the channel is clear and
* we can transmit. This would apply to the 300 baud
* HF SSB case where we have multiple decoders running
* at the same time. The channel is busy if ANY of them
* thinks the channel is busy.
*
*--------------------------------------------------------------------*/
int hdlc_rec_data_detect_1 (int chan, int subchan)
{
assert (chan >= 0 && chan < MAX_CHANS);
return ( hdlc_state[chan][subchan].data_detect );
} /* end hdlc_rec_data_detect_1 */
int hdlc_rec_data_detect_any (int chan)
{
int subchan;
assert (chan >= 0 && chan < MAX_CHANS);
for (subchan = 0; subchan < num_subchan[chan]; subchan++) {
assert (subchan >= 0 && subchan < MAX_SUBCHANS);
if (hdlc_state[chan][subchan].data_detect) {
return (1);
}
}
return (0);
} /* end hdlc_rec_data_detect_any */
/* end hdlc_rec.c */