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.
|
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
|
||||||
|
|
|
@ -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.
|
||||||
|
|
||||||
|
if (strcasecmp(optarg, "EAS") == 0) {
|
||||||
|
modem.achan[0].baud = 0xEA5EA5; // See special case below.
|
||||||
|
}
|
||||||
|
else {
|
||||||
modem.achan[0].baud = atoi(optarg);
|
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,15 +791,24 @@ 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");
|
||||||
|
|
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.
|
// 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/*-------------------------------------------------------------------
|
/*-------------------------------------------------------------------
|
||||||
|
|
|
@ -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);
|
|
@ -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 */
|
|
@ -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 */
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue