mirror of https://github.com/wb2osz/direwolf.git
				
				
				
			Add EAS to gen_packets.
This commit is contained in:
		
							parent
							
								
									4ac666df6a
								
							
						
					
					
						commit
						110b85a781
					
				|  | @ -46,6 +46,8 @@ Data rate in bits/sec for first channel.  Standard values are 300, 1200, 2400, 4 | |||
| 4800 bps uses 8PSK based on V.27 standard. | ||||
| .P | ||||
| 9600 bps and up uses K9NG/G3RUH standard. | ||||
| .P | ||||
| EAS for Emergency Alert System (EAS) Specific Area Message Encoding (SAME). | ||||
| .RE | ||||
| .RE | ||||
| .PD | ||||
|  |  | |||
|  | @ -1,7 +1,7 @@ | |||
| //
 | ||||
| //    This file is part of Dire Wolf, an amateur radio packet TNC.
 | ||||
| //
 | ||||
| //    Copyright (C) 2011, 2013, 2014, 2015, 2016, 2019, 2021  John Langner, WB2OSZ
 | ||||
| //    Copyright (C) 2011, 2013, 2014, 2015, 2016, 2019, 2021, 2023  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
 | ||||
|  | @ -118,13 +118,48 @@ static void send_packet (char *str) | |||
|     	packet_t pp; | ||||
|     	unsigned char fbuf[AX25_MAX_PACKET_LEN+2]; | ||||
|     	int flen; | ||||
| 	int c; | ||||
| 	int c = 0;	// channel number.
 | ||||
| 
 | ||||
| 	if (g_morse_wpm > 0) { | ||||
| 
 | ||||
| 	  // TODO: Why not use the destination field instead of command line option?
 | ||||
| 	  // Why not use the destination field instead of command line option?
 | ||||
| 	  // For one thing, this is not in TNC-2 monitor format.
 | ||||
| 
 | ||||
| 	  morse_send (0, str, g_morse_wpm, 100, 100); | ||||
| 	  morse_send (c, str, g_morse_wpm, 100, 100); | ||||
| 	} | ||||
| 	else if (modem.achan[0].modem_type == MODEM_EAS) { | ||||
| 
 | ||||
| // Generate EAS SAME signal FOR RESEARCH AND TESTING ONLY!!!
 | ||||
| // There could be legal consequences for sending unauhorized SAME
 | ||||
| // over the radio so don't do it!
 | ||||
| 
 | ||||
| 	  // I'm expecting to see TNC 2 monitor format.
 | ||||
| 	  // The source and destination are ignored.
 | ||||
| 	  // The optional destination SSID is the number of times to repeat.
 | ||||
| 	  // The user defined data type indicator can optionally be used
 | ||||
| 	  // for compatibility with how it is received and presented to client apps.
 | ||||
| 	  // Examples:
 | ||||
| 	  //	X>X-3:{DEZCZC-WXR-RWT-033019-033017-033015-033013-033011-025011-025017-033007-033005-033003-033001-025009-025027-033009+0015-1691525-KGYX/NWS-
 | ||||
| 	  //	X>X:NNNN
 | ||||
| 
 | ||||
| 	  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; | ||||
| 	  } | ||||
| 	  unsigned char *pinfo; | ||||
| 	  int info_len = ax25_get_info (pp, &pinfo); | ||||
| 	  if (info_len >= 3 && strncmp((char*)pinfo, "{DE", 3) == 0) { | ||||
| 	    pinfo += 3; | ||||
| 	    info_len -= 3; | ||||
| 	  } | ||||
| 
 | ||||
| 	  int repeat = ax25_get_ssid (pp, AX25_DESTINATION); | ||||
| 	  if (repeat == 0) repeat = 1; | ||||
| 
 | ||||
| 	  eas_send (c, pinfo, repeat, 500, 500); | ||||
| 	  ax25_delete (pp); | ||||
| 	} | ||||
| 	else { | ||||
| 	  pp = ax25_from_text (str, 1); | ||||
|  | @ -135,6 +170,9 @@ static void send_packet (char *str) | |||
| 	  } | ||||
| 	  flen = ax25_pack (pp, fbuf); | ||||
| 	  (void)flen; | ||||
| 
 | ||||
