2023-09-12 21:38:15 +01:00
|
|
|
#pragma once
|
|
|
|
|
|
|
|
// Includes code from Dire Wolf Copyright (C) 2011, 2012, 2013, 2014, 2015 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
|
|
|
|
// the Free Software Foundation, either version 2 of the License, or
|
|
|
|
// (at your option) any later version.
|
|
|
|
//
|
|
|
|
// This program is distributed in the hope that it will be useful,
|
|
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
// GNU General Public License for more details.
|
|
|
|
//
|
|
|
|
// You should have received a copy of the GNU General Public License
|
|
|
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
//
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Dephi emulation functions
|
|
|
|
|
|
|
|
string * Strings(TStringList * Q, int Index);
|
|
|
|
void Clear(TStringList * Q);
|
|
|
|
int Count(TStringList * List);
|
|
|
|
|
|
|
|
string * newString();
|
|
|
|
string * copy(string * Source, int StartChar, int Count);
|
|
|
|
TStringList * newTStringList();
|
|
|
|
|
|
|
|
void freeString(string * Msg);
|
|
|
|
|
|
|
|
void initString(string * S);
|
|
|
|
void initTStringList(TStringList* T);
|
|
|
|
|
|
|
|
// Two delete() This is confusing!!
|
|
|
|
// Not really - one acts on String, other TStringList
|
|
|
|
|
|
|
|
void Delete(TStringList * Q, int Index);
|
|
|
|
void mydelete(string * Source, int StartChar, int Count);
|
|
|
|
|
|
|
|
void move(unsigned char * SourcePointer, unsigned char * DestinationPointer, int CopyCount);
|
|
|
|
void fmove(float * SourcePointer, float * DestinationPointer, int CopyCount);
|
|
|
|
|
|
|
|
void setlength(string * Msg, int Count); // Set string length
|
|
|
|
|
|
|
|
string * stringAdd(string * Msg, unsigned char * Chars, int Count); // Extend string
|
|
|
|
|
|
|
|
void Assign(TStringList * to, TStringList * from); // Duplicate from to to
|
|
|
|
|
|
|
|
string * duplicateString(string * in);
|
|
|
|
|
|
|
|
// This looks for a string in a stringlist. Returns inhex if found, otherwise -1
|
|
|
|
|
|
|
|
int my_indexof(TStringList * l, string * s);
|
|
|
|
|
|
|
|
int Add(TStringList * Q, string * Entry);
|
|
|
|
|
|
|
|
|
|
|
|
#define MAX_FILTER_SIZE 480 /* 401 is needed for profile A, 300 baud & 44100. Revisit someday. */
|
|
|
|
// Size comes out to 417 for 1200 bps with 48000 sample rate
|
|
|
|
// v1.7 - Was 404. Bump up to 480.
|
|
|
|
|
|
|
|
|
|
|
|
#define MAX_TOTAL_CHANS 16
|
|
|
|
#define MAX_ADEVS 3
|
|
|
|
#define AX25_MAX_ADDR_LEN 12
|
|
|
|
|
|
|
|
#include <stdint.h> // for uint64_t
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#define AX25_MAX_REPEATERS 8
|
|
|
|
#define AX25_MIN_ADDRS 2 /* Destination & Source. */
|
|
|
|
#define AX25_MAX_ADDRS 10 /* Destination, Source, 8 digipeaters. */
|
|
|
|
|
|
|
|
#define AX25_DESTINATION 0 /* Address positions in frame. */
|
|
|
|
#define AX25_SOURCE 1
|
|
|
|
#define AX25_REPEATER_1 2
|
|
|
|
#define AX25_REPEATER_2 3
|
|
|
|
#define AX25_REPEATER_3 4
|
|
|
|
#define AX25_REPEATER_4 5
|
|
|
|
#define AX25_REPEATER_5 6
|
|
|
|
#define AX25_REPEATER_6 7
|
|
|
|
#define AX25_REPEATER_7 8
|
|
|
|
#define AX25_REPEATER_8 9
|
|
|
|
|
|
|
|
#define AX25_MAX_ADDR_LEN 12 /* In theory, you would expect the maximum length */
|
|
|
|
/* to be 6 letters, dash, 2 digits, and nul for a */
|
|
|
|
/* total of 10. However, object labels can be 10 */
|
|
|
|
/* characters so throw in a couple extra bytes */
|
|
|
|
/* to be safe. */
|
|
|
|
|
|
|
|
#define AX25_MIN_INFO_LEN 0 /* Previously 1 when considering only APRS. */
|
|
|
|
|
|
|
|
#define AX25_MAX_INFO_LEN 2048 /* Maximum size for APRS. */
|
|
|
|
/* AX.25 starts out with 256 as the default max */
|
|
|
|
/* length but the end stations can negotiate */
|
|
|
|
/* something different. */
|
|
|
|
/* version 0.8: Change from 256 to 2028 to */
|
|
|
|
/* handle the larger paclen for Linux AX25. */
|
|
|
|
|
|
|
|
/* These don't include the 2 bytes for the */
|
|
|
|
/* HDLC frame FCS. */
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Previously, for APRS only.
|
|
|
|
* #define AX25_MIN_PACKET_LEN ( 2 * 7 + 2 + AX25_MIN_INFO_LEN)
|
|
|
|
* #define AX25_MAX_PACKET_LEN ( AX25_MAX_ADDRS * 7 + 2 + AX25_MAX_INFO_LEN)
|
|
|
|
*/
|
|
|
|
|
|
|
|
/* The more general case. */
|
|
|
|
/* An AX.25 frame can have a control byte and no protocol. */
|
|
|
|
|
|
|
|
#define AX25_MIN_PACKET_LEN ( 2 * 7 + 1 )
|
|
|
|
|
|
|
|
#define AX25_MAX_PACKET_LEN ( AX25_MAX_ADDRS * 7 + 2 + 3 + AX25_MAX_INFO_LEN)
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* packet_t is a pointer to a packet object.
|
|
|
|
*
|
|
|
|
* The actual implementation is not visible outside ax25_pad.c.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#define AX25_UI_FRAME 3 /* Control field value. */
|
|
|
|
|
|
|
|
#define AX25_PID_NO_LAYER_3 0xf0 /* protocol ID used for APRS */
|
|
|
|
#define AX25_PID_SEGMENTATION_FRAGMENT 0x08
|
|
|
|
#define AX25_PID_ESCAPE_CHARACTER 0xff
|
|
|
|
|
|
|
|
|
|
|
|
struct packet_s {
|
|
|
|
|
|
|
|
int magic1; /* for error checking. */
|
|
|
|
|
|
|
|
int seq; /* unique sequence number for debugging. */
|
|
|
|
|
|
|
|
double release_time; /* Time stamp in format returned by dtime_now(). */
|
|
|
|
/* When to release from the SATgate mode delay queue. */
|
|
|
|
|
|
|
|
#define MAGIC 0x41583235
|
|
|
|
|
|
|
|
struct packet_s *nextp; /* Pointer to next in queue. */
|
|
|
|
|
|
|
|
int num_addr; /* Number of addresses in frame. */
|
|
|
|
/* Range of AX25_MIN_ADDRS .. AX25_MAX_ADDRS for AX.25. */
|
|
|
|
/* It will be 0 if it doesn't look like AX.25. */
|
|
|
|
/* -1 is used temporarily at allocation to mean */
|
|
|
|
/* not determined yet. */
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* The 7th octet of each address contains:
|
|
|
|
*
|
|
|
|
* Bits: H R R SSID 0
|
|
|
|
*
|
|
|
|
* H for digipeaters set to 0 initially.
|
|
|
|
* Changed to 1 when position has been used.
|
|
|
|
*
|
|
|
|
* for source & destination it is called
|
|
|
|
* command/response. Normally both 1 for APRS.
|
|
|
|
* They should be opposites for connected mode.
|
|
|
|
*
|
|
|
|
* R R Reserved. Normally set to 1 1.
|
|
|
|
*
|
|
|
|
* SSID Substation ID. Range of 0 - 15.
|
|
|
|
*
|
|
|
|
* 0 Usually 0 but 1 for last address.
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
#define SSID_H_MASK 0x80
|
|
|
|
#define SSID_H_SHIFT 7
|
|
|
|
|
|
|
|
#define SSID_RR_MASK 0x60
|
|
|
|
#define SSID_RR_SHIFT 5
|
|
|
|
|
|
|
|
#define SSID_SSID_MASK 0x1e
|
|
|
|
#define SSID_SSID_SHIFT 1
|
|
|
|
|
|
|
|
#define SSID_LAST_MASK 0x01
|
|
|
|
|
|
|
|
|
|
|
|
int frame_len; /* Frame length without CRC. */
|
|
|
|
|
|
|
|
int modulo; /* I & S frames have sequence numbers of either 3 bits (modulo 8) */
|
|
|
|
/* or 7 bits (modulo 128). This is conveyed by either 1 or 2 */
|
|
|
|
/* control bytes. Unfortunately, we can't determine this by looking */
|
|
|
|
/* at an isolated frame. We need to know about the context. If we */
|
|
|
|
/* are part of the conversation, we would know. But if we are */
|
|
|
|
/* just listening to others, this would be more difficult to determine. */
|
|
|
|
|
|
|
|
/* For U frames: set to 0 - not applicable */
|
|
|
|
/* For I & S frames: 8 or 128 if known. 0 if unknown. */
|
|
|
|
|
|
|
|
unsigned char frame_data[AX25_MAX_PACKET_LEN + 1];
|
|
|
|
/* Raw frame contents, without the CRC. */
|
|
|
|
|
|
|
|
|
|
|
|
int magic2; /* Will get stomped on if above overflows. */
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
typedef struct packet_s *packet_t;
|
|
|
|
|
|
|
|
typedef enum cmdres_e { cr_00 = 2, cr_cmd = 1, cr_res = 0, cr_11 = 3 } cmdres_t;
|
|
|
|
|
|
|
|
|
|
|
|
extern packet_t ax25_new(void);
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* APRS always has one control octet of 0x03 but the more
|
|
|
|
* general AX.25 case is one or two control bytes depending on
|
|
|
|
* whether "modulo 128 operation" is in effect.
|
|
|
|
*/
|
|
|
|
|
|
|
|
//#define DEBUGX 1
|
|
|
|
|
|
|
|
static inline int ax25_get_control_offset(packet_t this_p)
|
|
|
|
{
|
|
|
|
return (this_p->num_addr * 7);
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline int ax25_get_num_control(packet_t this_p)
|
|
|
|
{
|
|
|
|
int c;
|
|
|
|
|
|
|
|
c = this_p->frame_data[ax25_get_control_offset(this_p)];
|
|
|
|
|
|
|
|
if ((c & 0x01) == 0) { /* I xxxx xxx0 */
|
|
|
|
#if DEBUGX
|
|
|
|
dw_printf("ax25_get_num_control, %02x is I frame, returns %d\n", c, (this_p->modulo == 128) ? 2 : 1);
|
|
|
|
#endif
|
|
|
|
return ((this_p->modulo == 128) ? 2 : 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((c & 0x03) == 1) { /* S xxxx xx01 */
|
|
|
|
#if DEBUGX
|
|
|
|
dw_printf("ax25_get_num_control, %02x is S frame, returns %d\n", c, (this_p->modulo == 128) ? 2 : 1);
|
|
|
|
#endif
|
|
|
|
return ((this_p->modulo == 128) ? 2 : 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
#if DEBUGX
|
|
|
|
dw_printf("ax25_get_num_control, %02x is U frame, always returns 1.\n", c);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
return (1); /* U xxxx xx11 */
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* APRS always has one protocol octet of 0xF0 meaning no level 3
|
|
|
|
* protocol but the more general case is 0, 1 or 2 protocol ID octets.
|
|
|
|
*/
|
|
|
|
|
|
|
|
static inline int ax25_get_pid_offset(packet_t this_p)
|
|
|
|
{
|
|
|
|
return (ax25_get_control_offset(this_p) + ax25_get_num_control(this_p));
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ax25_get_num_pid(packet_t this_p)
|
|
|
|
{
|
|
|
|
int c;
|
|
|
|
int pid;
|
|
|
|
|
|
|
|
c = this_p->frame_data[ax25_get_control_offset(this_p)];
|
|
|
|
|
|
|
|
if ((c & 0x01) == 0 || /* I xxxx xxx0 */
|
|
|
|
c == 0x03 || c == 0x13) { /* UI 000x 0011 */
|
|
|
|
|
|
|
|
pid = this_p->frame_data[ax25_get_pid_offset(this_p)];
|
|
|
|
#if DEBUGX
|
|
|
|
dw_printf("ax25_get_num_pid, %02x is I or UI frame, pid = %02x, returns %d\n", c, pid, (pid == AX25_PID_ESCAPE_CHARACTER) ? 2 : 1);
|
|
|
|
#endif
|
|
|
|
if (pid == AX25_PID_ESCAPE_CHARACTER) {
|
|
|
|
return (2); /* pid 1111 1111 means another follows. */
|
|
|
|
}
|
|
|
|
return (1);
|
|
|
|
}
|
|
|
|
#if DEBUGX
|
|
|
|
dw_printf("ax25_get_num_pid, %02x is neither I nor UI frame, returns 0\n", c);
|
|
|
|
#endif
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* AX.25 has info field for 5 frame types depending on the control field.
|
|
|
|
*
|
|
|
|
* xxxx xxx0 I
|
|
|
|
* 000x 0011 UI (which includes APRS)
|
|
|
|
* 101x 1111 XID
|
|
|
|
* 111x 0011 TEST
|
|
|
|
* 100x 0111 FRMR
|
|
|
|
*
|
|
|
|
* APRS always has an Information field with at least one octet for the Data Type Indicator.
|
|
|
|
*/
|
|
|
|
|
|
|
|
static inline int ax25_get_info_offset(packet_t this_p)
|
|
|
|
{
|
|
|
|
int offset = ax25_get_control_offset(this_p) + ax25_get_num_control(this_p) + ax25_get_num_pid(this_p);
|
|
|
|
#if DEBUGX
|
|
|
|
dw_printf("ax25_get_info_offset, returns %d\n", offset);
|
|
|
|
#endif
|
|
|
|
return (offset);
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline int ax25_get_num_info(packet_t this_p)
|
|
|
|
{
|
|
|
|
int len;
|
|
|
|
|
|
|
|
/* assuming AX.25 frame. */
|
|
|
|
|
|
|
|
len = this_p->frame_len - this_p->num_addr * 7 - ax25_get_num_control(this_p) - ax25_get_num_pid(this_p);
|
|
|
|
if (len < 0) {
|
|
|
|
len = 0; /* print error? */
|
|
|
|
}
|
|
|
|
|
|
|
|
return (len);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
typedef enum ax25_modulo_e { modulo_unknown = 0, modulo_8 = 8, modulo_128 = 128 } ax25_modulo_t;
|
|
|
|
|
|
|
|
typedef enum ax25_frame_type_e {
|
|
|
|
|
|
|
|
frame_type_I = 0, // Information
|
|
|
|
|
|
|
|
frame_type_S_RR, // Receive Ready - System Ready To Receive
|
|
|
|
frame_type_S_RNR, // Receive Not Ready - TNC Buffer Full
|
|
|
|
frame_type_S_REJ, // Reject Frame - Out of Sequence or Duplicate
|
|
|
|
frame_type_S_SREJ, // Selective Reject - Request single frame repeat
|
|
|
|
|
|
|
|
frame_type_U_SABME, // Set Async Balanced Mode, Extended
|
|
|
|
frame_type_U_SABM, // Set Async Balanced Mode
|
|
|
|
frame_type_U_DISC, // Disconnect
|
|
|
|
frame_type_U_DM, // Disconnect Mode
|
|
|
|
frame_type_U_UA, // Unnumbered Acknowledge
|
|
|
|
frame_type_U_FRMR, // Frame Reject
|
|
|
|
frame_type_U_UI, // Unnumbered Information
|
|
|
|
frame_type_U_XID, // Exchange Identification
|
|
|
|
frame_type_U_TEST, // Test
|
|
|
|
frame_type_U, // other Unnumbered, not used by AX.25.
|
|
|
|
|
|
|
|
frame_not_AX25 // Could not get control byte from frame.
|
|
|
|
// This must be last because value plus 1 is
|
|
|
|
// for the size of an array.
|
|
|
|
|
|
|
|
} ax25_frame_type_t;
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Originally this was a single number.
|
|
|
|
* Let's try something new in version 1.2.
|
|
|
|
* Also collect AGC values from the mark and space filters.
|
|
|
|
*/
|
|
|
|
|
|
|
|
typedef struct alevel_s {
|
|
|
|
|
|
|
|
int rec;
|
|
|
|
int mark;
|
|
|
|
int space;
|
|
|
|
//float ms_ratio; // TODO: take out after temporary investigation.
|
|
|
|
} alevel_t;
|
|
|
|
|
|
|
|
|
|
|
|
#ifndef AXTEST
|
|
|
|
// TODO: remove this?
|
2024-04-13 17:48:58 +01:00
|
|
|
#define AX25MEMDEBUG 0
|
2023-09-12 21:38:15 +01:00
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
#if AX25MEMDEBUG // to investigate a memory leak problem
|
|
|
|
|
|
|
|
|
|
|
|
extern void ax25memdebug_set(void);
|
|
|
|
extern int ax25memdebug_get(void);
|
|
|
|
extern int ax25memdebug_seq(packet_t this_p);
|
|
|
|
|
|
|
|
|
|
|
|
extern packet_t ax25_from_text_debug(char *monitor, int strict, char *src_file, int src_line);
|
|
|
|
#define ax25_from_text(m,s) ax25_from_text_debug(m,s,__FILE__,__LINE__)
|
|
|
|
|
|
|
|
extern packet_t ax25_from_frame_debug(unsigned char *data, int len, alevel_t alevel, char *src_file, int src_line);
|
|
|
|
#define ax25_from_frame(d,l,a) ax25_from_frame_debug(d,l,a,__FILE__,__LINE__);
|
|
|
|
|
|
|
|
extern packet_t ax25_dup_debug(packet_t copy_from, char *src_file, int src_line);
|
|
|
|
#define ax25_dup(p) ax25_dup_debug(p,__FILE__,__LINE__);
|
|
|
|
|
|
|
|
extern void ax25_delete_debug(packet_t pp, char *src_file, int src_line);
|
|
|
|
#define ax25_delete(p) ax25_delete_debug(p,__FILE__,__LINE__);
|
|
|
|
|
|
|
|
#else
|
|
|
|
|
|
|
|
extern packet_t ax25_from_text(char *monitor, int strict);
|
|
|
|
|
|
|
|
extern packet_t ax25_from_frame(unsigned char *data, int len, alevel_t alevel);
|
|
|
|
|
|
|
|
extern packet_t ax25_dup(packet_t copy_from);
|
|
|
|
|
|
|
|
extern void ax25_delete(packet_t pp);
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
extern int ax25_parse_addr(int position, char *in_addr, int strict, char *out_addr, int *out_ssid, int *out_heard);
|
|
|
|
extern int ax25_check_addresses(packet_t pp);
|
|
|
|
|
|
|
|
extern packet_t ax25_unwrap_third_party(packet_t from_pp);
|
|
|
|
|
|
|
|
extern void ax25_set_addr(packet_t pp, int, char *);
|
|
|
|
extern void ax25_insert_addr(packet_t this_p, int n, char *ad);
|
|
|
|
extern void ax25_remove_addr(packet_t this_p, int n);
|
|
|
|
|
|
|
|
extern int ax25_get_num_addr(packet_t pp);
|
|
|
|
extern int ax25_get_num_repeaters(packet_t this_p);
|
|
|
|
|
|
|
|
extern void ax25_get_addr_with_ssid(packet_t pp, int n, char *station);
|
|
|
|
extern void ax25_get_addr_no_ssid(packet_t pp, int n, char *station);
|
|
|
|
|
|
|
|
extern int ax25_get_ssid(packet_t pp, int n);
|
|
|
|
extern void ax25_set_ssid(packet_t this_p, int n, int ssid);
|
|
|
|
|
|
|
|
extern int ax25_get_h(packet_t pp, int n);
|
|
|
|
|
|
|
|
extern void ax25_set_h(packet_t pp, int n);
|
|
|
|
|
|
|
|
extern int ax25_get_heard(packet_t this_p);
|
|
|
|
|
|
|
|
extern int ax25_get_first_not_repeated(packet_t pp);
|
|
|
|
|
|
|
|
extern int ax25_get_rr(packet_t this_p, int n);
|
|
|
|
|
|
|
|
extern int ax25_get_info(packet_t pp, unsigned char **paddr);
|
|
|
|
extern void ax25_set_info(packet_t pp, unsigned char *info_ptr, int info_len);
|
|
|
|
extern int ax25_cut_at_crlf(packet_t this_p);
|
|
|
|
|
|
|
|
extern void ax25_set_nextp(packet_t this_p, packet_t next_p);
|
|
|
|
|
|
|
|
extern int ax25_get_dti(packet_t this_p);
|
|
|
|
|
|
|
|
extern packet_t ax25_get_nextp(packet_t this_p);
|
|
|
|
|
|
|
|
extern void ax25_set_release_time(packet_t this_p, double release_time);
|
|
|
|
extern double ax25_get_release_time(packet_t this_p);
|
|
|
|
|
|
|
|
extern void ax25_set_modulo(packet_t this_p, int modulo);
|
|
|
|
extern int ax25_get_modulo(packet_t this_p);
|
|
|
|
|
|
|
|
extern void ax25_format_addrs(packet_t pp, char *);
|
|
|
|
extern void ax25_format_via_path(packet_t this_p, char *result, size_t result_size);
|
|
|
|
|
|
|
|
extern int ax25_pack(packet_t pp, unsigned char result[AX25_MAX_PACKET_LEN]);
|
|
|
|
|
|
|
|
extern ax25_frame_type_t ax25_frame_type(packet_t this_p, cmdres_t *cr, char *desc, int *pf, int *nr, int *ns);
|
|
|
|
|
|
|
|
extern void ax25_hex_dump(packet_t this_p);
|
|
|
|
|
|
|
|
extern int ax25_is_aprs(packet_t pp);
|
|
|
|
extern int ax25_is_null_frame(packet_t this_p);
|
|
|
|
|
|
|
|
extern int ax25_get_control(packet_t this_p);
|
|
|
|
extern int ax25_get_c2(packet_t this_p);
|
|
|
|
|
|
|
|
extern int ax25_get_pid(packet_t this_p);
|
|
|
|
|
|
|
|
extern int ax25_get_frame_len(packet_t this_p);
|
|
|
|
extern unsigned char *ax25_get_frame_data_ptr(packet_t this_p);
|
|
|
|
|
|
|
|
extern unsigned short ax25_dedupe_crc(packet_t pp);
|
|
|
|
|
|
|
|
extern unsigned short ax25_m_m_crc(packet_t pp);
|
|
|
|
|
|
|
|
extern void ax25_safe_print(char *, int, int ascii_only);
|
|
|
|
|
|
|
|
#define AX25_ALEVEL_TO_TEXT_SIZE 40 // overkill but safe.
|
|
|
|
extern int ax25_alevel_to_text(alevel_t alevel, char text[AX25_ALEVEL_TO_TEXT_SIZE]);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
int demod_init(struct audio_s *pa);
|
|
|
|
int demod_get_sample(int a);
|
|
|
|
void demod_process_sample(int chan, int subchan, int sam);
|
|
|
|
void demod_print_agc(int chan, int subchan);
|
|
|
|
alevel_t demod_get_audio_level(int chan, int subchan);
|
|
|
|
void hdlc_rec_bit(int chan, int subchan, int slice, int raw, int is_scrambled, int descram_state);
|
|
|
|
|
|
|
|
/* Provided elsewhere to process a complete frame. */
|
|
|
|
|
|
|
|
//void process_rec_frame (int chan, unsigned char *fbuf, int flen, int level);
|
|
|
|
|
|
|
|
|
|
|
|
/* Is HLDC decoder is currently gathering bits into a frame? */
|
|
|
|
/* Similar to, but not exactly the same as, data carrier detect. */
|
|
|
|
/* We use this to influence the PLL inertia. */
|
|
|
|
|
|
|
|
int hdlc_rec_gathering(int chan, int subchan, int slice);
|
|
|
|
|
|
|
|
/* Transmit needs to know when someone else is transmitting. */
|
|
|
|
|
|
|
|
void dcd_change(int chan, int subchan, int slice, int state);
|
|
|
|
|
|
|
|
int hdlc_rec_data_detect_any(int chan);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* direwolf.h - Common stuff used many places. */
|
|
|
|
|
|
|
|
// TODO: include this file first before anything else in each .c file.
|
|
|
|
|
|
|
|
|
|
|
|
#ifdef NDEBUG
|
|
|
|
#undef NDEBUG // Because it would disable assert().
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#define __restrict__
|
|
|
|
|
|
|
|
#define dw_printf Debugprintf
|
|
|
|
|
|
|
|
#define M_PI 3.1415926f
|
|
|
|
|
|
|
|
#define no_init_all deprecated
|
|
|
|
|
|
|
|
#ifndef DIREWOLF_H
|
|
|
|
#define DIREWOLF_H 1
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Support Windows XP and later.
|
|
|
|
*
|
|
|
|
* We need this before "#include <ws2tcpip.h>".
|
|
|
|
*
|
|
|
|
* Don't know what other impact it might have on others.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#ifdef WIN32
|
|
|
|
#define __WIN32__ 1
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#if __WIN32__
|
|
|
|
|
|
|
|
#ifdef _WIN32_WINNT
|
|
|
|
#error Include "direwolf.h" before any windows system files.
|
|
|
|
#endif
|
|
|
|
#ifdef WINVER
|
|
|
|
#error Include "direwolf.h" before any windows system files.
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#define _WIN32_WINNT 0x0501 /* Minimum OS version is XP. */
|
|
|
|
#define WINVER 0x0501 /* Minimum OS version is XP. */
|
|
|
|
|
|
|
|
#include <winsock2.h>
|
|
|
|
#include <windows.h>
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Maximum number of audio devices.
|
|
|
|
* Three is probably adequate for standard version.
|
|
|
|
* Larger reasonable numbers should also be fine.
|
|
|
|
*
|
|
|
|
* For example, if you wanted to use 4 audio devices at once, change this to 4.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#define MAX_ADEVS 3
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Maximum number of radio channels.
|
|
|
|
* Note that there could be gaps.
|
|
|
|
* Suppose audio device 0 was in mono mode and audio device 1 was stereo.
|
|
|
|
* The channels available would be:
|
|
|
|
*
|
|
|
|
* ADevice 0: channel 0
|
|
|
|
* ADevice 1: left = 2, right = 3
|
|
|
|
*
|
|
|
|
* TODO1.2: Look for any places that have
|
|
|
|
* for (ch=0; ch<MAX_CHANS; ch++) ...
|
|
|
|
* and make sure they handle undefined channels correctly.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#define MAX_RADIO_CHANS 4
|
|
|
|
|
|
|
|
#define MAX_CHANS 4 // TODO: Replace all former with latter to avoid confusion with following.
|
|
|
|
|
|
|
|
#define MAX_TOTAL_CHANS 16 // v1.7 allows additional virtual channels which are connected
|
|
|
|
// to something other than radio modems.
|
|
|
|
// Total maximum channels is based on the 4 bit KISS field.
|
|
|
|
// Someone with very unusual requirements could increase this and
|
|
|
|
// use only the AGW network protocol.
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Maximum number of rigs.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#ifdef USE_HAMLIB
|
|
|
|
#define MAX_RIGS MAX_CHANS
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Get audio device number for given channel.
|
|
|
|
* and first channel for given device.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#define ACHAN2ADEV(n) ((n)>>1)
|
|
|
|
#define ADEVFIRSTCHAN(n) ((n) * 2)
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Maximum number of modems per channel.
|
|
|
|
* I called them "subchannels" (in the code) because
|
|
|
|
* it is short and unambiguous.
|
|
|
|
* Nothing magic about the number. Could be larger
|
|
|
|
* but CPU demands might be overwhelming.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#define MAX_SUBCHANS 9
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Each one of these can have multiple slicers, at
|
|
|
|
* different levels, to compensate for different
|
|
|
|
* amplitudes of the AFSK tones.
|
|
|
|
* Initially used same number as subchannels but
|
|
|
|
* we could probably trim this down a little
|
|
|
|
* without impacting performance.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#define MAX_SLICERS 9
|
|
|
|
|
|
|
|
|
|
|
|
#if __WIN32__
|
|
|
|
#define SLEEP_SEC(n) Sleep((n)*1000)
|
|
|
|
#define SLEEP_MS(n) Sleep(n)
|
|
|
|
#else
|
|
|
|
#define SLEEP_SEC(n) sleep(n)
|
|
|
|
#define SLEEP_MS(n) usleep((n)*1000)
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#if __WIN32__
|
|
|
|
|
|
|
|
#define PTW32_STATIC_LIB
|
|
|
|
//#include "pthreads/pthread.h"
|
|
|
|
|
|
|
|
// This enables definitions of localtime_r and gmtime_r in system time.h.
|
|
|
|
//#define _POSIX_THREAD_SAFE_FUNCTIONS 1
|
|
|
|
#define _POSIX_C_SOURCE 1
|
|
|
|
|
|
|
|
#else
|
|
|
|
#include <pthread.h>
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
#ifdef __APPLE__
|
|
|
|
|
|
|
|
// https://groups.yahoo.com/neo/groups/direwolf_packet/conversations/messages/2072
|
|
|
|
|
|
|
|
// The original suggestion was to add this to only ptt.c.
|
|
|
|
// I thought it would make sense to put it here, so it will apply to all files,
|
|
|
|
// consistently, rather than only one file ptt.c.
|
|
|
|
|
|
|
|
// The placement of this is critical. Putting it earlier was a problem.
|
|
|
|
// https://github.com/wb2osz/direwolf/issues/113
|
|
|
|
|
|
|
|
// It needs to be after the include pthread.h because
|
|
|
|
// pthread.h pulls in <sys/cdefs.h>, which redefines __DARWIN_C_LEVEL back to ansi,
|
|
|
|
// which breaks things.
|
|
|
|
// Maybe it should just go in ptt.c as originally suggested.
|
|
|
|
|
|
|
|
// #define __DARWIN_C_LEVEL __DARWIN_C_FULL
|
|
|
|
|
|
|
|
// There is a more involved patch here:
|
|
|
|
// https://groups.yahoo.com/neo/groups/direwolf_packet/conversations/messages/2458
|
|
|
|
|
|
|
|
#ifndef _DARWIN_C_SOURCE
|
|
|
|
#define _DARWIN_C_SOURCE
|
|
|
|
#endif
|
|
|
|
|
|
|
|
// Defining _DARWIN_C_SOURCE ensures that the definition for the cfmakeraw function (or similar)
|
|
|
|
// are pulled in through the include file <sys/termios.h>.
|
|
|
|
|
|
|
|
#ifdef __DARWIN_C_LEVEL
|
|
|
|
#undef __DARWIN_C_LEVEL
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#define __DARWIN_C_LEVEL __DARWIN_C_FULL
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#define DW_METERS_TO_FEET(x) ((x) == G_UNKNOWN ? G_UNKNOWN : (x) * 3.2808399)
|
|
|
|
#define DW_FEET_TO_METERS(x) ((x) == G_UNKNOWN ? G_UNKNOWN : (x) * 0.3048)
|
|
|
|
#define DW_KM_TO_MILES(x) ((x) == G_UNKNOWN ? G_UNKNOWN : (x) * 0.621371192)
|
|
|
|
#define DW_MILES_TO_KM(x) ((x) == G_UNKNOWN ? G_UNKNOWN : (x) * 1.609344)
|
|
|
|
|
|
|
|
#define DW_KNOTS_TO_MPH(x) ((x) == G_UNKNOWN ? G_UNKNOWN : (x) * 1.15077945)
|
|
|
|
#define DW_KNOTS_TO_METERS_PER_SEC(x) ((x) == G_UNKNOWN ? G_UNKNOWN : (x) * 0.51444444444)
|
|
|
|
#define DW_MPH_TO_KNOTS(x) ((x) == G_UNKNOWN ? G_UNKNOWN : (x) * 0.868976)
|
|
|
|
#define DW_MPH_TO_METERS_PER_SEC(x) ((x) == G_UNKNOWN ? G_UNKNOWN : (x) * 0.44704)
|
|
|
|
|
|
|
|
#define DW_MBAR_TO_INHG(x) ((x) == G_UNKNOWN ? G_UNKNOWN : (x) * 0.0295333727)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#if __WIN32__
|
|
|
|
|
|
|
|
typedef CRITICAL_SECTION dw_mutex_t;
|
|
|
|
|
|
|
|
#define dw_mutex_init(x) \
|
|
|
|
InitializeCriticalSection (x)
|
|
|
|
|
|
|
|
/* This one waits for lock. */
|
|
|
|
|
|
|
|
#define dw_mutex_lock(x) \
|
|
|
|
EnterCriticalSection (x)
|
|
|
|
|
|
|
|
/* Returns non-zero if lock was obtained. */
|
|
|
|
|
|
|
|
#define dw_mutex_try_lock(x) \
|
|
|
|
TryEnterCriticalSection (x)
|
|
|
|
|
|
|
|
#define dw_mutex_unlock(x) \
|
|
|
|
LeaveCriticalSection (x)
|
|
|
|
|
|
|
|
|
|
|
|
#else
|
|
|
|
|
|
|
|
typedef pthread_mutex_t dw_mutex_t;
|
|
|
|
|
|
|
|
#define dw_mutex_init(x) pthread_mutex_init (x, NULL)
|
|
|
|
|
|
|
|
/* this one will wait. */
|
|
|
|
|
|
|
|
#define dw_mutex_lock(x) \
|
|
|
|
{ \
|
|
|
|
int err; \
|
|
|
|
err = pthread_mutex_lock (x); \
|
|
|
|
if (err != 0) { \
|
|
|
|
text_color_set(DW_COLOR_ERROR); \
|
|
|
|
dw_printf ("INTERNAL ERROR %s %d pthread_mutex_lock returned %d", __FILE__, __LINE__, err); \
|
|
|
|
exit (1); \
|
|
|
|
} \
|
|
|
|
}
|
|
|
|
|
|
|
|
/* This one returns true if lock successful, false if not. */
|
|
|
|
/* pthread_mutex_trylock returns 0 for success. */
|
|
|
|
|
|
|
|
#define dw_mutex_try_lock(x) \
|
|
|
|
({ \
|
|
|
|
int err; \
|
|
|
|
err = pthread_mutex_trylock (x); \
|
|
|
|
if (err != 0 && err != EBUSY) { \
|
|
|
|
text_color_set(DW_COLOR_ERROR); \
|
|
|
|
dw_printf ("INTERNAL ERROR %s %d pthread_mutex_trylock returned %d", __FILE__, __LINE__, err); \
|
|
|
|
exit (1); \
|
|
|
|
} ; \
|
|
|
|
! err; \
|
|
|
|
})
|
|
|
|
|
|
|
|
#define dw_mutex_unlock(x) \
|
|
|
|
{ \
|
|
|
|
int err; \
|
|
|
|
err = pthread_mutex_unlock (x); \
|
|
|
|
if (err != 0) { \
|
|
|
|
text_color_set(DW_COLOR_ERROR); \
|
|
|
|
dw_printf ("INTERNAL ERROR %s %d pthread_mutex_unlock returned %d", __FILE__, __LINE__, err); \
|
|
|
|
exit (1); \
|
|
|
|
} \
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Formerly used write/read on Linux, for some forgotten reason,
|
|
|
|
// but always using send/recv makes more sense.
|
|
|
|
// Need option to prevent a SIGPIPE signal on Linux. (added for 1.5 beta 2)
|
|
|
|
|
|
|
|
#if __WIN32__ || __APPLE__
|
|
|
|
#define SOCK_SEND(s,data,size) send(s,data,size,0)
|
|
|
|
#else
|
|
|
|
#define SOCK_SEND(s,data,size) send(s,data,size, MSG_NOSIGNAL)
|
|
|
|
#endif
|
|
|
|
#define SOCK_RECV(s,data,size) recv(s,data,size,0)
|
|
|
|
|
|
|
|
|
|
|
|
/* Platform differences for string functions. */
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#if __WIN32__
|
|
|
|
char *strsep(char **stringp, const char *delim);
|
|
|
|
char *strtok_r(char *str, const char *delim, char **saveptr);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
// Don't recall why I added this for everyone rather than only for Windows.
|
|
|
|
char *strcasestr(const char *S, const char *FIND);
|
|
|
|
|
|
|
|
#endif /* ifndef DIREWOLF_H */
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*------------------------------------------------------------------
|
|
|
|
*
|
|
|
|
* Module: audio.h
|
|
|
|
*
|
|
|
|
* Purpose: Interface to audio device commonly called a "sound card"
|
|
|
|
* for historical reasons.
|
|
|
|
*
|
|
|
|
*---------------------------------------------------------------*/
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* PTT control.
|
|
|
|
*/
|
|
|
|
|
|
|
|
enum ptt_method_e {
|
|
|
|
PTT_METHOD_NONE, /* VOX or no transmit. */
|
|
|
|
PTT_METHOD_SERIAL, /* Serial port RTS or DTR. */
|
|
|
|
PTT_METHOD_GPIO, /* General purpose I/O, Linux only. */
|
|
|
|
PTT_METHOD_LPT, /* Parallel printer port, Linux only. */
|
|
|
|
PTT_METHOD_HAMLIB, /* HAMLib, Linux only. */
|
|
|
|
PTT_METHOD_CM108
|
|
|
|
}; /* GPIO pin of CM108/CM119/etc. Linux only. */
|
|
|
|
|
|
|
|
typedef enum ptt_method_e ptt_method_t;
|
|
|
|
|
|
|
|
enum ptt_line_e { PTT_LINE_NONE = 0, PTT_LINE_RTS = 1, PTT_LINE_DTR = 2 }; // Important: 0 for neither.
|
|
|
|
typedef enum ptt_line_e ptt_line_t;
|
|
|
|
|
|
|
|
enum audio_in_type_e {
|
|
|
|
AUDIO_IN_TYPE_SOUNDCARD,
|
|
|
|
AUDIO_IN_TYPE_SDR_UDP,
|
|
|
|
AUDIO_IN_TYPE_STDIN
|
|
|
|
};
|
|
|
|
|
|
|
|
/* For option to try fixing frames with bad CRC. */
|
|
|
|
|
|
|
|
typedef enum retry_e {
|
|
|
|
RETRY_NONE = 0,
|
|
|
|
RETRY_INVERT_SINGLE = 1,
|
|
|
|
RETRY_INVERT_DOUBLE = 2,
|
|
|
|
RETRY_INVERT_TRIPLE = 3,
|
|
|
|
RETRY_INVERT_TWO_SEP = 4,
|
|
|
|
RETRY_MAX = 5
|
|
|
|
} retry_t;
|
|
|
|
|
|
|
|
// Type of communication medium associated with the channel.
|
|
|
|
|
|
|
|
enum medium_e {
|
|
|
|
MEDIUM_NONE = 0, // Channel is not valid for use.
|
|
|
|
MEDIUM_RADIO, // Internal modem for radio.
|
|
|
|
MEDIUM_IGATE, // Access IGate as ordinary channel.
|
|
|
|
MEDIUM_NETTNC
|
|
|
|
}; // Remote network TNC. (possible future)
|
|
|
|
|
|
|
|
|
|
|
|
typedef enum sanity_e { SANITY_APRS, SANITY_AX25, SANITY_NONE } sanity_t;
|
|
|
|
|
|
|
|
|
|
|
|
struct audio_s {
|
|
|
|
|
|
|
|
/* Previously we could handle only a single audio device. */
|
|
|
|
/* In version 1.2, we generalize this to handle multiple devices. */
|
|
|
|
/* This means we can now have more than 2 radio channels. */
|
|
|
|
|
|
|
|
struct adev_param_s {
|
|
|
|
|
|
|
|
/* Properties of the sound device. */
|
|
|
|
|
|
|
|
int defined; /* Was device defined? */
|
|
|
|
/* First one defaults to yes. */
|
|
|
|
|
|
|
|
char adevice_in[80]; /* Name of the audio input device (or file?). */
|
|
|
|
/* TODO: Can be "-" to read from stdin. */
|
|
|
|
|
|
|
|
char adevice_out[80]; /* Name of the audio output device (or file?). */
|
|
|
|
|
|
|
|
int num_channels; /* Should be 1 for mono or 2 for stereo. */
|
|
|
|
int samples_per_sec; /* Audio sampling rate. Typically 11025, 22050, or 44100. */
|
|
|
|
int bits_per_sample; /* 8 (unsigned char) or 16 (signed short). */
|
|
|
|
|
|
|
|
} adev[MAX_ADEVS];
|
|
|
|
|
|
|
|
|
|
|
|
/* Common to all channels. */
|
|
|
|
|
|
|
|
char tts_script[80]; /* Script for text to speech. */
|
|
|
|
|
|
|
|
int statistics_interval; /* Number of seconds between the audio */
|
|
|
|
/* statistics reports. This is set by */
|
|
|
|
/* the "-a" option. 0 to disable feature. */
|
|
|
|
|
|
|
|
int xmit_error_rate; /* For testing purposes, we can generate frames with an invalid CRC */
|
|
|
|
/* to simulate corruption while going over the air. */
|
|
|
|
/* This is the probability, in per cent, of randomly corrupting it. */
|
|
|
|
/* Normally this is 0. 25 would mean corrupt it 25% of the time. */
|
|
|
|
|
|
|
|
int recv_error_rate; /* Similar but the % probability of dropping a received frame. */
|
|
|
|
|
|
|
|
float recv_ber; /* Receive Bit Error Rate (BER). */
|
|
|
|
/* Probability of inverting a bit coming out of the modem. */
|
|
|
|
|
|
|
|
//int fx25_xmit_enable; /* Enable transmission of FX.25. */
|
|
|
|
/* See fx25_init.c for explanation of values. */
|
|
|
|
/* Initially this applies to all channels. */
|
|
|
|
/* This should probably be per channel. One step at a time. */
|
|
|
|
/* v1.7 - replaced by layer2_xmit==LAYER2_FX25 */
|
|
|
|
|
|
|
|
int fx25_auto_enable; /* Turn on FX.25 for current connected mode session */
|
|
|
|
/* under poor conditions. */
|
|
|
|
/* Set to 0 to disable feature. */
|
|
|
|
/* I put it here, rather than with the rest of the link layer */
|
|
|
|
/* parameters because it is really a part of the HDLC layer */
|
|
|
|
/* and is part of the KISS TNC functionality rather than our data link layer. */
|
|
|
|
/* Future: not used yet. */
|
|
|
|
|
|
|
|
|
|
|
|
char timestamp_format[40]; /* -T option */
|
|
|
|
/* Precede received & transmitted frames with timestamp. */
|
|
|
|
/* Command line option uses "strftime" format string. */
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* originally a "channel" was always connected to an internal modem. */
|
|
|
|
/* In version 1.6, this is generalized so that a channel (as seen by client application) */
|
|
|
|
/* can be connected to something else. Initially, this will allow application */
|
|
|
|
/* access to the IGate. Later we might have network TNCs or other internal functions. */
|
|
|
|
|
|
|
|
// Properties for all channels.
|
|
|
|
|
|
|
|
enum medium_e chan_medium[MAX_TOTAL_CHANS];
|
|
|
|
// MEDIUM_NONE for invalid.
|
|
|
|
// MEDIUM_RADIO for internal modem. (only possibility earlier)
|
|
|
|
// MEDIUM_IGATE allows application access to IGate.
|
|
|
|
// MEDIUM_NETTNC for external TNC via TCP.
|
|
|
|
|
|
|
|
int igate_vchannel; /* Virtual channel mapped to APRS-IS. */
|
|
|
|
/* -1 for none. */
|
|
|
|
/* Redundant but it makes things quicker and simpler */
|
|
|
|
/* than always searching thru above. */
|
|
|
|
|
|
|
|
/* Properties for each radio channel, common to receive and transmit. */
|
|
|
|
/* Can be different for each radio channel. */
|
|
|
|
|
|
|
|
struct achan_param_s {
|
|
|
|
|
|
|
|
// What else should be moved out of structure and enlarged when NETTNC is implemented. ???
|
|
|
|
char mycall[AX25_MAX_ADDR_LEN]; /* Call associated with this radio channel. */
|
|
|
|
/* Could all be the same or different. */
|
|
|
|
|
|
|
|
|
|
|
|
enum modem_t { MODEM_AFSK, MODEM_BASEBAND, MODEM_SCRAMBLE, MODEM_QPSK, MODEM_8PSK, MODEM_OFF, MODEM_16_QAM, MODEM_64_QAM, MODEM_AIS, MODEM_EAS } modem_type;
|
|
|
|
|
|
|
|
/* Usual AFSK. */
|
|
|
|
/* Baseband signal. Not used yet. */
|
|
|
|
/* Scrambled http://www.amsat.org/amsat/articles/g3ruh/109/fig03.gif */
|
|
|
|
/* Might try MFJ-2400 / CCITT v.26 / Bell 201 someday. */
|
|
|
|
/* No modem. Might want this for DTMF only channel. */
|
|
|
|
|
|
|
|
enum layer2_t { LAYER2_AX25 = 0, LAYER2_FX25, LAYER2_IL2P } layer2_xmit;
|
|
|
|
|
|
|
|
// IL2P - New for version 1.7.
|
|
|
|
// New layer 2 with FEC. Much less overhead than FX.25 but no longer backward compatible.
|
|
|
|
// Only applies to transmit.
|
|
|
|
// Listening for FEC sync word should add negligible overhead so
|
|
|
|
// we leave reception enabled all the time as we do with FX.25.
|
|
|
|
// TODO: FX.25 should probably be put here rather than global for all channels.
|
|
|
|
|
|
|
|
int fx25_strength; // Strength of FX.25 FEC.
|
|
|
|
// 16, 23, 64 for specific number of parity symbols.
|
|
|
|
// 1 for automatic selection based on frame size.
|
|
|
|
|
|
|
|
int il2p_max_fec; // 1 for max FEC length, 0 for automatic based on size.
|
|
|
|
|
|
|
|
int il2p_invert_polarity; // 1 means invert on transmit. Receive handles either automatically.
|
|
|
|
|
|
|
|
enum v26_e { V26_UNSPECIFIED = 0, V26_A, V26_B } v26_alternative;
|
|
|
|
|
|
|
|
// Original implementation used alternative A for 2400 bbps PSK.
|
|
|
|
// Years later, we discover that MFJ-2400 used alternative B.
|
|
|
|
// It's likely the others did too. it also works a little better.
|
|
|
|
// Default to MFJ compatible and print warning if user did not
|
|
|
|
// pick one explicitly.
|
|
|
|
|
|
|
|
#define V26_DEFAULT V26_B
|
|
|
|
|
|
|
|
enum dtmf_decode_t { DTMF_DECODE_OFF, DTMF_DECODE_ON } dtmf_decode;
|
|
|
|
|
|
|
|
/* Originally the DTMF ("Touch Tone") decoder was always */
|
|
|
|
/* enabled because it took a negligible amount of CPU. */
|
|
|
|
/* There were complaints about the false positives when */
|
|
|
|
/* hearing other modulation schemes on HF SSB so now it */
|
|
|
|
/* is enabled only when needed. */
|
|
|
|
|
|
|
|
/* "On" will send special "t" packet to attached applications */
|
|
|
|
/* and process as APRStt. Someday we might want to separate */
|
|
|
|
/* these but for now, we have a single off/on. */
|
|
|
|
|
|
|
|
int decimate; /* Reduce AFSK sample rate by this factor to */
|
|
|
|
/* decrease computational requirements. */
|
|
|
|
|
|
|
|
int upsample; /* Upsample by this factor for G3RUH. */
|
|
|
|
|
|
|
|
int mark_freq; /* Two tones for AFSK modulation, in Hz. */
|
|
|
|
int space_freq; /* Standard tones are 1200 and 2200 for 1200 baud. */
|
|
|
|
|
|
|
|
int baud; /* Data bits per second. */
|
|
|
|
/* Standard rates are 1200 for VHF and 300 for HF. */
|
|
|
|
/* This should really be called bits per second. */
|
|
|
|
|
|
|
|
/* Next 3 come from config file or command line. */
|
|
|
|
|
|
|
|
char profiles[16]; /* zero or more of ABC etc, optional + */
|
|
|
|
|
|
|
|
int num_freq; /* Number of different frequency pairs for decoders. */
|
|
|
|
|
|
|
|
int offset; /* Spacing between filter frequencies. */
|
|
|
|
|
|
|
|
int num_slicers; /* Number of different threshold points to decide */
|
|
|
|
/* between mark or space. */
|
|
|
|
|
|
|
|
/* This is derived from above by demod_init. */
|
|
|
|
|
|
|
|
int num_subchan; /* Total number of modems for each channel. */
|
|
|
|
|
|
|
|
|
|
|
|
/* These are for dealing with imperfect frames. */
|
|
|
|
|
|
|
|
enum retry_e fix_bits; /* Level of effort to recover from */
|
|
|
|
/* a bad FCS on the frame. */
|
|
|
|
/* 0 = no effort */
|
|
|
|
/* 1 = try fixing a single bit */
|
|
|
|
/* 2... = more techniques... */
|
|
|
|
|
|
|
|
enum sanity_e sanity_test; /* Sanity test to apply when finding a good */
|
|
|
|
/* CRC after making a change. */
|
|
|
|
/* Must look like APRS, AX.25, or anything. */
|
|
|
|
|
|
|
|
int passall; /* Allow thru even with bad CRC. */
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Additional properties for transmit. */
|
|
|
|
|
|
|
|
/* Originally we had control outputs only for PTT. */
|
|
|
|
/* In version 1.2, we generalize this to allow others such as DCD. */
|
|
|
|
/* In version 1.4 we add CON for connected to another station. */
|
|
|
|
/* Index following structure by one of these: */
|
|
|
|
|
|
|
|
|
|
|
|
#define OCTYPE_PTT 0
|
|
|
|
#define OCTYPE_DCD 1
|
|
|
|
#define OCTYPE_CON 2
|
|
|
|
|
|
|
|
#define NUM_OCTYPES 3 /* number of values above. i.e. last value +1. */
|
|
|
|
|
|
|
|
struct {
|
|
|
|
|
|
|
|
ptt_method_t ptt_method; /* none, serial port, GPIO, LPT, HAMLIB, CM108. */
|
|
|
|
|
|
|
|
char ptt_device[128]; /* Serial device name for PTT. e.g. COM1 or /dev/ttyS0 */
|
|
|
|
/* Also used for HAMLIB. Could be host:port when model is 1. */
|
|
|
|
/* For years, 20 characters was plenty then we start getting extreme names like this: */
|
|
|
|
/* /dev/serial/by-id/usb-FTDI_Navigator__CAT___2nd_PTT__00000000-if00-port0 */
|
|
|
|
/* /dev/serial/by-id/usb-Prolific_Technology_Inc._USB-Serial_Controller_D-if00-port0 */
|
|
|
|
/* Issue 104, changed to 100 bytes in version 1.5. */
|
|
|
|
|
|
|
|
/* This same field is also used for CM108/CM119 GPIO PTT which will */
|
|
|
|
/* have a name like /dev/hidraw1 for Linux or */
|
|
|
|
/* \\?\hid#vid_0d8c&pid_0008&mi_03#8&39d3555&0&0000#{4d1e55b2-f16f-11cf-88cb-001111000030} */
|
|
|
|
/* for Windows. Largest observed was 95 but add some extra to be safe. */
|
|
|
|
|
|
|
|
ptt_line_t ptt_line; /* Control line when using serial port. PTT_LINE_RTS, PTT_LINE_DTR. */
|
|
|
|
ptt_line_t ptt_line2; /* Optional second one: PTT_LINE_NONE when not used. */
|
|
|
|
|
|
|
|
int out_gpio_num; /* GPIO number. Originally this was only for PTT. */
|
|
|
|
/* It is now more general. */
|
|
|
|
/* octrl array is indexed by PTT, DCD, or CONnected indicator. */
|
|
|
|
/* For CM108/CM119, this should be in range of 1-8. */
|
|
|
|
|
|
|
|
#define MAX_GPIO_NAME_LEN 20 // 12 would cover any case I've seen so this should be safe
|
|
|
|
|
|
|
|
char out_gpio_name[MAX_GPIO_NAME_LEN];
|
|
|
|
/* originally, gpio number NN was assumed to simply */
|
|
|
|
/* have the name gpioNN but this turned out not to be */
|
|
|
|
/* the case for CubieBoard where it was longer. */
|
|
|
|
/* This is filled in by ptt_init so we don't have to */
|
|
|
|
/* recalculate it each time we access it. */
|
|
|
|
|
|
|
|
/* This could probably be collapsed into ptt_device instead of being separate. */
|
|
|
|
|
|
|
|
int ptt_lpt_bit; /* Bit number for parallel printer port. */
|
|
|
|
/* Bit 0 = pin 2, ..., bit 7 = pin 9. */
|
|
|
|
|
|
|
|
int ptt_invert; /* Invert the output. */
|
|
|
|
int ptt_invert2; /* Invert the secondary output. */
|
|
|
|
|
|
|
|
#ifdef USE_HAMLIB
|
|
|
|
|
|
|
|
int ptt_model; /* HAMLIB model. -1 for AUTO. 2 for rigctld. Others are radio model. */
|
|
|
|
int ptt_rate; /* Serial port speed when using hamlib CAT control for PTT. */
|
|
|
|
/* If zero, hamlib will come up with a default for pariticular rig. */
|
|
|
|
#endif
|
|
|
|
|
|
|
|
} octrl[NUM_OCTYPES];
|
|
|
|
|
|
|
|
|
|
|
|
/* Each channel can also have associated input lines. */
|
|
|
|
/* So far, we just have one for transmit inhibit. */
|
|
|
|
|
|
|
|
#define ICTYPE_TXINH 0
|
|
|
|
|
|
|
|
#define NUM_ICTYPES 1 /* number of values above. i.e. last value +1. */
|
|
|
|
|
|
|
|
struct {
|
|
|
|
ptt_method_t method; /* none, serial port, GPIO, LPT. */
|
|
|
|
|
|
|
|
int in_gpio_num; /* GPIO number */
|
|
|
|
|
|
|
|
char in_gpio_name[MAX_GPIO_NAME_LEN];
|
|
|
|
/* originally, gpio number NN was assumed to simply */
|
|
|
|
/* have the name gpioNN but this turned out not to be */
|
|
|
|
/* the case for CubieBoard where it was longer. */
|
|
|
|
/* This is filled in by ptt_init so we don't have to */
|
|
|
|
/* recalculate it each time we access it. */
|
|
|
|
|
|
|
|
int invert; /* 1 = active low */
|
|
|
|
} ictrl[NUM_ICTYPES];
|
|
|
|
|
|
|
|
/* Transmit timing. */
|
|
|
|
|
|
|
|
int dwait; /* First wait extra time for receiver squelch. */
|
|
|
|
/* Default 0 units of 10 mS each . */
|
|
|
|
|
|
|
|
int slottime; /* Slot time in 10 mS units for persistence algorithm. */
|
|
|
|
/* Typical value is 10 meaning 100 milliseconds. */
|
|
|
|
|
|
|
|
int persist; /* Sets probability for transmitting after each */
|
|
|
|
/* slot time delay. Transmit if a random number */
|
|
|
|
/* in range of 0 - 255 <= persist value. */
|
|
|
|
/* Otherwise wait another slot time and try again. */
|
|
|
|
/* Default value is 63 for 25% probability. */
|
|
|
|
|
|
|
|
int txdelay; /* After turning on the transmitter, */
|
|
|
|
/* send "flags" for txdelay * 10 mS. */
|
|
|
|
/* Default value is 30 meaning 300 milliseconds. */
|
|
|
|
|
|
|
|
int txtail; /* Amount of time to keep transmitting after we */
|
|
|
|
/* are done sending the data. This is to avoid */
|
|
|
|
/* dropping PTT too soon and chopping off the end */
|
|
|
|
/* of the frame. Again 10 mS units. */
|
|
|
|
/* At this point, I'm thinking of 10 (= 100 mS) as the default */
|
|
|
|
/* because we're not quite sure when the soundcard audio stops. */
|
|
|
|
|
|
|
|
int fulldup; /* Full Duplex. */
|
|
|
|
|
|
|
|
} achan[MAX_CHANS];
|
|
|
|
|
|
|
|
#ifdef USE_HAMLIB
|
|
|
|
int rigs; /* Total number of configured rigs */
|
|
|
|
RIG *rig[MAX_RIGS]; /* HAMLib rig instances */
|
|
|
|
#endif
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
#if __WIN32__
|
|
|
|
#define DEFAULT_ADEVICE "" /* Windows: Empty string = default audio device. */
|
|
|
|
#elif __APPLE__
|
|
|
|
#define DEFAULT_ADEVICE "" /* Mac OSX: Empty string = default audio device. */
|
|
|
|
#elif USE_ALSA
|
|
|
|
#define DEFAULT_ADEVICE "default" /* Use default device for ALSA. */
|
|
|
|
#elif USE_SNDIO
|
|
|
|
#define DEFAULT_ADEVICE "default" /* Use default device for sndio. */
|
|
|
|
#else
|
|
|
|
#define DEFAULT_ADEVICE "/dev/dsp" /* First audio device for OSS. (FreeBSD) */
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* UDP audio receiving port. Couldn't find any standard or usage precedent.
|
|
|
|
* Got the number from this example: http://gqrx.dk/doc/streaming-audio-over-udp
|
|
|
|
* Any better suggestions?
|
|
|
|
*/
|
|
|
|
|
|
|
|
#define DEFAULT_UDP_AUDIO_PORT 7355
|
|
|
|
|
|
|
|
|
|
|
|
// Maximum size of the UDP buffer (for allowing IP routing, udp packets are often limited to 1472 bytes)
|
|
|
|
|
|
|
|
#define SDR_UDP_BUF_MAXLEN 2000
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#define DEFAULT_NUM_CHANNELS 1
|
|
|
|
#define DEFAULT_SAMPLES_PER_SEC 44100 /* Very early observations. Might no longer be valid. */
|
|
|
|
/* 22050 works a lot better than 11025. */
|
|
|
|
/* 44100 works a little better than 22050. */
|
|
|
|
/* If you have a reasonable machine, use the highest rate. */
|
|
|
|
#define MIN_SAMPLES_PER_SEC 8000
|
|
|
|
//#define MAX_SAMPLES_PER_SEC 48000 /* Originally 44100. Later increased because */
|
|
|
|
/* Software Defined Radio often uses 48000. */
|
|
|
|
|
|
|
|
#define MAX_SAMPLES_PER_SEC 192000 /* The cheap USB-audio adapters (e.g. CM108) can handle 44100 and 48000. */
|
|
|
|
/* The "soundcard" in my desktop PC can do 96kHz or even 192kHz. */
|
|
|
|
/* We will probably need to increase the sample rate to go much above 9600 baud. */
|
|
|
|
|
|
|
|
#define DEFAULT_BITS_PER_SAMPLE 16
|
|
|
|
|
|
|
|
#define DEFAULT_FIX_BITS RETRY_INVERT_SINGLE
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Standard for AFSK on VHF FM.
|
|
|
|
* Reversing mark and space makes no difference because
|
|
|
|
* NRZI encoding only cares about change or lack of change
|
|
|
|
* between the two tones.
|
|
|
|
*
|
|
|
|
* HF SSB uses 300 baud and 200 Hz shift.
|
|
|
|
* 1600 & 1800 Hz is a popular tone pair, sometimes
|
|
|
|
* called the KAM tones.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#define DEFAULT_MARK_FREQ 1200
|
|
|
|
#define DEFAULT_SPACE_FREQ 2200
|
|
|
|
#define DEFAULT_BAUD 1200
|
|
|
|
|
|
|
|
/* Used for sanity checking in config file and command line options. */
|
|
|
|
/* 9600 baud is known to work. */
|
|
|
|
/* TODO: Is 19200 possible with a soundcard at 44100 samples/sec or do we need a higher sample rate? */
|
|
|
|
|
|
|
|
#define MIN_BAUD 100
|
|
|
|
//#define MAX_BAUD 10000
|
|
|
|
#define MAX_BAUD 40000 // Anyone want to try 38.4 k baud?
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Typical transmit timings for VHF.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#define DEFAULT_DWAIT 0
|
|
|
|
#define DEFAULT_SLOTTIME 10
|
|
|
|
#define DEFAULT_PERSIST 63
|
|
|
|
#define DEFAULT_TXDELAY 30
|
|
|
|
#define DEFAULT_TXTAIL 10
|
|
|
|
#define DEFAULT_FULLDUP 0
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Note that we have two versions of these in audio.c and audio_win.c.
|
|
|
|
* Use one or the other depending on the platform.
|
|
|
|
*/
|
|
|
|
|
|
|
|
int audio_open(struct audio_s *pa);
|
|
|
|
|
|
|
|
int audio_get(int a); /* a = audio device, 0 for first */
|
|
|
|
|
|
|
|
int audio_put(int a, int c);
|
|
|
|
|
|
|
|
int audio_flush(int a);
|
|
|
|
|
|
|
|
void audio_wait(int a);
|
|
|
|
|
|
|
|
int audio_close(void);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* end audio.h */
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void multi_modem_init(struct audio_s *pmodem);
|
|
|
|
|
|
|
|
void multi_modem_process_sample(int c, int audio_sample);
|
|
|
|
|
|
|
|
int multi_modem_get_dc_average(int chan);
|
|
|
|
|
|
|
|
// Deprecated. Replace with ...packet
|
|
|
|
|
|
|
|
|
|
|
|
void multi_modem_process_rec_packet(int chan, int subchan, int slice, packet_t pp, alevel_t alevel, retry_t retries, int is_fx25);
|
|
|
|
|
|
|
|
|
|
|
|
void fsk_gen_filter(int samples_per_sec,
|
|
|
|
int baud,
|
|
|
|
int mark_freq, int space_freq,
|
|
|
|
char profile,
|
|
|
|
struct demodulator_state_s *D);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* fsk_demod_state.h */
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Demodulator state.
|
|
|
|
* The name of the file is from we only had FSK. Now we have other techniques.
|
|
|
|
* Different copy is required for each channel & subchannel being processed concurrently.
|
|
|
|
*/
|
|
|
|
|
|
|
|
// TODO1.2: change prefix from BP_ to DSP_
|
|
|
|
|
|
|
|
typedef enum bp_window_e {
|
|
|
|
BP_WINDOW_TRUNCATED,
|
|
|
|
BP_WINDOW_COSINE,
|
|
|
|
BP_WINDOW_HAMMING,
|
|
|
|
BP_WINDOW_BLACKMAN,
|
|
|
|
BP_WINDOW_FLATTOP
|
|
|
|
} bp_window_t;
|
|
|
|
|
|
|
|
// Experimental low pass filter to detect DC bias or low frequency changes.
|
|
|
|
// IIR behaves like an analog R-C filter.
|
|
|
|
// Intuitively, it seems like FIR would be better because it is based on a finite history.
|
|
|
|
// However, it would require MANY taps and a LOT of computation for a low frequency.
|
|
|
|
// We can use a little trick here to keep a running average.
|
|
|
|
// This would be equivalent to convolving with an array of all 1 values.
|
|
|
|
// That would eliminate the need to multiply.
|
|
|
|
// We can also eliminate the need to add them all up each time by keeping a running total.
|
|
|
|
// Add a sample to the total when putting it in our array of recent samples.
|
|
|
|
// Subtract it from the total when it gets pushed off the end.
|
|
|
|
// We can also eliminate the need to shift them all down by using a circular buffer.
|
|
|
|
|
|
|
|
#define CIC_LEN_MAX 4000
|
|
|
|
|
|
|
|
typedef struct cic_s {
|
|
|
|
int len; // Number of elements used.
|
|
|
|
// Might want to dynamically allocate.
|
|
|
|
short in[CIC_LEN_MAX]; // Samples coming in.
|
|
|
|
int sum; // Running sum.
|
|
|
|
int inext; // Next position to fill.
|
|
|
|
} cic_t;
|
|
|
|
|
|
|
|
|
|
|
|
#define MAX_FILTER_SIZE 480 /* 401 is needed for profile A, 300 baud & 44100. Revisit someday. */
|
|
|
|
// Size comes out to 417 for 1200 bps with 48000 sample rate
|
|
|
|
// v1.7 - Was 404. Bump up to 480.
|
|
|
|
|
|
|
|
struct demodulator_state_s
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* These are set once during initialization.
|
|
|
|
*/
|
|
|
|
enum modem_t modem_type; // MODEM_AFSK, MODEM_8PSK, etc.
|
|
|
|
|
|
|
|
// enum v26_e v26_alt; // Which alternative when V.26.
|
|
|
|
|
|
|
|
char profile; // 'A', 'B', etc. Upper case.
|
|
|
|
// Only needed to see if we are using 'F' to take fast path.
|
|
|
|
|
|
|
|
#define TICKS_PER_PLL_CYCLE ( 256.0 * 256.0 * 256.0 * 256.0 )
|
|
|
|
|
|
|
|
int pll_step_per_sample; // PLL is advanced by this much each audio sample.
|
|
|
|
// Data is sampled when it overflows.
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Window type for the various filters.
|
|
|
|
*/
|
|
|
|
|
|
|
|
bp_window_t lp_window;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Alternate Low pass filters.
|
|
|
|
* First is arbitrary number for quick IIR.
|
|
|
|
* Second is frequency as ratio to baud rate for FIR.
|
|
|
|
*/
|
|
|
|
int lpf_use_fir; /* 0 for IIR, 1 for FIR. */
|
|
|
|
|
|
|
|
float lpf_iir; /* Only if using IIR. */
|
|
|
|
|
|
|
|
float lpf_baud; /* Cutoff frequency as fraction of baud. */
|
|
|
|
/* Intuitively we'd expect this to be somewhere */
|
|
|
|
/* in the range of 0.5 to 1. */
|
|
|
|
/* In practice, it turned out a little larger */
|
|
|
|
/* for profiles B, C, D. */
|
|
|
|
|
|
|
|
float lp_filter_width_sym; /* Length in number of symbol times. */
|
|
|
|
|
|
|
|
#define lp_filter_len_bits lp_filter_width_sym // FIXME: temp hack
|
|
|
|
|
|
|
|
int lp_filter_taps; /* Size of Low Pass filter, in audio samples. */
|
|
|
|
|
|
|
|
#define lp_filter_size lp_filter_taps // FIXME: temp hack
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Automatic gain control. Fast attack and slow decay factors.
|
|
|
|
*/
|
|
|
|
float agc_fast_attack;
|
|
|
|
float agc_slow_decay;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Use a longer term view for reporting signal levels.
|
|
|
|
*/
|
|
|
|
float quick_attack;
|
|
|
|
float sluggish_decay;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Hysteresis before final demodulator 0 / 1 decision.
|
|
|
|
*/
|
|
|
|
float hysteresis;
|
|
|
|
int num_slicers; /* >1 for multiple slicers. */
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Phase Locked Loop (PLL) inertia.
|
|
|
|
* Larger number means less influence by signal transitions.
|
|
|
|
* It is more resistant to change when locked on to a signal.
|
|
|
|
*/
|
|
|
|
float pll_locked_inertia;
|
|
|
|
float pll_searching_inertia;
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Optional band pass pre-filter before mark/space detector.
|
|
|
|
*/
|
|
|
|
int use_prefilter; /* True to enable it. */
|
|
|
|
|
|
|
|
float prefilter_baud; /* Cutoff frequencies, as fraction of */
|
|
|
|
/* baud rate, beyond tones used. */
|
|
|
|
/* Example, if we used 1600/1800 tones at */
|
|
|
|
/* 300 baud, and this was 0.5, the cutoff */
|
|
|
|
/* frequencies would be: */
|
|
|
|
/* lower = min(1600,1800) - 0.5 * 300 = 1450 */
|
|
|
|
/* upper = max(1600,1800) + 0.5 * 300 = 1950 */
|
|
|
|
|
|
|
|
float pre_filter_len_sym; // Length in number of symbol times.
|
|
|
|
#define pre_filter_len_bits pre_filter_len_sym // temp until all references changed.
|
|
|
|
|
|
|
|
bp_window_t pre_window; // Window type for filter shaping.
|
|
|
|
|
|
|
|
int pre_filter_taps; // Calculated number of filter taps.
|
|
|
|
#define pre_filter_size pre_filter_taps // temp until all references changed.
|
|
|
|
|
|
|
|
float pre_filter[MAX_FILTER_SIZE];
|
|
|
|
|
|
|
|
float raw_cb[MAX_FILTER_SIZE]; // audio in, need better name.
|
|
|
|
|
|
|
|
/*
|
|
|
|
* The rest are continuously updated.
|
|
|
|
*/
|
|
|
|
|
|
|
|
unsigned int lo_phase; /* Local oscillator for PSK. */
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Use half of the AGC code to get a measure of input audio amplitude.
|
|
|
|
* These use "quick" attack and "sluggish" decay while the
|
|
|
|
* AGC uses "fast" attack and "slow" decay.
|
|
|
|
*/
|
|
|
|
|
|
|
|
float alevel_rec_peak;
|
|
|
|
float alevel_rec_valley;
|
|
|
|
float alevel_mark_peak;
|
|
|
|
float alevel_space_peak;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Outputs from the mark and space amplitude detection,
|
|
|
|
* used as inputs to the FIR lowpass filters.
|
|
|
|
* Kernel for the lowpass filters.
|
|
|
|
*/
|
|
|
|
|
|
|
|
float lp_filter[MAX_FILTER_SIZE];
|
|
|
|
|
|
|
|
float m_peak, s_peak;
|
|
|
|
float m_valley, s_valley;
|
|
|
|
float m_amp_prev, s_amp_prev;
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* For the PLL and data bit timing.
|
|
|
|
* starting in version 1.2 we can have multiple slicers for one demodulator.
|
|
|
|
* Each slicer has its own PLL and HDLC decoder.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Version 1.3: Clean up subchan vs. slicer.
|
|
|
|
*
|
|
|
|
* Originally some number of CHANNELS (originally 2, later 6)
|
|
|
|
* which can have multiple parallel demodulators called SUB-CHANNELS.
|
|
|
|
* This was originally for staggered frequencies for HF SSB.
|
|
|
|
* It can also be used for multiple demodulators with the same
|
|
|
|
* frequency but other differing parameters.
|
|
|
|
* Each subchannel has its own demodulator and HDLC decoder.
|
|
|
|
*
|
|
|
|
* In version 1.2 we added multiple SLICERS.
|
|
|
|
* The data structure, here, has multiple slicers per
|
|
|
|
* demodulator (subchannel). Due to fuzzy thinking or
|
|
|
|
* expediency, the multiple slicers got mapped into subchannels.
|
|
|
|
* This means we can't use both multiple decoders and
|
|
|
|
* multiple slicers at the same time.
|
|
|
|
*
|
|
|
|
* Clean this up in 1.3 and keep the concepts separate.
|
|
|
|
* This means adding a third variable many places
|
|
|
|
* we are passing around the origin.
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
struct {
|
|
|
|
|
|
|
|
signed int data_clock_pll; // PLL for data clock recovery.
|
|
|
|
// It is incremented by pll_step_per_sample
|
|
|
|
// for each audio sample.
|
|
|
|
// Must be 32 bits!!!
|
|
|
|
// So far, this is the case for every compiler used.
|
|
|
|
|
|
|
|
signed int prev_d_c_pll; // Previous value of above, before
|
|
|
|
// incrementing, to detect overflows.
|
|
|
|
|
|
|
|
int pll_symbol_count; // Number symbols during time nudge_total is accumulated.
|
|
|
|
int64_t pll_nudge_total; // Sum of DPLL nudge amounts.
|
|
|
|
// Both of these are cleared at start of frame.
|
|
|
|
// At end of frame, we can see if incoming
|
|
|
|
// baud rate is a little off.
|
|
|
|
|
|
|
|
int prev_demod_data; // Previous data bit detected.
|
|
|
|
// Used to look for transitions.
|
|
|
|
float prev_demod_out_f;
|
|
|
|
|
|
|
|
/* This is used only for "9600" baud data. */
|
|
|
|
|
|
|
|
int lfsr; // Descrambler shift register.
|
|
|
|
|
|
|
|
// This is for detecting phase lock to incoming signal.
|
|
|
|
|
|
|
|
int good_flag; // Set if transition is near where expected,
|
|
|
|
// i.e. at a good time.
|
|
|
|
int bad_flag; // Set if transition is not where expected,
|
|
|
|
// i.e. at a bad time.
|
|
|
|
unsigned char good_hist; // History of good transitions for past octet.
|
|
|
|
unsigned char bad_hist; // History of bad transitions for past octet.
|
|
|
|
unsigned int score; // History of whether good triumphs over bad
|
|
|
|
// for past 32 symbols.
|
|
|
|
int data_detect; // True when locked on to signal.
|
|
|
|
|
|
|
|
} slicer[MAX_SLICERS]; // Actual number in use is num_slicers.
|
|
|
|
// Should be in range 1 .. MAX_SLICERS,
|
|
|
|
/*
|
|
|
|
* Version 1.6:
|
|
|
|
*
|
|
|
|
* This has become quite disorganized and messy with different combinations of
|
|
|
|
* fields used for different demodulator types. Start to reorganize it into a common
|
|
|
|
* part (with things like the DPLL for clock recovery), and separate sections
|
|
|
|
* for each of the demodulator types.
|
|
|
|
* Still a lot to do here.
|
|
|
|
*/
|
|
|
|
|
|
|
|
union {
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// //
|
|
|
|
// AFSK only - new method in 1.7 //
|
|
|
|
// //
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
|
|
struct afsk_only_s {
|
|
|
|
|
|
|
|
unsigned int m_osc_phase; // Phase for Mark local oscillator.
|
|
|
|
unsigned int m_osc_delta; // How much to change for each audio sample.
|
|
|
|
|
|
|
|
unsigned int s_osc_phase; // Phase for Space local oscillator.
|
|
|
|
unsigned int s_osc_delta; // How much to change for each audio sample.
|
|
|
|
|
|
|
|
unsigned int c_osc_phase; // Phase for Center frequency local oscillator.
|
|
|
|
unsigned int c_osc_delta; // How much to change for each audio sample.
|
|
|
|
|
|
|
|
// Need two mixers for profile "A".
|
|
|
|
|
|
|
|
float m_I_raw[MAX_FILTER_SIZE];
|
|
|
|
float m_Q_raw[MAX_FILTER_SIZE];
|
|
|
|
|
|
|
|
float s_I_raw[MAX_FILTER_SIZE];
|
|
|
|
float s_Q_raw[MAX_FILTER_SIZE];
|
|
|
|
|
|
|
|
// Only need one mixer for profile "B". Reuse the same storage?
|
|
|
|
|
|
|
|
//#define c_I_raw m_I_raw
|
|
|
|
//#define c_Q_raw m_Q_raw
|
|
|
|
float c_I_raw[MAX_FILTER_SIZE];
|
|
|
|
float c_Q_raw[MAX_FILTER_SIZE];
|
|
|
|
|
|
|
|
int use_rrc; // Use RRC rather than generic low pass.
|
|
|
|
|
|
|
|
float rrc_width_sym; /* Width of RRC filter in number of symbols. */
|
|
|
|
|
|
|
|
float rrc_rolloff; /* Rolloff factor for RRC. Between 0 and 1. */
|
|
|
|
|
|
|
|
float prev_phase; // To see phase shift between samples for FM demod.
|
|
|
|
|
|
|
|
float normalize_rpsam; // Normalize to -1 to +1 for expected tones.
|
|
|
|
|
|
|
|
} afsk;
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// //
|
|
|
|
// Baseband only, AKA G3RUH //
|
|
|
|
// //
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
// TODO: Continue experiments with root raised cosine filter.
|
|
|
|
// Either switch to that or take out all the related stuff.
|
|
|
|
|
|
|
|
struct bb_only_s {
|
|
|
|
|
|
|
|
float rrc_width_sym; /* Width of RRC filter in number of symbols. */
|
|
|
|
|
|
|
|
float rrc_rolloff; /* Rolloff factor for RRC. Between 0 and 1. */
|
|
|
|
|
|
|
|
int rrc_filter_taps; // Number of elements used in the next two.
|
|
|
|
|
|
|
|
// FIXME: TODO: reevaluate max size needed.
|
|
|
|
|
|
|
|
float audio_in[MAX_FILTER_SIZE]; // Audio samples in.
|
|
|
|
|
|
|
|
|
|
|
|
float lp_filter[MAX_FILTER_SIZE]; // Low pass filter.
|
|
|
|
|
|
|
|
// New in 1.7 - Polyphase filter to reduce CPU requirements.
|
|
|
|
|
|
|
|
float lp_polyphase_1[MAX_FILTER_SIZE];
|
|
|
|
float lp_polyphase_2[MAX_FILTER_SIZE];
|
|
|
|
float lp_polyphase_3[MAX_FILTER_SIZE];
|
|
|
|
float lp_polyphase_4[MAX_FILTER_SIZE];
|
|
|
|
|
|
|
|
float lp_1_iir_param; // very low pass filters to get DC offset.
|
|
|
|
float lp_1_out;
|
|
|
|
|
|
|
|
float lp_2_iir_param;
|
|
|
|
float lp_2_out;
|
|
|
|
|
|
|
|
float agc_1_fast_attack; // Signal envelope detection.
|
|
|
|
float agc_1_slow_decay;
|
|
|
|
float agc_1_peak;
|
|
|
|
float agc_1_valley;
|
|
|
|
|
|
|
|
float agc_2_fast_attack;
|
|
|
|
float agc_2_slow_decay;
|
|
|
|
float agc_2_peak;
|
|
|
|
float agc_2_valley;
|
|
|
|
|
|
|
|
float agc_3_fast_attack;
|
|
|
|
float agc_3_slow_decay;
|
|
|
|
float agc_3_peak;
|
|
|
|
float agc_3_valley;
|
|
|
|
|
|
|
|
// CIC low pass filters to detect DC bias or low frequency changes.
|
|
|
|
// IIR behaves like an analog R-C filter.
|
|
|
|
// Intuitively, it seems like FIR would be better because it is based on a finite history.
|
|
|
|
// However, it would require MANY taps and a LOT of computation for a low frequency.
|
|
|
|
// We can use a little trick here to keep a running average.
|
|
|
|
// This would be equivalent to convolving with an array of all 1 values.
|
|
|
|
// That would eliminate the need to multiply.
|
|
|
|
// We can also eliminate the need to add them all up each time by keeping a running total.
|
|
|
|
// Add a sample to the total when putting it in our array of recent samples.
|
|
|
|
// Subtract it from the total when it gets pushed off the end.
|
|
|
|
// We can also eliminate the need to shift them all down by using a circular buffer.
|
|
|
|
// This only works with integers because float would have cumulated round off errors.
|
|
|
|
|
|
|
|
cic_t cic_center1;
|
|
|
|
cic_t cic_above;
|
|
|
|
cic_t cic_below;
|
|
|
|
|
|
|
|
} bb;
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// //
|
|
|
|
// PSK only. //
|
|
|
|
// //
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
|
|
struct psk_only_s {
|
|
|
|
|
|
|
|
enum v26_e v26_alt; // Which alternative when V.26.
|
|
|
|
|
|
|
|
float sin_table256[256]; // Precomputed sin table for speed.
|
|
|
|
|
|
|
|
|
|
|
|
// Optional band pass pre-filter before phase detector.
|
|
|
|
|
|
|
|
// TODO? put back into common section?
|
|
|
|
// TODO? Why was I thinking that?
|
|
|
|
|
|
|
|
int use_prefilter; // True to enable it.
|
|
|
|
|
|
|
|
float prefilter_baud; // Cutoff frequencies, as fraction of baud rate, beyond tones used.
|
|
|
|
// In the case of PSK, we use only a single tone of 1800 Hz.
|
|
|
|
// If we were using 2400 bps (= 1200 baud), this would be
|
|
|
|
// the fraction of 1200 for the cutoff below and above 1800.
|
|
|
|
|
|
|
|
|
|
|
|
float pre_filter_width_sym; /* Length in number of symbol times. */
|
|
|
|
|
|
|
|
int pre_filter_taps; /* Size of pre filter, in audio samples. */
|
|
|
|
|
|
|
|
bp_window_t pre_window;
|
|
|
|
|
|
|
|
float audio_in[MAX_FILTER_SIZE];
|
|
|
|
float pre_filter[MAX_FILTER_SIZE];
|
|
|
|
|
|
|
|
// Use local oscillator or correlate with previous sample.
|
|
|
|
|
|
|
|
int psk_use_lo; /* Use local oscillator rather than self correlation. */
|
|
|
|
|
|
|
|
unsigned int lo_step; /* How much to advance the local oscillator */
|
|
|
|
/* phase for each audio sample. */
|
|
|
|
|
|
|
|
unsigned int lo_phase; /* Local oscillator phase accumulator for PSK. */
|
|
|
|
|
|
|
|
// After mixing with LO before low pass filter.
|
|
|
|
|
|
|
|
float I_raw[MAX_FILTER_SIZE]; // signal * LO cos.
|
|
|
|
float Q_raw[MAX_FILTER_SIZE]; // signal * LO sin.
|
|
|
|
|
|
|
|
// Number of delay line taps into previous symbol.
|
|
|
|
// They are one symbol period and + or - 45 degrees of the carrier frequency.
|
|
|
|
|
|
|
|
int boffs; /* symbol length based on sample rate and baud. */
|
|
|
|
int coffs; /* to get cos component of previous symbol. */
|
|
|
|
int soffs; /* to get sin component of previous symbol. */
|
|
|
|
|
|
|
|
float delay_line_width_sym;
|
|
|
|
int delay_line_taps; // In audio samples.
|
|
|
|
|
|
|
|
float delay_line[MAX_FILTER_SIZE];
|
|
|
|
|
|
|
|
// Low pass filter Second is frequency as ratio to baud rate for FIR.
|
|
|
|
|
|
|
|
// TODO? put back into common section?
|
|
|
|
// TODO? What are the tradeoffs?
|
|
|
|
float lpf_baud; /* Cutoff frequency as fraction of baud. */
|
|
|
|
/* Intuitively we'd expect this to be somewhere */
|
|
|
|
/* in the range of 0.5 to 1. */
|
|
|
|
|
|
|
|
float lp_filter_width_sym; /* Length in number of symbol times. */
|
|
|
|
|
|
|
|
int lp_filter_taps; /* Size of Low Pass filter, in audio samples (i.e. filter taps). */
|
|
|
|
|
|
|
|
bp_window_t lp_window;
|
|
|
|
|
|
|
|
float lp_filter[MAX_FILTER_SIZE];
|
|
|
|
|
|
|
|
} psk;
|
|
|
|
|
|
|
|
} u; // end of union for different demodulator types.
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/*-------------------------------------------------------------------
|
|
|
|
*
|
|
|
|
* Name: pll_dcd_signal_transition2
|
|
|
|
* dcd_each_symbol2
|
|
|
|
*
|
|
|
|
* Purpose: New DCD strategy for 1.6.
|
|
|
|
*
|
|
|
|
* Inputs: D Pointer to demodulator state.
|
|
|
|
*
|
|
|
|
* chan Radio channel: 0 to MAX_CHANS - 1
|
|
|
|
*
|
|
|
|
* subchan Which of multiple demodulators: 0 to MAX_SUBCHANS - 1
|
|
|
|
*
|
|
|
|
* slice Slicer number: 0 to MAX_SLICERS - 1.
|
|
|
|
*
|
|
|
|
* dpll_phase Signed 32 bit counter for DPLL phase.
|
|
|
|
* Wraparound is where data is sampled.
|
|
|
|
* Ideally transitions would occur close to 0.
|
|
|
|
*
|
|
|
|
* Output: D->slicer[slice].data_detect - true when PLL is locked to incoming signal.
|
|
|
|
*
|
|
|
|
* Description: From the beginning, DCD was based on finding several flag octets
|
|
|
|
* in a row and dropping when eight bits with no transitions.
|
|
|
|
* It was less than ideal but we limped along with it all these years.
|
|
|
|
* This fell apart when FX.25 came along and a couple of the
|
|
|
|
* correlation tags have eight "1" bits in a row.
|
|
|
|
*
|
|
|
|
* Our new strategy is to keep a running score of how well demodulator
|
|
|
|
* output transitions match to where expected.
|
|
|
|
*
|
|
|
|
*--------------------------------------------------------------------*/
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// These are good for 1200 bps AFSK.
|
|
|
|
// Might want to override for other modems.
|
|
|
|
|
|
|
|
#ifndef DCD_THRESH_ON
|
|
|
|
#define DCD_THRESH_ON 30 // Hysteresis: Can miss 2 out of 32 for detecting lock.
|
|
|
|
// 31 is best for TNC Test CD. 30 almost as good.
|
|
|
|
// 30 better for 1200 regression test.
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifndef DCD_THRESH_OFF
|
|
|
|
#define DCD_THRESH_OFF 6 // Might want a little more fine tuning.
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifndef DCD_GOOD_WIDTH
|
|
|
|
#define DCD_GOOD_WIDTH 512 // No more than 1024!!!
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
inline static void pll_dcd_signal_transition2(struct demodulator_state_s *D, int slice, int dpll_phase)
|
|
|
|
{
|
|
|
|
if (dpll_phase > -DCD_GOOD_WIDTH * 1024 * 1024 && dpll_phase < DCD_GOOD_WIDTH * 1024 * 1024) {
|
|
|
|
D->slicer[slice].good_flag = 1;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
D->slicer[slice].bad_flag = 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
inline static void pll_dcd_each_symbol2(struct demodulator_state_s *D, int chan, int subchan, int slice)
|
|
|
|
{
|
|
|
|
D->slicer[slice].good_hist <<= 1;
|
|
|
|
D->slicer[slice].good_hist |= D->slicer[slice].good_flag;
|
|
|
|
D->slicer[slice].good_flag = 0;
|
|
|
|
|
|
|
|
D->slicer[slice].bad_hist <<= 1;
|
|
|
|
D->slicer[slice].bad_hist |= D->slicer[slice].bad_flag;
|
|
|
|
D->slicer[slice].bad_flag = 0;
|
|
|
|
|
|
|
|
D->slicer[slice].score <<= 1;
|
|
|
|
// 2 is to detect 'flag' patterns with 2 transitions per octet.
|
|
|
|
D->slicer[slice].score |= (signed)__builtin_popcount(D->slicer[slice].good_hist)
|
|
|
|
- (signed)__builtin_popcount(D->slicer[slice].bad_hist) >= 2;
|
|
|
|
|
|
|
|
int s = __builtin_popcount(D->slicer[slice].score);
|
|
|
|
if (s >= DCD_THRESH_ON) {
|
|
|
|
if (D->slicer[slice].data_detect == 0) {
|
|
|
|
D->slicer[slice].data_detect = 1;
|
|
|
|
dcd_change(chan, subchan, slice, D->slicer[slice].data_detect);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (s <= DCD_THRESH_OFF) {
|
|
|
|
if (D->slicer[slice].data_detect != 0) {
|
|
|
|
D->slicer[slice].data_detect = 0;
|
|
|
|
dcd_change(chan, subchan, slice, D->slicer[slice].data_detect);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Provided elsewhere to process a complete frame. */
|
|
|
|
|
|
|
|
//void process_rec_frame (int chan, unsigned char *fbuf, int flen, int level);
|
|
|
|
|
|
|
|
|
|
|
|
/* Is HLDC decoder is currently gathering bits into a frame? */
|
|
|
|
/* Similar to, but not exactly the same as, data carrier detect. */
|
|
|
|
/* We use this to influence the PLL inertia. */
|
|
|
|
|
|
|
|
int hdlc_rec_gathering(int chan, int subchan, int slice);
|
|
|
|
|
|
|
|
/* Transmit needs to know when someone else is transmitting. */
|
|
|
|
|
|
|
|
void dcd_change(int chan, int subchan, int slice, int state);
|
|
|
|
|
|
|
|
int hdlc_rec_data_detect_any(int chan);
|
|
|
|
|
|
|
|
|
|
|
|
#define FASTER13 1 // Don't pack 8 samples per byte.
|
|
|
|
|
|
|
|
|
|
|
|
//typedef short slice_t;
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Maximum size (in bytes) of an AX.25 frame including the 2 octet FCS.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#define MAX_FRAME_LEN ((AX25_MAX_PACKET_LEN) + 2)
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Maximum number of bits in AX.25 frame excluding the flags.
|
|
|
|
* Adequate for extreme case of bit stuffing after every 5 bits
|
|
|
|
* which could never happen.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#define MAX_NUM_BITS (MAX_FRAME_LEN * 8 * 6 / 5)
|
|
|
|
|
|
|
|
typedef struct rrbb_s {
|
|
|
|
int magic1;
|
|
|
|
struct rrbb_s* nextp; /* Next pointer to maintain a queue. */
|
|
|
|
|
|
|
|
int chan; /* Radio channel from which it was received. */
|
|
|
|
int subchan; /* Which modem when more than one per channel. */
|
|
|
|
int slice; /* Which slicer. */
|
|
|
|
|
|
|
|
alevel_t alevel; /* Received audio level at time of frame capture. */
|
|
|
|
unsigned int len; /* Current number of samples in array. */
|
|
|
|
|
|
|
|
int is_scrambled; /* Is data scrambled G3RUH / K9NG style? */
|
|
|
|
int descram_state; /* Descrambler state before first data bit of frame. */
|
|
|
|
int prev_descram; /* Previous descrambled bit. */
|
|
|
|
|
|
|
|
unsigned char fdata[MAX_NUM_BITS];
|
|
|
|
|
|
|
|
int magic2;
|
|
|
|
} *rrbb_t;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
rrbb_t rrbb_new(int chan, int subchan, int slice, int is_scrambled, int descram_state, int prev_descram);
|
|
|
|
|
|
|
|
void rrbb_clear(rrbb_t b, int is_scrambled, int descram_state, int prev_descram);
|
|
|
|
|
|
|
|
|
|
|
|
static inline /*__attribute__((always_inline))*/ void rrbb_append_bit(rrbb_t b, const unsigned char val)
|
|
|
|
{
|
|
|
|
if (b->len >= MAX_NUM_BITS) {
|
|
|
|
return; /* Silently discard if full. */
|
|
|
|
}
|
|
|
|
b->fdata[b->len] = val;
|
|
|
|
b->len++;
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline /*__attribute__((always_inline))*/ unsigned char rrbb_get_bit(const rrbb_t b, const int ind)
|
|
|
|
{
|
|
|
|
return (b->fdata[ind]);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void rrbb_chop8(rrbb_t b);
|
|
|
|
|
|
|
|
int rrbb_get_len(rrbb_t b);
|
|
|
|
|
|
|
|
//void rrbb_flip_bit (rrbb_t b, unsigned int ind);
|
|
|
|
|
|
|
|
void rrbb_delete(rrbb_t b);
|
|
|
|
|
|
|
|
void rrbb_set_nextp(rrbb_t b, rrbb_t np);
|
|
|
|
rrbb_t rrbb_get_nextp(rrbb_t b);
|
|
|
|
|
|
|
|
int rrbb_get_chan(rrbb_t b);
|
|
|
|
int rrbb_get_subchan(rrbb_t b);
|
|
|
|
int rrbb_get_slice(rrbb_t b);
|
|
|
|
|
|
|
|
void rrbb_set_audio_level(rrbb_t b, alevel_t alevel);
|
|
|
|
alevel_t rrbb_get_audio_level(rrbb_t b);
|
|
|
|
|
|
|
|
int rrbb_get_is_scrambled(rrbb_t b);
|
|
|
|
int rrbb_get_descram_state(rrbb_t b);
|
|
|
|
int rrbb_get_prev_descram(rrbb_t b);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void hdlc_rec2_init(struct audio_s *audio_config_p);
|
|
|
|
|
|
|
|
void hdlc_rec2_block(rrbb_t block);
|
|
|
|
|
|
|
|
int hdlc_rec2_try_to_fix_later(rrbb_t block, int chan, int subchan, int slice, alevel_t alevel);
|
|
|
|
|
|
|
|
/* Provided by the top level application to process a complete frame. */
|
|
|
|
|
|
|
|
void app_process_rec_packet(int chan, int subchan, int slice, packet_t pp, alevel_t level, int is_fx25, retry_t retries, char *spectrum);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
int gen_tone_init(struct audio_s *pp, int amp, int gen_packets);
|
|
|
|
|
|
|
|
|
|
|
|
//int gen_tone_open (int nchan, int sample_rate, int bit_rate, int f1, int f2, int amp, char *fname);
|
|
|
|
|
|
|
|
//int gen_tone_open_fd (int nchan, int sample_rate, int bit_rate, int f1, int f2, int amp, int fd) ;
|
|
|
|
|
|
|
|
//int gen_tone_close (void);
|
|
|
|
|
2024-04-13 17:48:58 +01:00
|
|
|
void tone_gen_put_bit(int chan, int dat, int scramble);
|
2023-09-12 21:38:15 +01:00
|
|
|
|
|
|
|
void gen_tone_put_sample(int chan, int a, int sam);
|
|
|
|
|
|
|
|
enum dw_color_e {
|
|
|
|
DW_COLOR_INFO, /* black */
|
|
|
|
DW_COLOR_ERROR, /* red */
|
|
|
|
DW_COLOR_REC, /* green */
|
|
|
|
DW_COLOR_DECODED, /* blue */
|
|
|
|
DW_COLOR_XMIT, /* magenta */
|
|
|
|
DW_COLOR_DEBUG /* dark_green */
|
|
|
|
};
|
|
|
|
|
|
|
|
typedef enum dw_color_e dw_color_t;
|
|
|
|
|
|
|
|
|
|
|
|
void text_color_init(int enable_color);
|
|
|
|
void text_color_set(dw_color_t c);
|
|
|
|
void text_color_term(void);
|
|
|
|
|
|
|
|
|
|
|
|
/* Degree symbol. */
|
|
|
|
|
|
|
|
#if __WIN32__
|
|
|
|
|
|
|
|
//#define CH_DEGREE "\xc2\xb0" /* UTF-8. */
|
|
|
|
|
|
|
|
#define CH_DEGREE " "
|
|
|
|
|
|
|
|
|
|
|
|
#else
|
|
|
|
|
|
|
|
/* Maybe we could change this based on LANG environment variable. */
|
|
|
|
|
|
|
|
//#define CH_DEGREE "\xc2\xb0" /* UTF-8. */
|
|
|
|
|
|
|
|
#define CH_DEGREE " "
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/* demod_9600.h */
|
|
|
|
|
|
|
|
|
|
|
|
void demod_9600_init(enum modem_t modem_type, int original_sample_rate, int upsample, int baud, struct demodulator_state_s *D);
|
|
|
|
|
|
|
|
void demod_9600_process_sample(int chan, int sam, int upsample, struct demodulator_state_s *D);
|
|
|
|
|
|
|
|
|
|
|
|
/* Undo data scrambling for 9600 baud. */
|
|
|
|
|
|
|
|
static inline int descramble(int in, int *state)
|
|
|
|
{
|
|
|
|
int out;
|
|
|
|
|
|
|
|
out = (in ^ (*state >> 16) ^ (*state >> 11)) & 1;
|
|
|
|
*state = (*state << 1) | (in & 1);
|
|
|
|
return (out);
|
|
|
|
}
|