// // This file is part of Dire Wolf, an amateur radio packet TNC. // // Copyright (C) 2011, 2012, 2013, 2014, 2015 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 #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" #include "demod_9600.h" /* for descramble() */ #include "ptt.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. */ int lfsr; /* Descrambler shift register for 9600 baud. */ int prev_descram; /* Previous descrambled for 9600 baud. */ 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. */ rrbb_t rrbb; /* Handle for bit array for raw received bits. */ }; 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][MAX_SUBCHANS+1]; /*********************************************************************************** * * 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 ch, sub, slice; struct hdlc_state_s *H; //text_color_set(DW_COLOR_DEBUG); //dw_printf ("hdlc_rec_init (%p) \n", pa); assert (pa != NULL); memset (composite_dcd, 0, sizeof(composite_dcd)); for (ch = 0; ch < MAX_CHANS; ch++) { if (pa->achan[ch].valid) { num_subchan[ch] = pa->achan[ch].num_subchan; assert (num_subchan[ch] >= 1 && num_subchan[ch] <= MAX_SUBCHANS); for (sub = 0; sub < num_subchan[ch]; sub++) { for (slice = 0; slice < MAX_SLICERS; slice++) { 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; } /*********************************************************************************** * * Name: hdlc_rec_bit * * Purpose: Extract HDLC frames from a stream of bits. * * Inputs: chan - Channel number. * * 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. (not used - remove) * * * Description: This is called once for each received bit. * For each valid frame, process_rec_frame() * is called for further processing. * ***********************************************************************************/ // TODO: 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. */ /* 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); assert (slice >= 0 && slice < MAX_SLICERS); /* * Different state information for each channel / subchannel / slice. */ H = &hdlc_state[chan][subchan][slice]; /* * Using NRZI encoding, * A '0' bit is represented by an inversion since previous bit. * A '1' bit is represented by no change. */ if (is_scrambled) { int descram; descram = descramble(raw, &(H->lfsr)); dbit = (descram == H->prev_descram); H->prev_descram = descram; H->prev_raw = raw; } else { 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 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. * Recognize zero(s) followed by a flag even though it vilolates the spec. */ /* * 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) { //if ((H->flag4_det & 0xffff0000) == 0x7e7e0000) { 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. */ 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) { 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) { alevel_t alevel = demod_get_audio_level (chan, subchan); multi_modem_process_rec_frame (chan, subchan, slice, 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, channel %d.%d, %d bits in frame\n", chan, subchan, rrbb_get_len(H->rrbb) - 1); #endif if (rrbb_get_len(H->rrbb) >= MIN_FRAME_LEN * 8) { alevel_t alevel = demod_get_audio_level (chan, subchan); 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, slice, is_scrambled, H->lfsr, H->prev_descram); /* Allocate a new one. */ } else { rrbb_clear (H->rrbb, is_scrambled, H->lfsr, H->prev_descram); } H->olen = 0; /* Allow accumulation of octets. */ H->frame_len = 0; rrbb_append_bit (H->rrbb, H->prev_raw); /* Last bit of flag. Needed to get first data bit. */ /* Now that we are saving other initial state information, */ /* it would be sensible to do the same for this instead */ /* of lumping it in with the frame data bits. */ #endif } //#define EXPERIMENT12B 1 #if EXPERIMENT12B else if (H->pat_det == 0xff) { /* * Valid data will never have seven 1 bits in a row. * * 11111110 * * This indicates loss of signal. * But we will let it slip thru because it might diminish * our single bit fixup effort. Instead give up on frame * only when we see eight 1 bits in a row. * * 11111111 * * What is the impact? No difference. * * Before: atest -P E -F 1 ../02_Track_2.wav = 1003 * After: atest -P E -F 1 ../02_Track_2.wav = 1003 */ #else else if (H->pat_det == 0xfe) { /* * Valid data will never have 7 one bits in a row. * * 11111110 * * This indicates loss of signal. */ #endif H->olen = -1; /* Stop accumulating octets. */ H->frame_len = 0; /* Discard anything in progress. */ rrbb_clear (H->rrbb, is_scrambled, H->lfsr, H->prev_descram); } 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_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 */ /*------------------------------------------------------------------- * * Name: dcd_change * * Purpose: Combine DCD states of all subchannels/ into an overall * state for the channel. * * Inputs: chan * * subchan 0 to MAX_SUBCHANS-1 for HDLC. * SPECIAL CASE --> MAX_SUBCHANS for DTMF decoder. * * slice slicer number, 0 .. MAX_SLICERS - 1. * * state 1 for active, 0 for not. * * Returns: None. Use hdlc_rec_data_detect_any to retrieve result. * * 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 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 = %d \n", chan, subchan, slice, state); #endif old = hdlc_rec_data_detect_any(chan); if (state) { composite_dcd[chan][subchan] |= (1 << slice); } else { composite_dcd[chan][subchan] &= ~ (1 << slice); } new = hdlc_rec_data_detect_any(chan); if (new != old) { ptt_set (OCTYPE_DCD, chan, new); } } /*------------------------------------------------------------------- * * Name: 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. * * 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_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_any (int chan) { int sc; assert (chan >= 0 && chan < MAX_CHANS); 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 */