| 	  // If stereo, put same thing in each channel.
 | ||||
| 
 | ||||
| 	  for (c=0; c<modem.adev[0].num_channels; c++) | ||||
| 	  { | ||||
| 
 | ||||
|  | @ -282,23 +320,31 @@ int main(int argc, char **argv) | |||
| 
 | ||||
| 						// FIXME: options should not be order dependent.
 | ||||
| 
 | ||||
|               modem.achan[0].baud = atoi(optarg); | ||||
|               if (strcasecmp(optarg, "EAS") == 0) { | ||||
| 	        modem.achan[0].baud = 0xEA5EA5;	// See special case below.
 | ||||
| 	      } | ||||
| 	      else { | ||||
| 	        modem.achan[0].baud = atoi(optarg); | ||||
| 	      } | ||||
| 
 | ||||
|               text_color_set(DW_COLOR_INFO);  | ||||
|               dw_printf ("Data rate set to %d bits / second.\n", modem.achan[0].baud); | ||||
|               if (modem.achan[0].baud != 100 && (modem.achan[0].baud < MIN_BAUD || modem.achan[0].baud > MAX_BAUD)) { | ||||
|                 text_color_set(DW_COLOR_ERROR); | ||||
|                 dw_printf ("Use a more reasonable bit rate in range of %d - %d.\n", MIN_BAUD, MAX_BAUD); | ||||
|                 exit (EXIT_FAILURE); | ||||
|               } | ||||
| 
 | ||||
| 	      /* We have similar logic in direwolf.c, config.c, gen_packets.c, and atest.c, */ | ||||
| 	      /* that need to be kept in sync.  Maybe it could be a common function someday. */ | ||||
| 
 | ||||
| 	      if (modem.achan[0].baud == 100) { | ||||
| 	      if (modem.achan[0].baud == 100) {			// What was this for?
 | ||||
|                   modem.achan[0].modem_type = MODEM_AFSK; | ||||
|                   modem.achan[0].mark_freq = 1615; | ||||
|                   modem.achan[0].space_freq = 1785; | ||||
| 	      } | ||||
| 	      else if (modem.achan[0].baud == 0xEA5EA5) { | ||||
| 		  modem.achan[0].baud = 521;			// Fine tuned later. 520.83333
 | ||||
| 								// Proper fix is to make this float.
 | ||||
|                   modem.achan[0].modem_type = MODEM_EAS; | ||||
|                   modem.achan[0].mark_freq = 2083.3333;		// Ideally these should be floating point.
 | ||||
|                   modem.achan[0].space_freq = 1562.5000 ; | ||||
| 	      } | ||||
| 	      else if (modem.achan[0].baud < 600) { | ||||
|                   modem.achan[0].modem_type = MODEM_AFSK; | ||||
|                   modem.achan[0].mark_freq = 1600;		// Typical for HF SSB
 | ||||
|  | @ -334,6 +380,11 @@ int main(int argc, char **argv) | |||
|                   text_color_set(DW_COLOR_INFO);  | ||||
|                   dw_printf ("Using scrambled baseband signal rather than AFSK.\n"); | ||||
| 	      } | ||||
|               if (modem.achan[0].baud != 100 && (modem.achan[0].baud < MIN_BAUD || modem.achan[0].baud > MAX_BAUD)) { | ||||
|                 text_color_set(DW_COLOR_ERROR); | ||||
|                 dw_printf ("Use a more reasonable bit rate in range of %d - %d.\n", MIN_BAUD, MAX_BAUD); | ||||
|                 exit (EXIT_FAILURE); | ||||
|               } | ||||
|               break; | ||||
| 
 | ||||
|             case 'g':				/* -g for g3ruh scrambling */ | ||||
|  | @ -740,14 +791,23 @@ int main(int argc, char **argv) | |||
| 	} | ||||
| 	else { | ||||
| 
 | ||||
| 	  // This should send a total of 6.
 | ||||
| 	  // Note that sticking in the user defined type {DE is optional.
 | ||||
| 
 | ||||
| 	  if (modem.achan[0].modem_type == MODEM_EAS) { | ||||
| 	    send_packet ("X>X-3:{DEZCZC-WXR-RWT-033019-033017-033015-033013-033011-025011-025017-033007-033005-033003-033001-025009-025027-033009+0015-1691525-KGYX/NWS-"); | ||||
| 	    send_packet ("X>X-2:{DENNNN"); | ||||
| 	    send_packet ("X>X:NNNN"); | ||||
| 	  } | ||||
| 	  else { | ||||
| /*
 | ||||
|  * Builtin default 4 packets. | ||||
|  */ | ||||
| 
 | ||||
| 	  send_packet ("WB2OSZ-15>TEST:,The quick brown fox jumps over the lazy dog!  1 of 4"); | ||||
| 	  send_packet ("WB2OSZ-15>TEST:,The quick brown fox jumps over the lazy dog!  2 of 4"); | ||||
| 	  send_packet ("WB2OSZ-15>TEST:,The quick brown fox jumps over the lazy dog!  3 of 4"); | ||||
| 	  send_packet ("WB2OSZ-15>TEST:,The quick brown fox jumps over the lazy dog!  4 of 4"); | ||||
| 	    send_packet ("WB2OSZ-15>TEST:,The quick brown fox jumps over the lazy dog!  1 of 4"); | ||||
| 	    send_packet ("WB2OSZ-15>TEST:,The quick brown fox jumps over the lazy dog!  2 of 4"); | ||||
| 	    send_packet ("WB2OSZ-15>TEST:,The quick brown fox jumps over the lazy dog!  3 of 4"); | ||||
| 	    send_packet ("WB2OSZ-15>TEST:,The quick brown fox jumps over the lazy dog!  4 of 4"); | ||||
| 	  } | ||||
| 	} | ||||
| 
 | ||||
| 	audio_file_close(); | ||||
|  | @ -765,7 +825,7 @@ static void usage (char **argv) | |||
| 	dw_printf ("Options:\n"); | ||||
| 	dw_printf ("  -a <number>   Signal amplitude in range of 0 - 200%%.  Default 50.\n"); | ||||
| 	dw_printf ("  -b <number>   Bits / second for data.  Default is %d.\n", DEFAULT_BAUD); | ||||
| 	dw_printf ("  -B <number>   Bits / second for data.  Proper modem selected for 300, 1200, 2400, 4800, 9600.\n"); | ||||
| 	dw_printf ("  -B <number>   Bits / second for data.  Proper modem selected for 300, 1200, 2400, 4800, 9600, EAS.\n"); | ||||
| 	dw_printf ("  -g            Scrambled baseband rather than AFSK.\n"); | ||||
| 	dw_printf ("  -j            2400 bps QPSK compatible with direwolf <= 1.5.\n"); | ||||
| 	dw_printf ("  -J            2400 bps QPSK compatible with MFJ-2400.\n"); | ||||
|  | @ -788,6 +848,7 @@ static void usage (char **argv) | |||
| 	dw_printf ("the default built-in message. The format should correspond to\n"); | ||||
| 	dw_printf ("the standard packet monitoring representation such as,\n\n"); | ||||
| 	dw_printf ("    WB2OSZ-1>APDW12,WIDE2-2:!4237.14NS07120.83W#\n"); | ||||
| 	dw_printf ("User defined content can't be used with -n option.\n"); | ||||
| 	dw_printf ("\n"); | ||||
| 	dw_printf ("Example:  gen_packets -o x.wav \n"); | ||||
| 	dw_printf ("\n"); | ||||
|  |  | |||
							
								
								
									
										161
									
								
								src/gen_tone.c
								
								
								
								
							
							
						
						
									
										161
									
								
								src/gen_tone.c
								
								
								
								
							|  | @ -1,7 +1,7 @@ | |||
| //
 | ||||
| //    This file is part of Dire Wolf, an amateur radio packet TNC.
 | ||||
| //
 | ||||
| //    Copyright (C) 2011, 2014, 2015, 2016, 2019  John Langner, WB2OSZ
 | ||||
| //    Copyright (C) 2011, 2014, 2015, 2016, 2019, 2023  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
 | ||||
|  | @ -70,6 +70,7 @@ static int ticks_per_sample[MAX_CHANS];	/* Same for both channels of same soundc | |||
| static int ticks_per_bit[MAX_CHANS]; | ||||
| static int f1_change_per_sample[MAX_CHANS]; | ||||
| static int f2_change_per_sample[MAX_CHANS]; | ||||
| static float samples_per_symbol[MAX_CHANS]; | ||||
| 
 | ||||
| 
 | ||||
| static short sine_table[256]; | ||||
|  | @ -198,8 +199,11 @@ int gen_tone_init (struct audio_s *audio_config_p, int amp, int gen_packets) | |||
| 	        ticks_per_bit[chan] = (int) ((TICKS_PER_CYCLE / ((double)audio_config_p->achan[chan].baud * 0.5)) + 0.5); | ||||
| 	        f1_change_per_sample[chan] = (int) (((double)audio_config_p->achan[chan].mark_freq * TICKS_PER_CYCLE / (double)audio_config_p->adev[a].samples_per_sec ) + 0.5); | ||||
| 	        f2_change_per_sample[chan] = f1_change_per_sample[chan];	// Not used.
 | ||||
| 	        samples_per_symbol[chan] = 2. * (float)audio_config_p->adev[a].samples_per_sec / (float)audio_config_p->achan[chan].baud; | ||||
| 
 | ||||
| 	        tone_phase[chan] = PHASE_SHIFT_45;	// Just to mimic first attempt.
 | ||||
| 							// ??? Why?  We are only concerned with the difference
 | ||||
| 							// from one symbol to the next.
 | ||||
| 	        break; | ||||
| 
 | ||||
| 	      case MODEM_8PSK: | ||||
|  | @ -211,6 +215,7 @@ int gen_tone_init (struct audio_s *audio_config_p, int amp, int gen_packets) | |||
| 	        ticks_per_bit[chan] = (int) ((TICKS_PER_CYCLE / ((double)audio_config_p->achan[chan].baud / 3.)) + 0.5); | ||||
| 	        f1_change_per_sample[chan] = (int) (((double)audio_config_p->achan[chan].mark_freq * TICKS_PER_CYCLE / (double)audio_config_p->adev[a].samples_per_sec ) + 0.5); | ||||
| 	        f2_change_per_sample[chan] = f1_change_per_sample[chan];	// Not used.
 | ||||
| 	        samples_per_symbol[chan] = 3. * (float)audio_config_p->adev[a].samples_per_sec / (float)audio_config_p->achan[chan].baud; | ||||
| 	        break; | ||||
| 
 | ||||
| 	      case MODEM_BASEBAND: | ||||
|  | @ -220,11 +225,23 @@ int gen_tone_init (struct audio_s *audio_config_p, int amp, int gen_packets) | |||
| 		// Tone is half baud.
 | ||||
| 	        ticks_per_bit[chan] = (int) ((TICKS_PER_CYCLE / (double)audio_config_p->achan[chan].baud ) + 0.5); | ||||
| 	        f1_change_per_sample[chan] = (int) (((double)audio_config_p->achan[chan].baud * 0.5 * TICKS_PER_CYCLE / (double)audio_config_p->adev[a].samples_per_sec ) + 0.5); | ||||
| 	        samples_per_symbol[chan] = (float)audio_config_p->adev[a].samples_per_sec / (float)audio_config_p->achan[chan].baud; | ||||
| 	        break; | ||||
| 
 | ||||
| 	      case MODEM_EAS:		//  EAS.
 | ||||
| 
 | ||||
| 		// TODO: Proper fix would be to use float for baud, mark, space.
 | ||||
| 
 | ||||
| 	        ticks_per_bit[chan] = (int) ((TICKS_PER_CYCLE / 520.833333333333 ) + 0.5); | ||||
| 	        samples_per_symbol[chan] = (int)((audio_config_p->adev[a].samples_per_sec / 520.83333) + 0.5); | ||||
| 	        f1_change_per_sample[chan] = (int) ((2083.33333333333 * TICKS_PER_CYCLE / (double)audio_config_p->adev[a].samples_per_sec ) + 0.5); | ||||
| 	        f2_change_per_sample[chan] = (int) ((1562.5000000 * TICKS_PER_CYCLE / (double)audio_config_p->adev[a].samples_per_sec ) + 0.5); | ||||
| 	        break; | ||||
| 
 | ||||
| 	      default:		// AFSK
 | ||||
| 
 | ||||
| 	        ticks_per_bit[chan] = (int) ((TICKS_PER_CYCLE / (double)audio_config_p->achan[chan].baud ) + 0.5); | ||||
| 	        samples_per_symbol[chan] = (float)audio_config_p->adev[a].samples_per_sec / (float)audio_config_p->achan[chan].baud; | ||||
| 	        f1_change_per_sample[chan] = (int) (((double)audio_config_p->achan[chan].mark_freq * TICKS_PER_CYCLE / (double)audio_config_p->adev[a].samples_per_sec ) + 0.5); | ||||
| 	        f2_change_per_sample[chan] = (int) (((double)audio_config_p->achan[chan].space_freq * TICKS_PER_CYCLE / (double)audio_config_p->adev[a].samples_per_sec ) + 0.5); | ||||
| 	        break; | ||||
|  | @ -285,9 +302,64 @@ int gen_tone_init (struct audio_s *audio_config_p, int amp, int gen_packets) | |||
|  * | ||||
|  *--------------------------------------------------------------------*/ | ||||
| 
 | ||||
| // Interpolate between two values.
 | ||||
| // My original approximation simply jumped between phases, producing a discontinuity,
 | ||||
| // and increasing bandwidth.
 | ||||
| // According to multiple sources, we should transition more gently.
 | ||||
| // Below see see a rough approximation of:
 | ||||
| //  * A step function, immediately going to new value.
 | ||||
| //  * Linear interpoation.
 | ||||
| //  * Raised cosine.  Square root of cosine is also mentioned.
 | ||||
| //
 | ||||
| //	new	      -		    /		   --
 | ||||
| //		      |		   /		  /
 | ||||
| //		      |		  /		  |
 | ||||
| //		      |		 /		  /
 | ||||
| //	old	-------		/		--
 | ||||
| //		step		linear		raised cosine
 | ||||
| //
 | ||||
| // Inputs are the old (previous value), new value, and a blending control
 | ||||
| // 0 -> take old value
 | ||||
| // 1 -> take new value.
 | ||||
| // inbetween some sort of weighted average.
 | ||||
| 
 | ||||
| static inline float interpol8 (float oldv, float newv, float bc) | ||||
| { | ||||
| 	// Step function.
 | ||||
| 	//return (newv);				// 78 on 11/7
 | ||||
| 
 | ||||
| 	assert (bc >= 0); | ||||
| 	assert (bc <= 1.1); | ||||
| 
 | ||||
| 	if (bc < 0) return (oldv); | ||||
| 	if (bc > 1) return (newv); | ||||
| 
 | ||||
| 	// Linear interpolation, just for comparison.
 | ||||
| 	//return (bc * newv + (1.0f - bc) * oldv);	// 39 on 11/7
 | ||||
| 
 | ||||
| 	float rc = 0.5f * (cosf(bc * M_PI - M_PI) + 1.0f); | ||||
| 	float rrc = bc >= 0.5f | ||||
| 				? 0.5f * (sqrtf(fabsf(cosf(bc * M_PI - M_PI))) + 1.0f) | ||||
| 				: 0.5f * (-sqrtf(fabsf(cosf(bc * M_PI - M_PI))) + 1.0f); | ||||
| 
 | ||||
| 	(void)rrc; | ||||
| 	return (rc * newv + (1.0f - bc) * oldv);	// 49 on 11/7
 | ||||
| 	//return (rrc * newv + (1.0f - bc) * oldv);	// 55 on 11/7
 | ||||
| } | ||||
| 
 | ||||
| static const int gray2phase_v26[4] = {0, 1, 3, 2}; | ||||
| static const int gray2phase_v27[8] = {1, 0, 2, 3, 6, 7, 5, 4}; | ||||
| 
 | ||||
| // #define PSKIQ 1  // not ready for prime time yet.
 | ||||
| #if PSKIQ | ||||
| static int xmit_octant[MAX_CHANS];	// absolute phase in 45 degree units.
 | ||||
| static int xmit_prev_octant[MAX_CHANS];	// from previous symbol.
 | ||||
| 
 | ||||
| // For PSK, we generate the final signal by combining fixed frequency cosine and
 | ||||
| // sine by the following weights.
 | ||||
| static const float ci[8] = { 1,	.7071,	0,	-.7071,	-1,	-.7071,	0,	.7071	}; | ||||
| static const float sq[8] = { 0,	.7071,	1,	.7071,	0,	-.7071,	-1,	-.7071	}; | ||||
| #endif | ||||
| 
 | ||||
| void tone_gen_put_bit (int chan, int dat) | ||||
| { | ||||
|  | @ -324,14 +396,28 @@ void tone_gen_put_bit (int chan, int dat) | |||
| 
 | ||||
| 	  // All zero bits should give us steady 1800 Hz.
 | ||||
| 	  // All one bits should flip phase by 180 degrees each time.
 | ||||
| 	  // For V.26B, add another 45 degrees.
 | ||||
| 	  // This seems to work a little better.
 | ||||
| 
 | ||||
| 	  dibit = (save_bit[chan] << 1) | dat; | ||||
| 
 | ||||
| 	  symbol = gray2phase_v26[dibit]; | ||||
| 	  symbol = gray2phase_v26[dibit];	// 0 .. 3 for QPSK.
 | ||||
| #if PSKIQ | ||||
| 	  // One phase shift unit is 45 degrees.
 | ||||
| 	  // Remember what it was last time and calculate new.
 | ||||
| 	  // values 0 .. 7.
 | ||||
| 	  xmit_prev_octant[chan] = xmit_octant[chan]; | ||||
| 	  xmit_octant[chan] += symbol * 2; | ||||
| 	  if (save_audio_config_p->achan[chan].v26_alternative == V26_B) { | ||||
| 	    xmit_octant[chan] += 1; | ||||
| 	  } | ||||
| 	  xmit_octant[chan] &= 0x7; | ||||
| #else | ||||
| 	  tone_phase[chan] += symbol * PHASE_SHIFT_90; | ||||
| 	  if (save_audio_config_p->achan[chan].v26_alternative == V26_B) { | ||||
| 	    tone_phase[chan] += PHASE_SHIFT_45; | ||||
| 	  } | ||||
| #endif | ||||
| 	  bit_count[chan]++; | ||||
| 	} | ||||
| 
 | ||||
|  | @ -370,7 +456,9 @@ void tone_gen_put_bit (int chan, int dat) | |||
| 	  lfsr[chan] = (lfsr[chan] << 1) | (x & 1); | ||||
| 	  dat = x; | ||||
| 	} | ||||
| 	   | ||||
| #if PSKIQ | ||||
| 	int blend = 1; | ||||
| #endif | ||||
| 	do {		/* until enough audio samples for this symbol. */ | ||||
| 
 | ||||
| 	  int sam; | ||||
|  | @ -395,9 +483,58 @@ void tone_gen_put_bit (int chan, int dat) | |||
| 	      gen_tone_put_sample (chan, a, sam); | ||||
| 	      break; | ||||
| 
 | ||||
| 	    case MODEM_QPSK: | ||||
| 	    case MODEM_8PSK: | ||||
| 	    case MODEM_EAS: | ||||
| 
 | ||||
| 	      tone_phase[chan] += dat ? f1_change_per_sample[chan] : f2_change_per_sample[chan]; | ||||
|               sam = sine_table[(tone_phase[chan] >> 24) & 0xff]; | ||||
| 	      gen_tone_put_sample (chan, a, sam); | ||||
| 	      break; | ||||
| 
 | ||||
| 	    case MODEM_QPSK: | ||||
| 
 | ||||
| #if DEBUG2 | ||||
| 	      text_color_set(DW_COLOR_DEBUG); | ||||
| 	      dw_printf ("tone_gen_put_bit %d PSK\n", __LINE__); | ||||
| #endif | ||||
| 	      tone_phase[chan] += f1_change_per_sample[chan]; | ||||
| #if PSKIQ | ||||
| #if 1  // blend JWL
 | ||||
| 	      // remove loop invariant
 | ||||
| 	      float old_i = ci[xmit_prev_octant[chan]]; | ||||
| 	      float old_q = sq[xmit_prev_octant[chan]]; | ||||
| 
 | ||||
| 	      float new_i = ci[xmit_octant[chan]]; | ||||
| 	      float new_q = sq[xmit_octant[chan]]; | ||||
| 
 | ||||
| 	      float b = blend / samples_per_symbol[chan];	// roughly 0 to 1
 | ||||
| 	      blend++; | ||||
| 	     // b = (b - 0.5) * 20 + 0.5;
 | ||||
| 	     // if (b < 0) b = 0;
 | ||||
| 	     // if (b > 1) b = 1;
 | ||||
| 		// b = b > 0.5;
 | ||||
| 		//b = 1;		// 78 decoded with this.
 | ||||
| 					// only 39 without.
 | ||||
| 
 | ||||
| 
 | ||||
| 	      //float blended_i = new_i * b + old_i * (1.0f - b);
 | ||||
| 	      //float blended_q = new_q * b + old_q * (1.0f - b);
 | ||||
| 
 | ||||
| 	      float blended_i = interpol8 (old_i, new_i, b); | ||||
| 	      float blended_q = interpol8 (old_q, new_q, b); | ||||
| 
 | ||||
| 	      sam = blended_i * sine_table[((tone_phase[chan] - PHASE_SHIFT_90) >> 24) & 0xff] + | ||||
| 	            blended_q * sine_table[(tone_phase[chan] >> 24) & 0xff]; | ||||
| #else  // jump
 | ||||
| 	      sam = ci[xmit_octant[chan]] * sine_table[((tone_phase[chan] - PHASE_SHIFT_90) >> 24) & 0xff] + | ||||
| 	            sq[xmit_octant[chan]] * sine_table[(tone_phase[chan] >> 24) & 0xff]; | ||||
| #endif | ||||
| #else | ||||
|               sam = sine_table[(tone_phase[chan] >> 24) & 0xff]; | ||||
| #endif | ||||
| 	      gen_tone_put_sample (chan, a, sam); | ||||
| 	      break; | ||||
| 
 | ||||
| 	    case MODEM_8PSK: | ||||
| #if DEBUG2 | ||||
| 	      text_color_set(DW_COLOR_DEBUG); | ||||
| 	      dw_printf ("tone_gen_put_bit %d PSK\n", __LINE__); | ||||
|  | @ -521,6 +658,20 @@ void gen_tone_put_sample (int chan, int a, int sam) { | |||
| 	} | ||||
| } | ||||
| 
 | ||||
