Add EAS to gen_packets.

This commit is contained in:
wb2osz 2023-05-01 02:41:05 +01:00
parent 4ac666df6a
commit 110b85a781
6 changed files with 324 additions and 25 deletions

View File

@ -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. 4800 bps uses 8PSK based on V.27 standard.
.P .P
9600 bps and up uses K9NG/G3RUH standard. 9600 bps and up uses K9NG/G3RUH standard.
.P
EAS for Emergency Alert System (EAS) Specific Area Message Encoding (SAME).
.RE .RE
.RE .RE
.PD .PD

View File

@ -1,7 +1,7 @@
// //
// This file is part of Dire Wolf, an amateur radio packet TNC. // 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 // 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 // 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; packet_t pp;
unsigned char fbuf[AX25_MAX_PACKET_LEN+2]; unsigned char fbuf[AX25_MAX_PACKET_LEN+2];
int flen; int flen;
int c; int c = 0; // channel number.
if (g_morse_wpm > 0) { 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 { else {
pp = ax25_from_text (str, 1); pp = ax25_from_text (str, 1);
@ -135,6 +170,9 @@ static void send_packet (char *str)
} }
flen = ax25_pack (pp, fbuf); flen = ax25_pack (pp, fbuf);
(void)flen; (void)flen;
// If stereo, put same thing in each channel.
for (c=0; c<modem.adev[0].num_channels; c++) 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. // 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); text_color_set(DW_COLOR_INFO);
dw_printf ("Data rate set to %d bits / second.\n", modem.achan[0].baud); 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, */ /* 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. */ /* 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].modem_type = MODEM_AFSK;
modem.achan[0].mark_freq = 1615; modem.achan[0].mark_freq = 1615;
modem.achan[0].space_freq = 1785; 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) { else if (modem.achan[0].baud < 600) {
modem.achan[0].modem_type = MODEM_AFSK; modem.achan[0].modem_type = MODEM_AFSK;
modem.achan[0].mark_freq = 1600; // Typical for HF SSB 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); text_color_set(DW_COLOR_INFO);
dw_printf ("Using scrambled baseband signal rather than AFSK.\n"); 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; break;
case 'g': /* -g for g3ruh scrambling */ case 'g': /* -g for g3ruh scrambling */
@ -740,14 +791,23 @@ int main(int argc, char **argv)
} }
else { 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. * 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! 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! 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! 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! 4 of 4"); }
} }
audio_file_close(); audio_file_close();
@ -765,7 +825,7 @@ static void usage (char **argv)
dw_printf ("Options:\n"); dw_printf ("Options:\n");
dw_printf (" -a <number> Signal amplitude in range of 0 - 200%%. Default 50.\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. 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 (" -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 direwolf <= 1.5.\n");
dw_printf (" -J 2400 bps QPSK compatible with MFJ-2400.\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 default built-in message. The format should correspond to\n");
dw_printf ("the standard packet monitoring representation such as,\n\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 (" 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 ("\n");
dw_printf ("Example: gen_packets -o x.wav \n"); dw_printf ("Example: gen_packets -o x.wav \n");
dw_printf ("\n"); dw_printf ("\n");

View File

@ -1,7 +1,7 @@
// //
// This file is part of Dire Wolf, an amateur radio packet TNC. // 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 // 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 // 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 ticks_per_bit[MAX_CHANS];
static int f1_change_per_sample[MAX_CHANS]; static int f1_change_per_sample[MAX_CHANS];
static int f2_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]; 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); 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); 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. 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. 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; break;
case MODEM_8PSK: 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); 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); 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. 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; break;
case MODEM_BASEBAND: 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. // Tone is half baud.
ticks_per_bit[chan] = (int) ((TICKS_PER_CYCLE / (double)audio_config_p->achan[chan].baud ) + 0.5); 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); 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; break;
default: // AFSK default: // AFSK
ticks_per_bit[chan] = (int) ((TICKS_PER_CYCLE / (double)audio_config_p->achan[chan].baud ) + 0.5); 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); 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); 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; 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_v26[4] = {0, 1, 3, 2};
static const int gray2phase_v27[8] = {1, 0, 2, 3, 6, 7, 5, 4}; 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) 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 zero bits should give us steady 1800 Hz.
// All one bits should flip phase by 180 degrees each time. // 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; 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; tone_phase[chan] += symbol * PHASE_SHIFT_90;
if (save_audio_config_p->achan[chan].v26_alternative == V26_B) { if (save_audio_config_p->achan[chan].v26_alternative == V26_B) {
tone_phase[chan] += PHASE_SHIFT_45; tone_phase[chan] += PHASE_SHIFT_45;
} }
#endif
bit_count[chan]++; bit_count[chan]++;
} }
@ -370,7 +456,9 @@ void tone_gen_put_bit (int chan, int dat)
lfsr[chan] = (lfsr[chan] << 1) | (x & 1); lfsr[chan] = (lfsr[chan] << 1) | (x & 1);
dat = x; dat = x;
} }
#if PSKIQ
int blend = 1;
#endif
do { /* until enough audio samples for this symbol. */ do { /* until enough audio samples for this symbol. */
int sam; int sam;
@ -395,9 +483,58 @@ void tone_gen_put_bit (int chan, int dat)
gen_tone_put_sample (chan, a, sam); gen_tone_put_sample (chan, a, sam);
break; break;
case MODEM_QPSK: case MODEM_EAS:
case MODEM_8PSK:
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 #if DEBUG2
text_color_set(DW_COLOR_DEBUG); text_color_set(DW_COLOR_DEBUG);
dw_printf ("tone_gen_put_bit %d PSK\n", __LINE__); 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;
}
/*------------------------------------------------------------------- /*-------------------------------------------------------------------

View File

@ -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 tone_gen_put_bit (int chan, int dat);
void gen_tone_put_sample (int chan, int a, int sam); void gen_tone_put_sample (int chan, int a, int sam);
void gen_tone_put_quiet_ms (int chan, int time_ms);

View File

@ -295,4 +295,82 @@ static void send_bit_nrzi (int chan, int b)
number_of_bits_sent[chan]++; 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 */ /* end hdlc_send.c */

View File

@ -3,7 +3,10 @@
// In version 1.7 an extra layer of abstraction was added here. // In version 1.7 an extra layer of abstraction was added here.
// Rather than calling hdlc_send_frame, we now use another function // 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 "ax25_pad.h"
#include "audio.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 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 */ /* end hdlc_send.h */