int demod_init (struct audio_s *pa) { int j; int chan; /* Loop index over number of radio channels. */ char profile; /* * Save audio configuration for later use. */ save_audio_config_p = pa; for (chan = 0; chan < MAX_CHANS; chan++) { if (save_audio_config_p->achan[chan].valid) { char *p; char just_letters[16]; int num_letters; int have_plus; switch (save_audio_config_p->achan[chan].modem_type) { case MODEM_OFF: break; case MODEM_AFSK: /* * Tear apart the profile and put it back together in a normalized form: * - At least one letter, supply suitable default if necessary. * - Upper case only. * - Any plus will be at the end. */ num_letters = 0; just_letters[num_letters] = '\0'; have_plus = 0; for (p = save_audio_config_p->achan[chan].profiles; *p != '\0'; p++) { if (islower(*p)) { just_letters[num_letters] = toupper(*p); num_letters++; just_letters[num_letters] = '\0'; } else if (isupper(*p)) { just_letters[num_letters] = *p; num_letters++; just_letters[num_letters] = '\0'; } else if (*p == '+') { have_plus = 1; if (p[1] != '\0') { text_color_set(DW_COLOR_ERROR); dw_printf ("Channel %d: + option must appear at end of demodulator types \"%s\" \n", chan, save_audio_config_p->achan[chan].profiles); } } else if (*p == '-') { have_plus = -1; if (p[1] != '\0') { text_color_set(DW_COLOR_ERROR); dw_printf ("Channel %d: - option must appear at end of demodulator types \"%s\" \n", chan, save_audio_config_p->achan[chan].profiles); } } else { text_color_set(DW_COLOR_ERROR); dw_printf ("Channel %d: Demodulator types \"%s\" can contain only letters and + - characters.\n", chan, save_audio_config_p->achan[chan].profiles); } } assert (num_letters == strlen(just_letters)); /* * Pick a good default demodulator if none specified. */ if (num_letters == 0) { if (save_audio_config_p->achan[chan].baud < 600) { /* This has been optimized for 300 baud. */ strcpy (just_letters, "D"); } else { #if __arm__ /* We probably don't have a lot of CPU power available. */ /* Previously we would use F if possible otherwise fall back to A. */ #if 0 if (save_audio_config_p->achan[chan].baud == FFF_BAUD && save_audio_config_p->achan[chan].mark_freq == FFF_MARK_FREQ && save_audio_config_p->achan[chan].space_freq == FFF_SPACE_FREQ && save_audio_config_p->adev[ACHAN2ADEV(chan)].samples_per_sec == FFF_SAMPLES_PER_SEC) { just_letters[0] = FFF_PROFILE; just_letters[1] = '\0'; } else { strcpy (just_letters, "A"); } #else /* In version 1.2, new default is E+ /3. */ strcpy (just_letters, "E"); // version 1.2 now E. if (have_plus != -1) have_plus = 1; // Add as default for version 1.2 // If not explicitly turned off. if (save_audio_config_p->achan[chan].decimate == 0) { save_audio_config_p->achan[chan].decimate = 3; } #endif #else strcpy (just_letters, "E"); // version 1.2 changed C to E. if (have_plus != -1) have_plus = 1; // Add as default for version 1.2 // If not explicitly turned off. #endif } num_letters = 1; } assert (num_letters == strlen(just_letters)); /* * Put it back together again. */ /* At this point, have_plus can have 3 values: */ /* 1 = turned on, either explicitly or by applied default */ /* -1 = explicitly turned off. change to 0 here so it is false. */ /* 0 = off by default. */ if (have_plus == -1) have_plus = 0; strcpy (save_audio_config_p->achan[chan].profiles, just_letters); assert (strlen(save_audio_config_p->achan[chan].profiles) >= 1); if (have_plus) { strcat (save_audio_config_p->achan[chan].profiles, "+"); } /* 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; /* * Some error checking - Can use only one of these: * * - Multiple letters. * - New + multi-slicer. * - 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); strcpy (save_audio_config_p->achan[chan].profiles, "C+"); // 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; } if (num_letters > 1 && save_audio_config_p->achan[chan].num_freq > 1) { text_color_set(DW_COLOR_ERROR); dw_printf ("Channel %d: Multiple demodulator types can't be combined with multiple frequencies.\n", chan); save_audio_config_p->achan[chan].profiles[1] = '\0'; num_letters = 1; } if (save_audio_config_p->achan[chan].decimate == 0) { save_audio_config_p->achan[chan].decimate = 1; if (strchr (just_letters, 'D') != NULL && save_audio_config_p->adev[ACHAN2ADEV(chan)].samples_per_sec > 40000) { save_audio_config_p->achan[chan].decimate = 3; } } text_color_set(DW_COLOR_DEBUG); dw_printf ("Channel %d: %d baud, AFSK %d & %d Hz, %s, %d sample rate", chan, save_audio_config_p->achan[chan].baud, save_audio_config_p->achan[chan].mark_freq, save_audio_config_p->achan[chan].space_freq, save_audio_config_p->achan[chan].profiles, save_audio_config_p->adev[ACHAN2ADEV(chan)].samples_per_sec); if (save_audio_config_p->achan[chan].decimate != 1) dw_printf (" / %d", save_audio_config_p->achan[chan].decimate); if (save_audio_config_p->achan[chan].dtmf_decode != DTMF_DECODE_OFF) dw_printf (", DTMF decoder enabled"); dw_printf (".\n"); /* * Initialize the demodulator(s). * * We have 3 cases to consider. */ if (num_letters > 1) { int d; /* * Multiple letters, usually for 1200 baud. * 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. */ save_audio_config_p->achan[chan].num_subchan = num_letters; save_audio_config_p->achan[chan].num_demod = num_letters; if (save_audio_config_p->achan[chan].num_demod != 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); } if (save_audio_config_p->achan[chan].num_freq != 1) { text_color_set(DW_COLOR_ERROR); dw_printf ("INTERNAL ERROR, %s:%d, chan=%d, num_freq(%d) != 1\n", __FILE__, __LINE__, chan, save_audio_config_p->achan[chan].num_freq); } for (d = 0; d < save_audio_config_p->achan[chan].num_demod; d++) { int mark, space; assert (d >= 0 && d < MAX_SUBCHANS); struct demodulator_state_s *D; D = &demodulator_state[chan][d]; profile = save_audio_config_p->achan[chan].profiles[d]; 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) { 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, save_audio_config_p->achan[chan].baud, mark, space, profile, D); /* For siginal level reporting, we want a longer term view. */ // TODO: Should probably move this into the init functions. D->quick_attack = D->agc_fast_attack * 0.2; D->sluggish_decay = D->agc_slow_decay * 0.2; } } else if (have_plus) { /* * PLUS - which implies we have only one letter and one frequency pair. * * One demodulator feeds multiple slicers, each a subchannel. */ if (num_letters != 1) { text_color_set(DW_COLOR_ERROR); dw_printf ("INTERNAL ERROR, %s:%d, chan=%d, strlen(\"%s\") != 1\n", __FILE__, __LINE__, chan, just_letters); } if (save_audio_config_p->achan[chan].num_freq != 1) { text_color_set(DW_COLOR_ERROR); dw_printf ("INTERNAL ERROR, %s:%d, chan=%d, num_freq(%d) != 1\n", __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) { 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); } 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; demod_afsk_init (save_audio_config_p->adev[ACHAN2ADEV(chan)].samples_per_sec / save_audio_config_p->achan[chan].decimate, save_audio_config_p->achan[chan].baud, save_audio_config_p->achan[chan].mark_freq, save_audio_config_p->achan[chan].space_freq, 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. */ D->num_slicers = MAX_SUBCHANS; /* For siginal level reporting, we want a longer term view. */ D->quick_attack = D->agc_fast_attack * 0.2; D->sluggish_decay = D->agc_slow_decay * 0.2; } else { int d; /* * One letter. * Can be combined with multiple frequencies. */ if (num_letters != 1) { text_color_set(DW_COLOR_ERROR); dw_printf ("INTERNAL ERROR, %s:%d, chan=%d, strlen(\"%s\") != 1\n", __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++) { int mark, space, k; assert (d >= 0 && d < MAX_SUBCHANS); struct demodulator_state_s *D; D = &demodulator_state[chan][d]; profile = save_audio_config_p->achan[chan].profiles[0]; k = d * save_audio_config_p->achan[chan].offset - ((save_audio_config_p->achan[chan].num_freq - 1) * save_audio_config_p->achan[chan].offset) / 2; mark = save_audio_config_p->achan[chan].mark_freq + k; space = save_audio_config_p->achan[chan].space_freq + k; if (save_audio_config_p->achan[chan].num_freq != 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, save_audio_config_p->achan[chan].baud, mark, space, profile, D); /* For siginal level reporting, we want a longer term view. */ D->quick_attack = D->agc_fast_attack * 0.2; D->sluggish_decay = D->agc_slow_decay * 0.2; } /* for each freq pair */ } break; //TODO: how about MODEM_OFF case? case MODEM_BASEBAND: case MODEM_SCRAMBLE: default: /* Not AFSK */ { if (strcmp(save_audio_config_p->achan[chan].profiles, "") == 0) { /* Apply default if not set earlier. */ /* Not sure if it should be on for ARM too. */ /* Need to take a look at CPU usage and performance difference. */ #ifndef __arm__ strcpy (save_audio_config_p->achan[chan].profiles, "+"); #endif } text_color_set(DW_COLOR_DEBUG); dw_printf ("Channel %d: %d baud, K9NG/G3RUH, %s, %d sample rate x %d", chan, save_audio_config_p->achan[chan].baud, save_audio_config_p->achan[chan].profiles, save_audio_config_p->adev[ACHAN2ADEV(chan)].samples_per_sec, UPSAMPLE); if (save_audio_config_p->achan[chan].dtmf_decode != DTMF_DECODE_OFF) dw_printf (", DTMF decoder enabled"); dw_printf (".\n"); struct demodulator_state_s *D; 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; 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; } demod_9600_init (UPSAMPLE * save_audio_config_p->adev[ACHAN2ADEV(chan)].samples_per_sec, save_audio_config_p->achan[chan].baud, D); if (strchr(save_audio_config_p->achan[chan].profiles, '+') != NULL) { /* 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; } /* For siginal level reporting, we want a longer term view. */ D->quick_attack = D->agc_fast_attack * 0.2; D->sluggish_decay = D->agc_slow_decay * 0.2; } break; } /* switch on modulation type. */ } /* if channel number is valid */ } /* for chan ... */ return (0); } /* end demod_init */
static void xmit_ax25_frames (int c, int p, packet_t pp) { unsigned char fbuf[AX25_MAX_PACKET_LEN+2]; int flen; char stemp[1024]; /* max size needed? */ int info_len; unsigned char *pinfo; int pre_flags, post_flags; int num_bits; /* Total number of bits in transmission */ /* including all flags and bit stuffing. */ int duration; /* Transmission time in milliseconds. */ int already; int wait_more; int maxframe; /* Maximum number of frames for one transmission. */ int numframe; /* Number of frames sent during this transmission. */ /* * These are for timing of a transmission. * All are in usual unix time (seconds since 1/1/1970) but higher resolution */ double time_ptt; /* Time when PTT is turned on. */ double time_now; /* Current time. */ int nb; maxframe = (p == TQ_PRIO_0_HI) ? 1 : 7; /* * Print trasmitted packet. Prefix by channel and priority. * Do this before we get into the time critical part. */ ax25_format_addrs (pp, stemp); info_len = ax25_get_info (pp, &pinfo); text_color_set(DW_COLOR_XMIT); dw_printf ("[%d%c] ", c, p==TQ_PRIO_0_HI ? 'H' : 'L'); dw_printf ("%s", stemp); /* stations followed by : */ ax25_safe_print ((char *)pinfo, info_len, ! ax25_is_aprs(pp)); dw_printf ("\n"); (void)ax25_check_addresses (pp); /* Optional hex dump of packet. */ if (g_debug_xmit_packet) { text_color_set(DW_COLOR_DEBUG); dw_printf ("------\n"); ax25_hex_dump (pp); dw_printf ("------\n"); } /* * Turn on transmitter. * Start sending leading flag bytes. */ time_ptt = dtime_now (); #if DEBUG text_color_set(DW_COLOR_DEBUG); dw_printf ("xmit_thread: Turn on PTT now for channel %d. speed = %d\n", c, xmit_bits_per_sec[c]); #endif ptt_set (OCTYPE_PTT, c, 1); pre_flags = MS_TO_BITS(xmit_txdelay[c] * 10, c) / 8; num_bits = hdlc_send_flags (c, pre_flags, 0); #if DEBUG text_color_set(DW_COLOR_DEBUG); dw_printf ("xmit_thread: txdelay=%d [*10], pre_flags=%d, num_bits=%d\n", xmit_txdelay[c], pre_flags, num_bits); #endif /* * Transmit the frame. */ flen = ax25_pack (pp, fbuf); assert (flen >= 1 && flen <= sizeof(fbuf)); nb = hdlc_send_frame (c, fbuf, flen); num_bits += nb; numframe = 1; #if DEBUG text_color_set(DW_COLOR_DEBUG); dw_printf ("xmit_thread: flen=%d, nb=%d, num_bits=%d, numframe=%d\n", flen, nb, num_bits, numframe); #endif ax25_delete (pp); /* * Additional packets if available and not exceeding max. */ while (numframe < maxframe && tq_count (c,p) > 0) { pp = tq_remove (c, p); #if DEBUG text_color_set(DW_COLOR_DEBUG); dw_printf ("xmit_thread: tq_remove(chan=%d, prio=%d) returned %p\n", c, p, pp); #endif ax25_format_addrs (pp, stemp); info_len = ax25_get_info (pp, &pinfo); text_color_set(DW_COLOR_XMIT); dw_printf ("[%d%c] ", c, p==TQ_PRIO_0_HI ? 'H' : 'L'); dw_printf ("%s", stemp); /* stations followed by : */ ax25_safe_print ((char *)pinfo, info_len, ! ax25_is_aprs(pp)); dw_printf ("\n"); (void)ax25_check_addresses (pp); if (g_debug_xmit_packet) { text_color_set(DW_COLOR_DEBUG); dw_printf ("------\n"); ax25_hex_dump (pp); dw_printf ("------\n"); } /* * Transmit the frame. */ flen = ax25_pack (pp, fbuf); assert (flen >= 1 && flen <= sizeof(fbuf)); nb = hdlc_send_frame (c, fbuf, flen); num_bits += nb; numframe++; #if DEBUG text_color_set(DW_COLOR_DEBUG); dw_printf ("xmit_thread: flen=%d, nb=%d, num_bits=%d, numframe=%d\n", flen, nb, num_bits, numframe); #endif ax25_delete (pp); } /* * Need TXTAIL because we don't know exactly when the sound is done. */ post_flags = MS_TO_BITS(xmit_txtail[c] * 10, c) / 8; nb = hdlc_send_flags (c, post_flags, 1); num_bits += nb; #if DEBUG text_color_set(DW_COLOR_DEBUG); dw_printf ("xmit_thread: txtail=%d [*10], post_flags=%d, nb=%d, num_bits=%d\n", xmit_txtail[c], post_flags, nb, num_bits); #endif /* * While demodulating is CPU intensive, generating the tones is not. * Example: on the RPi, with 50% of the CPU taken with two receive * channels, a transmission of more than a second is generated in * about 40 mS of elapsed real time. */ audio_wait(ACHAN2ADEV(c)); /* * Ideally we should be here just about the time when the audio is ending. * However, the innards of "audio_wait" are not satisfactory in all cases. * * Calculate how long the frame(s) should take in milliseconds. */ duration = BITS_TO_MS(num_bits, c); /* * See how long it has been since PTT was turned on. * Wait additional time if necessary. */ time_now = dtime_now(); already = (int) ((time_now - time_ptt) * 1000.); wait_more = duration - already; #if DEBUG text_color_set(DW_COLOR_DEBUG); dw_printf ("xmit_thread: xmit duration=%d, %d already elapsed since PTT, wait %d more\n", duration, already, wait_more ); #endif if (wait_more > 0) { SLEEP_MS(wait_more); } else if (wait_more < -100) { /* If we run over by 10 mSec or so, it's nothing to worry about. */ /* However, if PTT is still on about 1/10 sec after audio */ /* should be done, something is wrong. */ /* Looks like a bug with the RPi audio system. Never an issue with Ubuntu. */ /* This runs over randomly sometimes. TODO: investigate more fully sometime. */ #ifndef __arm__ text_color_set(DW_COLOR_ERROR); dw_printf ("Transmit timing error: PTT is on %d mSec too long.\n", -wait_more); #endif } /* * Turn off transmitter. */ #if DEBUG text_color_set(DW_COLOR_DEBUG); time_now = dtime_now(); dw_printf ("xmit_thread: Turn off PTT now. Actual time on was %d mS, vs. %d desired\n", (int) ((time_now - time_ptt) * 1000.), duration); #endif ptt_set (OCTYPE_PTT, c, 0); } /* end xmit_ax25_frames */
static int wait_for_clear_channel (int channel, int nowait, int slottime, int persist) { int r; int n; n = 0; start_over_again: while (hdlc_rec_data_detect_any(channel)) { SLEEP_MS(WAIT_CHECK_EVERY_MS); n++; if (n > (WAIT_TIMEOUT_MS / WAIT_CHECK_EVERY_MS)) { return 0; } } //TODO1.2: rethink dwait. /* * Added in version 1.2 - for transceivers that can't * turn around fast enough when using squelch and VOX. */ if (save_audio_config_p->achan[channel].dwait > 0) { SLEEP_MS (save_audio_config_p->achan[channel].dwait * 10); } if (hdlc_rec_data_detect_any(channel)) { goto start_over_again; } if ( ! nowait) { while (1) { SLEEP_MS (slottime * 10); if (hdlc_rec_data_detect_any(channel)) { goto start_over_again; } r = rand() & 0xff; if (r <= persist) { break; } } } // TODO1.2 while ( ! dw_mutex_try_lock(&(audio_out_dev_mutex[ACHAN2ADEV(channel)]))) { SLEEP_MS(WAIT_CHECK_EVERY_MS); n++; if (n > (WAIT_TIMEOUT_MS / WAIT_CHECK_EVERY_MS)) { return 0; } } return 1; } /* end wait_for_clear_channel */
static void * xmit_thread (void *arg) #endif { int c = (int)(long)arg; // channel number. packet_t pp; int p; int ok; /* * These are for timing of a transmission. * All are in usual unix time (seconds since 1/1/1970) but higher resolution */ while (1) { tq_wait_while_empty (c); #if DEBUG text_color_set(DW_COLOR_DEBUG); dw_printf ("xmit_thread, channel %d: woke up\n", c); #endif for (p=0; p<TQ_NUM_PRIO; p++) { pp = tq_remove (c, p); #if DEBUG text_color_set(DW_COLOR_DEBUG); dw_printf ("xmit_thread: tq_remove(chan=%d, prio=%d) returned %p\n", c, p, pp); #endif if (pp != NULL) { /* * Wait for the channel to be clear. * For the high priority queue, begin transmitting immediately. * For the low priority queue, wait a random amount of time, in hopes * of minimizing collisions. */ ok = wait_for_clear_channel (c, (p==TQ_PRIO_0_HI), xmit_slottime[c], xmit_persist[c]); if (ok) { /* * Channel is clear and we have lock on output device. * * If destination is "SPEECH" send info part to speech synthesizer. * If destination is "MORSE" send as morse code. */ char dest[AX25_MAX_ADDR_LEN]; int ssid = 0; if (ax25_is_aprs (pp)) { ax25_get_addr_no_ssid(pp, AX25_DESTINATION, dest); ssid = ax25_get_ssid(pp, AX25_DESTINATION); } else { strlcpy (dest, "", sizeof(dest)); } if (strcmp(dest, "SPEECH") == 0) { xmit_speech (c, pp); } else if (strcmp(dest, "MORSE") == 0) { int wpm = ssid * 2; if (wpm == 0) wpm = MORSE_DEFAULT_WPM; // This is a bit of a hack so we don't respond too quickly for APRStt. // It will be sent in high priority queue while a beacon wouldn't. // Add a little delay so user has time release PTT after sending #. // This and default txdelay would give us a second. if (p == TQ_PRIO_0_HI) { //text_color_set(DW_COLOR_DEBUG); //dw_printf ("APRStt morse xmit delay hack...\n"); SLEEP_MS (700); } xmit_morse (c, pp, wpm); } else { xmit_ax25_frames (c, p, pp); } dw_mutex_unlock (&(audio_out_dev_mutex[ACHAN2ADEV(c)])); } else { /* * Timeout waiting for clear channel. * Discard the packet. * Display with ERROR color rather than XMIT color. */ char stemp[1024]; /* max size needed? */ int info_len; unsigned char *pinfo; text_color_set(DW_COLOR_ERROR); dw_printf ("Waited too long for clear channel. Discarding packet below.\n"); ax25_format_addrs (pp, stemp); info_len = ax25_get_info (pp, &pinfo); text_color_set(DW_COLOR_INFO); dw_printf ("[%d%c] ", c, p==TQ_PRIO_0_HI ? 'H' : 'L'); dw_printf ("%s", stemp); /* stations followed by : */ ax25_safe_print ((char *)pinfo, info_len, ! ax25_is_aprs(pp)); dw_printf ("\n"); ax25_delete (pp); } /* wait for clear channel. */ } /* for high priority then low priority */ } } return 0; /* unreachable but quiet the warning. */ } /* end xmit_thread */