Пример #1
0
static void xmit_speech (int c, packet_t pp)
{


	int info_len;
	unsigned char *pinfo;

/*
 * Print spoken packet.  Prefix by channel.
 */

	info_len = ax25_get_info (pp, &pinfo);
	text_color_set(DW_COLOR_XMIT);
	dw_printf ("[%d.speech] \"%s\"\n", c, pinfo);


	if (strlen(save_audio_config_p->tts_script) == 0) {
          text_color_set(DW_COLOR_ERROR);
          dw_printf ("Text-to-speech script has not been configured.\n");
	  ax25_delete (pp);
	  return;
	}

/* 
 * Turn on transmitter.
 */
	ptt_set (OCTYPE_PTT, c, 1);

/*
 * Invoke the speech-to-text script.
 */	

	xmit_speak_it (save_audio_config_p->tts_script, c, (char*)pinfo);

/*
 * Turn off transmitter.
 */
		
	ptt_set (OCTYPE_PTT, c, 0);
	ax25_delete (pp);

} /* end xmit_speech */
Пример #2
0
static void send_packet (char *str)
{
    packet_t pp;
    unsigned char fbuf[AX25_MAX_PACKET_LEN+2];
    int flen;
    int c;

    pp = ax25_from_text (str, 1);
    flen = ax25_pack (pp, fbuf);
    for (c=0; c<modem.adev[0].num_channels; c++)
    {
        hdlc_send_flags (c, 8, 0);
        hdlc_send_frame (c, fbuf, flen);
        hdlc_send_flags (c, 2, 1);
    }
    ax25_delete (pp);
}
Пример #3
0
static void xmit_morse (int c, packet_t pp, int wpm)
{


	int info_len;
	unsigned char *pinfo;


	info_len = ax25_get_info (pp, &pinfo);
	text_color_set(DW_COLOR_XMIT);
	dw_printf ("[%d.morse] \"%s\"\n", c, pinfo);

	ptt_set (OCTYPE_PTT, c, 1);

	morse_send (c, (char*)pinfo, wpm, xmit_txdelay[c] * 10, xmit_txtail[c] * 10);

	ptt_set (OCTYPE_PTT, c, 0);
	ax25_delete (pp);

} /* end xmit_morse */
Пример #4
0
static void pftest (int test_num, char *filter, char *monitor, int expected)
{
	int result;
	packet_t pp;

	text_color_set (DW_COLOR_DEBUG);
	dw_printf ("test number %d\n", test_num);
	
	pp = ax25_from_text (monitor, 1);
	assert (pp != NULL);

	result = pfilter (0, 0, filter, pp);
	if (result != expected) {
	  text_color_set (DW_COLOR_ERROR);
	  dw_printf ("Unexpected result for test number %d\n", test_num);
	  error_count++;
	}

	ax25_delete (pp);
}
Пример #5
0
static void test (char *in, char *out)
{
	packet_t pp, result;
	//int should_repeat;
	char rec[256];
	char xmit[256];
	unsigned char *pinfo;
	int info_len;
	unsigned char frame[AX25_MAX_PACKET_LEN];
	int frame_len;
	alevel_t alevel;

	dw_printf ("\n");

/*
 * As an extra test, change text to internal format back to 
 * text again to make sure it comes out the same.
 */
	pp = ax25_from_text (in, 1);
	assert (pp != NULL);

	ax25_format_addrs (pp, rec);
	info_len = ax25_get_info (pp, &pinfo);
	strlcat (rec, (char*)pinfo, sizeof(rec));

	if (strcmp(in, rec) != 0) {
	  text_color_set(DW_COLOR_ERROR);
	  dw_printf ("Text/internal/text error-1 %s -> %s\n", in, rec);
	}

/*
 * Just for more fun, write as the frame format, read it back
 * again, and make sure it is still the same.
 */

	frame_len = ax25_pack (pp, frame);
	ax25_delete (pp);

	alevel.rec = 50;
	alevel.mark = 50;
	alevel.space = 50;

	pp = ax25_from_frame (frame, frame_len, alevel);
	assert (pp != NULL);
	ax25_format_addrs (pp, rec);
	info_len = ax25_get_info (pp, &pinfo);
	strlcat (rec, (char*)pinfo, sizeof(rec));

	if (strcmp(in, rec) != 0) {
	  text_color_set(DW_COLOR_ERROR);
	  dw_printf ("internal/frame/internal/text error-2 %s -> %s\n", in, rec);
	}

/*
 * On with the digipeater test.
 */	
	
	text_color_set(DW_COLOR_REC);
	dw_printf ("Rec\t%s\n", rec);

//TODO:											Add filtering to test.
//											V
	result = digipeat_match (0, pp, mycall, mycall, &alias_re, &wide_re, 0, preempt, NULL);
	
	if (result != NULL) {

	  dedupe_remember (result, 0);
	  ax25_format_addrs (result, xmit);
	  info_len = ax25_get_info (result, &pinfo);
	  strlcat (xmit, (char*)pinfo, sizeof(xmit));
	  ax25_delete (result);
	}
	else {
	  strlcpy (xmit, "", sizeof(xmit));
	}

	text_color_set(DW_COLOR_XMIT);
	dw_printf ("Xmit\t%s\n", xmit);
	
	if (strcmp(xmit, out) == 0) {
	  text_color_set(DW_COLOR_INFO);
	  dw_printf ("OK\n");
	}
	else {
	  text_color_set(DW_COLOR_ERROR);
	  dw_printf ("Expect\t%s\n", out);
 	  failed++;
	}

	dw_printf ("\n");
}
Пример #6
0
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 */
Пример #7
0
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 */
Пример #8
0
static void * beacon_thread (void *arg)
#endif
{
	int j;
	time_t earliest;
	time_t now;

/*
 * Information from GPS.
 */
	int fix = 0;			/* 0 = none, 2 = 2D, 3 = 3D */
	double my_lat = 0;		/* degrees */
	double my_lon = 0;
	float  my_course = 0;		/* degrees */
	float  my_speed_knots = 0;
	float  my_speed_mph = 0;
	float  my_alt = 0;		/* meters */

/*
 * SmartBeaconing state.
 */
	time_t sb_prev_time = 0;	/* Time of most recent transmission. */
	float sb_prev_course = 0;	/* Most recent course reported. */
	//float sb_prev_speed_mph;	/* Most recent speed reported. */
	int sb_every;			/* Calculated time between transmissions. */


#if DEBUG
	struct tm tm;
	char hms[20];

	now = time(NULL);
	localtime_r (&now, &tm);
	strftime (hms, sizeof(hms), "%H:%M:%S", &tm);
	text_color_set(DW_COLOR_DEBUG);
	dw_printf ("beacon_thread: started %s\n", hms);
#endif
	now = time(NULL);

	while (1) {

	  assert (g_misc_config_p->num_beacons >= 1);

/* 
 * Sleep until time for the earliest scheduled or
 * the soonest we could transmit due to corner pegging.
 */
	  
	  earliest = g_misc_config_p->beacon[0].next;
	  for (j=1; j<g_misc_config_p->num_beacons; j++) {
	    if (g_misc_config_p->beacon[j].btype == BEACON_IGNORE)
	      continue;
	    earliest = MIN(g_misc_config_p->beacon[j].next, earliest);
	  }

	  if (g_misc_config_p->sb_configured && g_using_gps) {
	    earliest = MIN(now + g_misc_config_p->sb_turn_time, earliest);
            earliest = MIN(now + g_misc_config_p->sb_fast_rate, earliest);
	  }

	  if (earliest > now) {
	    SLEEP_SEC (earliest - now);
	  }

/*
 * Woke up.  See what needs to be done.
 */
	  now = time(NULL);

#if DEBUG
	  localtime_r (&now, &tm);
	  strftime (hms, sizeof(hms), "%H:%M:%S", &tm);
	  text_color_set(DW_COLOR_DEBUG);
	  dw_printf ("beacon_thread: woke up %s\n", hms);
#endif

/*
 * Get information from GPS if being used.
 * This needs to be done before the next scheduled tracker
 * beacon because corner pegging make it sooner. 
 */

#if DEBUG_SIM
	  FILE *fp;
	  char cs[40];

	  fp = fopen ("c:\\cygwin\\tmp\\cs", "r");
	  if (fp != NULL) {
	    fscanf (fp, "%f %f", &my_course, &my_speed_knots);
	    fclose (fp);
	  }
	  else {
	    fprintf (stderr, "Can't read /tmp/cs.\n");
	  }
	  fix = 3;
	  my_speed_mph = KNOTS_TO_MPH * my_speed_knots;
	  my_lat = 42.99;
	  my_lon = 71.99;
	  my_alt = 100;
#else
	  if (g_using_gps) {

	    fix = dwgps_read (&my_lat, &my_lon, &my_speed_knots, &my_course, &my_alt);
	    my_speed_mph = KNOTS_TO_MPH * my_speed_knots;

	    /* Don't complain here for no fix. */
	    /* Possibly at the point where about to transmit. */
	  }
#endif

/*
 * Run SmartBeaconing calculation if configured and GPS data available.
 */
	  if (g_misc_config_p->sb_configured && g_using_gps && fix >= 2) {

	    if (my_speed_mph > g_misc_config_p->sb_fast_speed) {
		sb_every = g_misc_config_p->sb_fast_rate;
	    }
	    else if (my_speed_mph < g_misc_config_p->sb_slow_speed) {
	      sb_every = g_misc_config_p->sb_slow_rate;
	    }
	    else {
	      /* Can't divide by 0 assuming sb_slow_speed > 0. */
	      sb_every = ( g_misc_config_p->sb_fast_rate * g_misc_config_p->sb_fast_speed ) / my_speed_mph;
	    }

#if DEBUG_SIM
	  text_color_set(DW_COLOR_DEBUG);
	  dw_printf ("SB: fast %d %d slow %d %d speed=%.1f every=%d\n",
			g_misc_config_p->sb_fast_speed, g_misc_config_p->sb_fast_rate,
			g_misc_config_p->sb_slow_speed, g_misc_config_p->sb_slow_rate,
			my_speed_mph, sb_every);
#endif 
	
/*
 * Test for "Corner Pegging" if moving.
 */
	    if (my_speed_mph >= 1.0) {
	      int turn_threshold = g_misc_config_p->sb_turn_angle + 
			g_misc_config_p->sb_turn_slope / my_speed_mph;

#if DEBUG_SIM
	  text_color_set(DW_COLOR_DEBUG);
	  dw_printf ("SB-moving: course %.0f  prev %.0f  thresh %d\n",
		my_course, sb_prev_course, turn_threshold);
#endif 
	      if (heading_change(my_course, sb_prev_course) > turn_threshold &&
		  now >= sb_prev_time + g_misc_config_p->sb_turn_time) {

		/* Send it now. */
	        for (j=0; j<g_misc_config_p->num_beacons; j++) {
                  if (g_misc_config_p->beacon[j].btype == BEACON_TRACKER) {
		    g_misc_config_p->beacon[j].next = now;
	          }
	        }
	      }  /* significant change in direction */
	    }  /* is moving */
	  }  /* apply SmartBeaconing */
	    
      
	  for (j=0; j<g_misc_config_p->num_beacons; j++) {

	    if (g_misc_config_p->beacon[j].btype == BEACON_IGNORE)
	      continue;

	    if (g_misc_config_p->beacon[j].next <= now) {

	      int strict = 1;	/* Strict packet checking because they will go over air. */
	      char stemp[20];
	      char info[AX25_MAX_INFO_LEN];
	      char beacon_text[AX25_MAX_PACKET_LEN];
	      packet_t pp = NULL;
	      char mycall[AX25_MAX_ADDR_LEN];

/*
 * Obtain source call for the beacon.
 * This could potentially be different on different channels.
 * When sending to IGate server, use call from first radio channel.
 *
 * Check added in version 1.0a.  Previously used index of -1.
 */
	      strcpy (mycall, "NOCALL");

	      if (g_misc_config_p->beacon[j].chan == -1) {
		strcpy (mycall, g_digi_config_p->mycall[0]);
	      }
	      else {
		strcpy (mycall, g_digi_config_p->mycall[g_misc_config_p->beacon[j].chan]);
	      }

	      if (strlen(mycall) == 0 || strcmp(mycall, "NOCALL") == 0) {
	        text_color_set(DW_COLOR_ERROR);
	        dw_printf ("MYCALL not set for beacon in config file line %d.\n", g_misc_config_p->beacon[j].lineno);
		continue;
	      }

/* 
 * Prepare the monitor format header. 
 */

	      strcpy (beacon_text, mycall);
	      strcat (beacon_text, ">");
	      sprintf (stemp, "%s%1d%1d", APP_TOCALL, MAJOR_VERSION, MINOR_VERSION);
	      strcat (beacon_text, stemp);
	      if (g_misc_config_p->beacon[j].via) {
	        strcat (beacon_text, ",");
	        strcat (beacon_text, g_misc_config_p->beacon[j].via);
	      }
	      strcat (beacon_text, ":");

/* 
 * Add the info part depending on beacon type. 
 */
	      switch (g_misc_config_p->beacon[j].btype) {

		case BEACON_POSITION:

		  encode_position (g_misc_config_p->beacon[j].compress, g_misc_config_p->beacon[j].lat, g_misc_config_p->beacon[j].lon, 
			g_misc_config_p->beacon[j].symtab, g_misc_config_p->beacon[j].symbol, 
			g_misc_config_p->beacon[j].power, g_misc_config_p->beacon[j].height, g_misc_config_p->beacon[j].gain, g_misc_config_p->beacon[j].dir,
			0, 0, /* course, speed */	
			g_misc_config_p->beacon[j].freq, g_misc_config_p->beacon[j].tone, g_misc_config_p->beacon[j].offset,
			g_misc_config_p->beacon[j].comment,
			info);
		  strcat (beacon_text, info);
	          g_misc_config_p->beacon[j].next = now + g_misc_config_p->beacon[j].every;
		  break;

		case BEACON_OBJECT:

		  encode_object (g_misc_config_p->beacon[j].objname, g_misc_config_p->beacon[j].compress, 0, g_misc_config_p->beacon[j].lat, g_misc_config_p->beacon[j].lon, 
			g_misc_config_p->beacon[j].symtab, g_misc_config_p->beacon[j].symbol, 
			g_misc_config_p->beacon[j].power, g_misc_config_p->beacon[j].height, g_misc_config_p->beacon[j].gain, g_misc_config_p->beacon[j].dir,
			0, 0, /* course, speed */
			g_misc_config_p->beacon[j].freq, g_misc_config_p->beacon[j].tone, g_misc_config_p->beacon[j].offset, g_misc_config_p->beacon[j].comment,
			info);
		  strcat (beacon_text, info);
	          g_misc_config_p->beacon[j].next = now + g_misc_config_p->beacon[j].every;
		  break;

		case BEACON_TRACKER:

		  if (fix >= 2) {
		    int coarse;		/* APRS encoder wants 1 - 360.  */
					/* 0 means none or unknown. */

		    coarse = (int)roundf(my_course);
		    if (coarse == 0) {
		      coarse = 360;
		    }
		    encode_position (g_misc_config_p->beacon[j].compress, 
			my_lat, my_lon, 
			g_misc_config_p->beacon[j].symtab, g_misc_config_p->beacon[j].symbol, 
			g_misc_config_p->beacon[j].power, g_misc_config_p->beacon[j].height, g_misc_config_p->beacon[j].gain, g_misc_config_p->beacon[j].dir,
			coarse, (int)roundf(my_speed_knots),	
			g_misc_config_p->beacon[j].freq, g_misc_config_p->beacon[j].tone, g_misc_config_p->beacon[j].offset,
			g_misc_config_p->beacon[j].comment,
			info);
		    strcat (beacon_text, info);

		    /* Remember most recent tracker beacon. */

		    sb_prev_time = now;
		    sb_prev_course = my_course;
		    //sb_prev_speed_mph = my_speed_mph;

		    /* Calculate time for next transmission. */
	            if (g_misc_config_p->sb_configured) {
	              g_misc_config_p->beacon[j].next = now + sb_every;
	            }
	            else {
	              g_misc_config_p->beacon[j].next = now + g_misc_config_p->beacon[j].every;
	            }
	 	  }
	          else {
		    g_misc_config_p->beacon[j].next = now + 2;
	            continue;   /* No fix.  Try again in a couple seconds. */
		  }
		  break;

		case BEACON_CUSTOM:

		  if (g_misc_config_p->beacon[j].custom_info != NULL) {
	            strcat (beacon_text, g_misc_config_p->beacon[j].custom_info);
		  }
		  else {
		    text_color_set(DW_COLOR_ERROR);
	    	    dw_printf ("Internal error. custom_info is null. %s %d\n", __FILE__, __LINE__);
	            continue;
	          }
	          g_misc_config_p->beacon[j].next = now + g_misc_config_p->beacon[j].every;
		  break;

		case BEACON_IGNORE:		
	        default:
		  break;

	      } /* switch beacon type. */

/*
 * Parse monitor format into form for transmission.
 */	
	      pp = ax25_from_text (beacon_text, strict);

              if (pp != NULL) {

		/* Send to IGate server or radio. */

	        if (g_misc_config_p->beacon[j].chan == -1) {
#if 1
	  	  text_color_set(DW_COLOR_XMIT);
	  	  dw_printf ("[ig] %s\n", beacon_text);
#endif
		  igate_send_rec_packet (0, pp);
		  ax25_delete (pp);
	 	}
		else {
	          tq_append (g_misc_config_p->beacon[j].chan, TQ_PRIO_1_LO, pp);
		}
	      }
	      else {
	        text_color_set(DW_COLOR_ERROR);
	        dw_printf ("Config file: Failed to parse packet constructed from line %d.\n", g_misc_config_p->beacon[j].lineno);
	        dw_printf ("%s\n", beacon_text);
	      }

	    }  /* if time to send it */

	  }  /* for each configured beacon */

	}  /* do forever */

} /* end beacon_thread */