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.
|
||||
|
||||
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,15 +791,24 @@ 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");
|
||||
}
|
||||
}
|
||||
|
||||
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