| void gen_tone_put_quiet_ms (int chan, int time_ms) { | ||||
| 
 | ||||
| 	int a = ACHAN2ADEV(chan);	/* device for channel. */ | ||||
| 	int sam = 0; | ||||
| 
 | ||||
| 	int nsamples = (int) ((time_ms * (float)save_audio_config_p->adev[a].samples_per_sec / 1000.) + 0.5); | ||||
| 
 | ||||
| 	for (int j=0; j<nsamples; j++)  { | ||||
| 	  gen_tone_put_sample (chan, a, sam); | ||||
|         }; | ||||
| 
 | ||||
| 	// Avoid abrupt change when it starts up again.
 | ||||
| 	tone_phase[chan] = 0; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| /*-------------------------------------------------------------------
 | ||||
|  |  | |||
|  | @ -15,3 +15,5 @@ int gen_tone_init (struct audio_s *pp, int amp, int gen_packets); | |||
| void tone_gen_put_bit (int chan, int dat); | ||||
| 
 | ||||
| void gen_tone_put_sample (int chan, int a, int sam); | ||||
| 
 | ||||
| void gen_tone_put_quiet_ms (int chan, int time_ms); | ||||
|  | @ -295,4 +295,82 @@ static void send_bit_nrzi (int chan, int b) | |||
| 	number_of_bits_sent[chan]++; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| //  The rest of this is for EAS SAME.
 | ||||
| //  This is sort of a logical place because it serializes a frame, but not in HDLC.
 | ||||
| //  We have a parallel where SAME deserialization is in hdlc_rec.
 | ||||
| //  Maybe both should be pulled out and moved to a same.c.
 | ||||
| 
 | ||||
| 
 | ||||
| /*-------------------------------------------------------------------
 | ||||
|  * | ||||
|  * Name:        eas_send | ||||
|  * | ||||
|  * Purpose:    	Serialize EAS SAME for transmission. | ||||
|  * | ||||
|  * Inputs:	chan	- Radio channel number. | ||||
|  *		str	- Character string to send. | ||||
|  *		repeat	- Number of times to repeat with 1 sec quiet between. | ||||
|  *		txdelay	- Delay (ms) from PTT to first preamble bit. | ||||
|  *		txtail	- Delay (ms) from last data bit to PTT off.	 | ||||
|  *		 | ||||
|  * | ||||
|  * Returns:	Total number of milliseconds to activate PTT. | ||||
|  *		This includes delays before the first character | ||||
|  *		and after the last to avoid chopping off part of it. | ||||
|  * | ||||
|  * Description:	xmit_thread calls this instead of the usual hdlc_send | ||||
|  *		when we have a special packet that means send EAS SAME | ||||
|  *		code. | ||||
|  * | ||||
|  *--------------------------------------------------------------------*/ | ||||
| 
 | ||||
| static inline void eas_put_byte (int chan, unsigned char b) | ||||
| { | ||||
| 	for (int n=0; n<8; n++) { | ||||
| 	  tone_gen_put_bit (chan, (b & 1)); | ||||
| 	  b >>= 1; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| int eas_send (int chan, unsigned char *str, int repeat, int txdelay, int txtail) | ||||
| { | ||||
| 	int bytes_sent = 0; | ||||
| 	const int gap = 1000; | ||||
| 	int gaps_sent = 0; | ||||
| 
 | ||||
| 	gen_tone_put_quiet_ms (chan, txdelay); | ||||
| 
 | ||||
| 	for (int r=0; r<repeat; r++ ) { | ||||
| 	  for (int j=0; j<16; j++) { | ||||
| 	    eas_put_byte (chan, 0xAB); | ||||
| 	    bytes_sent++; | ||||
| 	  } | ||||
| 
 | ||||
| 	  for (unsigned char *p = str; *p != '\0'; p++) { | ||||
| 	    eas_put_byte (chan, *p); | ||||
| 	    bytes_sent++; | ||||
| 	  } | ||||
| 
 | ||||
| 	  if (r < repeat-1) { | ||||
| 	    gen_tone_put_quiet_ms (chan, gap); | ||||
| 	    gaps_sent++; | ||||
| 	  } | ||||
| 	} | ||||
| 
 | ||||
| 	gen_tone_put_quiet_ms (chan, txtail); | ||||
| 
 | ||||
| 	audio_flush(ACHAN2ADEV(chan)); | ||||
| 
 | ||||
| 	int elapsed = txdelay + (int) (bytes_sent * 8 * 1.92) + (gaps_sent * gap) + txtail; | ||||
| 
 | ||||
| // dw_printf ("DEBUG:  EAS total time = %d ms\n", elapsed);
 | ||||
| 
 | ||||
| 	return (elapsed); | ||||
| 
 | ||||
| }  /* end eas_send */ | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| /* end hdlc_send.c */ | ||||
|  | @ -3,7 +3,10 @@ | |||
| 
 | ||||
| // In version 1.7 an extra layer of abstraction was added here.
 | ||||
| // Rather than calling hdlc_send_frame, we now use another function
 | ||||
| // which sends AX.25, FX.25, or IL2P depending on
 | ||||
| // which sends AX.25, FX.25, or IL2P depending on mode.
 | ||||
| 
 | ||||
| // eas_send fits here logically because it also serializes a packet.
 | ||||
| 
 | ||||
| 
 | ||||
| #include "ax25_pad.h" | ||||
| #include "audio.h" | ||||
|  | @ -12,6 +15,8 @@ int layer2_send_frame (int chan, packet_t pp, int bad_fcs, struct audio_s *audio | |||
| 
 | ||||
| int layer2_preamble_postamble (int chan, int flags, int finish, struct audio_s *audio_config_p); | ||||
| 
 | ||||
| int eas_send (int chan, unsigned char *str, int repeat, int txdelay, int txtail); | ||||
| 
 | ||||
| /* end hdlc_send.h */ | ||||
| 
 | ||||
| 
 | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue