2023-09-04 19:06:44 +01:00
|
|
|
/*
|
|
|
|
Copyright (C) 2019-2020 Andrei Kopanchuk UZ7HO
|
|
|
|
|
|
|
|
This file is part of QtSoundModem
|
|
|
|
|
|
|
|
QtSoundModem 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 3 of the License, or
|
|
|
|
(at your option) any later version.
|
|
|
|
|
|
|
|
QtSoundModem 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 QtSoundModem. If not, see http://www.gnu.org/licenses
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
// UZ7HO Soundmodem Port by John Wiseman G8BPQ
|
|
|
|
|
|
|
|
|
|
|
|
#include "UZ7HOStuff.h"
|
|
|
|
|
|
|
|
// I assume this modulates (and sends?} frames
|
|
|
|
|
2024-10-29 22:45:21 +00:00
|
|
|
void InitBuffers();
|
|
|
|
void EncodeRS(Byte * xData, Byte * xEncoded);
|
|
|
|
void scrambler(UCHAR * in_buf, int Len);
|
|
|
|
void fx25_encode_rs(Byte * data, Byte *parity, int pad, int rs_size);
|
|
|
|
int fx25_decode_rs(Byte * data, int * eras_pos, int no_eras, int pad, int rs_size);
|
|
|
|
int il2p_get_new_bit_tail(UCHAR snd_ch, UCHAR bit);
|
|
|
|
int il2p_get_new_bit(int snd_ch, Byte bit);
|
|
|
|
int ARDOPSendToCard(int Chan, int Len);
|
|
|
|
void Flush();
|
|
|
|
void SampleSink(int LR, short Sample);
|
|
|
|
|
2023-09-04 19:06:44 +01:00
|
|
|
int RSEncode(UCHAR * bytToRS, UCHAR * RSBytes, int DataLen, int RSLen);
|
|
|
|
|
|
|
|
//unit ax25_mod;
|
|
|
|
|
|
|
|
//interface
|
|
|
|
|
|
|
|
//uses sysutils,classes,math
|
|
|
|
|
|
|
|
extern int SampleNo;
|
|
|
|
|
|
|
|
extern BOOL KISSServ;
|
|
|
|
|
|
|
|
extern TStringList KISS_acked[];
|
|
|
|
extern TStringList KISS_iacked[];
|
|
|
|
|
|
|
|
extern UCHAR modem_mode[];
|
|
|
|
|
|
|
|
#define sbc 175
|
|
|
|
|
|
|
|
extern single ch_offset[4];
|
2023-09-12 21:38:15 +01:00
|
|
|
int Continuation[4] = { 0, 0, 0, 0 }; // Sending 2nd or more packet of burst
|
2023-09-04 19:06:44 +01:00
|
|
|
|
|
|
|
#define COS45 0.70710676908493f
|
|
|
|
|
|
|
|
#define TX_SILENCE 0
|
|
|
|
#define TX_DELAY 1
|
|
|
|
#define TX_TAIL 2
|
|
|
|
#define TX_NO_DATA 3
|
|
|
|
#define TX_FRAME 4
|
|
|
|
#define TX_WAIT_BPF 5
|
|
|
|
|
|
|
|
|
|
|
|
#define TX_BIT0 0
|
|
|
|
#define TX_BIT1 1
|
|
|
|
#define FRAME_EMPTY 0
|
|
|
|
#define FRAME_FULL 1
|
|
|
|
#define FRAME_NO_FRAME 2
|
|
|
|
#define FRAME_NEW_FRAME 3
|
|
|
|
#define BYTE_EMPTY 0
|
|
|
|
#define BYTE_FULL 1
|
|
|
|
|
|
|
|
|
|
|
|
UCHAR gray_8PSK[8] = {7,0,6,5,2,1,3,4}; // ?? was 1::8
|
|
|
|
|
|
|
|
UCHAR gray_PI4QPSK[4] = {3,1,5,7};
|
|
|
|
|
|
|
|
|
|
|
|
float audio_buf[5][32768]; // [1..4,0..32767]
|
|
|
|
float tx_src_BPF_buf[5][32768];
|
|
|
|
float tx_BPF_buf[5][32768];
|
|
|
|
float tx_prev_BPF_buf[5][32768];
|
|
|
|
float tx_BPF_core[5][32768];
|
|
|
|
|
|
|
|
long tx_delay_cnt[5] = {0}; // : array[1..4] of longword=(0,0,0,0};
|
|
|
|
long tx_tail_cnt[5] = {0};
|
|
|
|
|
|
|
|
int tx_hitoneraisedb[5] = {0}; // : array[1..4] of integer=(0,0,0,0};
|
|
|
|
float tx_hitoneraise[5] = {0}; // : array[1..4] of single=(0,0,0,0};
|
|
|
|
float tx_freq[5] = { 1000, 1000, 1000, 1000, 1000}; // : array[1..4] of single=(1000,1000,1000,1000};
|
|
|
|
float tx_shift[5] = { 200, 200, 200, 200, 200}; // : array[1..4] of single=(200,200,200,200};
|
|
|
|
float tx_bit_mod[5] = {1, 1, 1, 1, 1}; // : array[1..4] of single=(1,1,1,1};
|
|
|
|
float tx_osc[5] = {0}; // : array[1..4] of single=(0,0,0,0};
|
|
|
|
float tx_bit_osc[5] = {0}; // : array[1..4] of single=(0,0,0,0};
|
|
|
|
unsigned short txbpf[5] = { 400, 400, 400, 400, 400}; // : array[1..4] of word=(400,400,400,400};
|
|
|
|
unsigned short tx_BPF_tap[5] = { 256, 256, 256, 256, 256}; // : array[1..4] of word=(256,256,256,256};
|
2023-09-12 21:38:15 +01:00
|
|
|
unsigned short tx_baudrate[5] = { 300, 300, 300, 300, 300 }; // : array[1..4] of word=(300,300,300,300};
|
|
|
|
unsigned short tx_bitrate[5] = { 300, 300, 300, 300, 300 }; // : array[1..4] of word=(300,300,300,300};
|
2023-09-04 19:06:44 +01:00
|
|
|
unsigned short tx_BPF_timer[5] = {0}; // : array[1..4] of word=(0,0,0,0};
|
|
|
|
UCHAR tx_pol[5] = {0}; // : array[1..4] of byte=(0,0,0,0};
|
|
|
|
UCHAR tx_last_pol[5] = {0}; // : array[1..4] of byte=(0,0,0,0};
|
|
|
|
UCHAR tx_last_diddle[5] = {0}; // : array[1..4] of byte=(0,0,0,0};
|
|
|
|
UCHAR tx_flag_cnt[5] = {0}; // : array[1..4] of byte=(0,0,0,0};
|
|
|
|
UCHAR tx_frame_status[5] = {0}; // : array[1..4] of byte=(0,0,0,0};
|
|
|
|
UCHAR tx_byte_status[5] = {0}; // : array[1..4] of byte=(0,0,0,0};
|
|
|
|
UCHAR tx_status[5] = {0}; // : array[1..4] of byte=(0,0,0,0};
|
|
|
|
UCHAR tx_bit_stuff_cnt[5] = {0}; // : array[1..4] of byte=(0,0,0,0};
|
|
|
|
UCHAR tx_bit_cnt[5] = {0}; // : array[1..4] of byte=(0,0,0,0};
|
|
|
|
UCHAR tx_last_bit[5] = {0}; // : array[1..4] of byte=(0,0,0,0};
|
|
|
|
UCHAR tx_bit_stream[5] = {0}; // : array[1..4] of byte=(0,0,0,0};
|
|
|
|
|
|
|
|
UCHAR tx_8PSK[5] = {0}; // : array[1..4] of byte=(0,0,0,0};
|
|
|
|
UCHAR tx_QPSK[5] = {0}; // : array[1..4] of byte=(0,0,0,0};
|
|
|
|
|
|
|
|
float tx_I_mod[5] = {1, 1, 1, 1, 1}; // : array[1..4] of single=(1,1,1,1};
|
|
|
|
float tx_Q_mod[5] = {1, 1, 1, 1, 1}; // : array[1..4] of single=(1,1,1,1};
|
|
|
|
float tx_QPSK_avg_I[5] = {0}; // : array[1..4] of single=(0,0,0,0};
|
|
|
|
float tx_QPSK_avg_Q[5] = {0}; // : array[1..4] of single=(0,0,0,0};
|
|
|
|
float tx_QPSK_df_I[5] = {0}; // : array[1..4] of single=(0,0,0,0};
|
|
|
|
float tx_QPSK_df_Q [5] = {0}; // : array[1..4] of single=(0,0,0,0};
|
|
|
|
float tx_QPSK_I[5] = {0}; // : array[1..4] of single=(0,0,0,0};
|
|
|
|
float tx_QPSK_Q[5] = {0}; // : array[1..4] of single=(0,0,0,0};
|
|
|
|
float tx_QPSK_old_I[5] = {0}; // : array[1..4] of single=(0,0,0,0};
|
|
|
|
float tx_QPSK_old_Q[5] = {0}; // : array[1..4] of single=(0,0,0,0};
|
|
|
|
float tx_8PSK_avg_I[5] = {0}; // : array[1..4] of single=(0,0,0,0};
|
|
|
|
float tx_8PSK_avg_Q[5] = {0}; // : array[1..4] of single=(0,0,0,0};
|
|
|
|
float tx_8PSK_df_I[5] = {0}; // : array[1..4] of single=(0,0,0,0};
|
|
|
|
float tx_8PSK_df_Q[5] = {0}; // : array[1..4] of single=(0,0,0,0};
|
|
|
|
float tx_8PSK_I[5] = {0}; // : array[1..4] of single=(0,0,0,0};
|
|
|
|
float tx_8PSK_Q[5] = {0}; // : array[1..4] of single=(0,0,0,0};
|
|
|
|
float tx_8PSK_old_I[5] = {0}; // : array[1..4] of single=(0,0,0,0};
|
|
|
|
float tx_8PSK_old_Q[5] = {0}; // : array[1..4] of single=(0,0,0,0};
|
|
|
|
|
|
|
|
float tx_osc1[5] = {0}; // : array[1..4] of single=(0,0,0,0};
|
|
|
|
float tx_osc2[5] = {0}; // : array[1..4] of single=(0,0,0,0};
|
|
|
|
float tx_osc3[5] = {0}; // : array[1..4] of single=(0,0,0,0};
|
|
|
|
float tx_osc4[5] = {0}; // : array[1..4] of single=(0,0,0,0};
|
|
|
|
short tx_inv1[5] = {1, 1, 1, 1, 1}; // : array[1..4] of shortint=(1,1,1,1};
|
|
|
|
short tx_inv2[5] = {1, 1, 1, 1, 1}; // : array[1..4] of shortint=(1,1,1,1};
|
|
|
|
short tx_inv3[5] = {1, 1, 1, 1, 1}; // : array[1..4] of shortint=(1,1,1,1};
|
|
|
|
short tx_inv4[5] = {1, 1, 1, 1, 1}; // : array[1..4] of shortint=(1,1,1,1};
|
|
|
|
short tx_old_inv1[5] = {1, 1, 1, 1, 1}; // : array[1..4] of shortint=(1,1,1,1};
|
|
|
|
short tx_old_inv2[5] = {1, 1, 1, 1, 1}; // : array[1..4] of shortint=(1,1,1,1};
|
|
|
|
short tx_old_inv3[5] = {1, 1, 1, 1, 1}; // : array[1..4] of shortint=(1,1,1,1};
|
|
|
|
short tx_old_inv4[5] = {1, 1, 1, 1, 1}; // : array[1..4] of shortint=(1,1,1,1};
|
|
|
|
float tx_bit1_mod[5] = {1, 1, 1, 1, 1}; // : array[1..4] of single=(1,1,1,1};
|
|
|
|
float tx_bit2_mod[5] = {1, 1, 1, 1, 1}; // : array[1..4] of single=(1,1,1,1};
|
|
|
|
float tx_bit3_mod[5] = {1, 1, 1, 1, 1}; // : array[1..4] of single=(1,1,1,1};
|
|
|
|
float tx_bit4_mod[5] = {1, 1, 1, 1, 1}; // : array[1..4] of single=(1,1,1,1};
|
|
|
|
UINT tx_viterbi[5] = {0}; // : array[1..4] of word=(0,0,0,0};
|
|
|
|
UCHAR tx_intv_tbl[5][4]; // : array[1..4,0..3] of byte;
|
|
|
|
|
|
|
|
short tx_inv[5] = {1, 1, 1, 1, 1}; // : array[1..4] of shortint=(1,1,1,1};
|
|
|
|
BOOL tx_change_phase[5] = {0}; // : array[1..4] of boolean=(FALSE,FALSE,FALSE,FALSE};
|
|
|
|
BOOL tx_bs_bit[5] = {0}; // : array[1..4] of boolean=(FALSE,FALSE,FALSE,FALSE};
|
|
|
|
|
|
|
|
string * tx_data[5] = {0}; // : array[1..4] of string=('','','',''};
|
|
|
|
int tx_data_len[5] = {0};
|
|
|
|
|
|
|
|
int tx_fx25_size[4] = { 0, 0, 0, 0 };
|
|
|
|
int tx_fx25_size_cnt[4] = { 0, 0, 0, 0 };
|
|
|
|
int tx_fx25_mode[4] = { 0, 0, 0, 0 };
|
|
|
|
|
|
|
|
|
|
|
|
// uses sm_main,ax25,ax25_agw,ax25_demod,rsunit;
|
|
|
|
|
|
|
|
UCHAR tx_nrzi(UCHAR snd_ch, UCHAR bit)
|
|
|
|
{
|
|
|
|
// Debugprintf("Before NRZI %d", bit);
|
|
|
|
|
|
|
|
if (bit == TX_BIT0)
|
|
|
|
{
|
|
|
|
// Zero so switch bit
|
|
|
|
|
|
|
|
tx_last_bit[snd_ch] ^= 1;
|
|
|
|
}
|
|
|
|
return tx_last_bit[snd_ch];
|
|
|
|
}
|
|
|
|
|
|
|
|
BOOL tx_bit_stuffing(UCHAR snd_ch, UCHAR bit)
|
|
|
|
{
|
|
|
|
// result = FALSE;
|
|
|
|
// if bit=TX_BIT1 then inc(tx_bit_stuff_cnt[snd_ch]};
|
|
|
|
// if bit=TX_BIT0 then tx_bit_stuff_cnt[snd_ch] = 0;
|
|
|
|
// if tx_bit_stuff_cnt[snd_ch]=5 then begin tx_bit_stuff_cnt[snd_ch] = 0; result = TRUE; end;
|
|
|
|
//end;
|
|
|
|
|
|
|
|
if (bit == TX_BIT1)
|
|
|
|
tx_bit_stuff_cnt[snd_ch]++;
|
|
|
|
|
|
|
|
if (bit == TX_BIT0)
|
|
|
|
tx_bit_stuff_cnt[snd_ch] = 0;
|
|
|
|
|
|
|
|
if (tx_bit_stuff_cnt[snd_ch] == 5)
|
|
|
|
{
|
|
|
|
tx_bit_stuff_cnt[snd_ch] = 0;
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void interleave(char *s, int len)
|
|
|
|
{
|
|
|
|
// var
|
|
|
|
// data: string;
|
|
|
|
// i,k,len: word;
|
|
|
|
// nr_blocks: word;
|
|
|
|
//begin{
|
|
|
|
// data = '';
|
|
|
|
// len = length(s};
|
|
|
|
// if len>0 then nr_blocks = ((len-1} div 16}+1 else nr_blocks = 1;
|
|
|
|
// for i = 1 to 16 do
|
|
|
|
// for k = 0 to nr_blocks-1 do
|
|
|
|
// if (i+k*16}<=len then data = data+s[i+k*16];
|
|
|
|
// result = data;
|
|
|
|
//end;
|
|
|
|
|
|
|
|
char data[1024];
|
|
|
|
|
|
|
|
UINT i,k;
|
|
|
|
UINT nr_blocks;
|
|
|
|
int n = 0;
|
|
|
|
|
|
|
|
if (len > 0)
|
|
|
|
nr_blocks = ((len - 1) / 16) + 1;
|
|
|
|
else
|
|
|
|
nr_blocks = 1;
|
|
|
|
|
|
|
|
for (i = 0; i < 16; i++)
|
|
|
|
{
|
|
|
|
for (k = 0; k < nr_blocks; k++)
|
|
|
|
{
|
|
|
|
if ((i + k * 16) <= len)
|
|
|
|
data[n++] = s[i + k * 16];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
memcpy(s, data, len);
|
|
|
|
}
|
|
|
|
|
|
|
|
//procedure get_new_frame(snd_ch: byte; var frame_stream: TStringList};
|
|
|
|
//var
|
|
|
|
// header,line,temp: string;
|
|
|
|
// len,i,size: word;
|
|
|
|
// crc: word;
|
|
|
|
//begin
|
|
|
|
|
|
|
|
void get_new_frame(UCHAR snd_ch, TStringList * frame_stream)
|
|
|
|
{
|
|
|
|
UCHAR header[256];
|
|
|
|
UCHAR line[1024];
|
|
|
|
|
|
|
|
int LineLen;
|
|
|
|
|
|
|
|
string ** Items;
|
|
|
|
|
|
|
|
string * myTemp;
|
|
|
|
|
|
|
|
UCHAR temp[1024];
|
|
|
|
|
|
|
|
UINT len, i, size;
|
|
|
|
UINT crc;
|
|
|
|
|
|
|
|
tx_bs_bit[snd_ch] = FALSE;
|
|
|
|
tx_bit_cnt[snd_ch] = 0;
|
|
|
|
tx_flag_cnt[snd_ch] = 0;
|
|
|
|
tx_bit_stuff_cnt[snd_ch] = 0;
|
|
|
|
tx_bit_stream[snd_ch] = FRAME_FLAG;
|
|
|
|
tx_frame_status[snd_ch] = FRAME_NEW_FRAME;
|
|
|
|
tx_byte_status[snd_ch] = BYTE_EMPTY;
|
|
|
|
|
|
|
|
if (frame_stream->Count == 0)
|
|
|
|
{
|
|
|
|
tx_frame_status[snd_ch] = FRAME_NO_FRAME;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// We now pass control byte and ack bytes on front and pointer to socket on end if ackmode
|
|
|
|
|
|
|
|
myTemp = Strings(frame_stream, 0); // get message
|
|
|
|
|
|
|
|
if ((myTemp->Data[0] & 0x0f) == 12) // ACKMODE
|
|
|
|
{
|
|
|
|
// Save copy then copy data up 3 bytes
|
|
|
|
|
|
|
|
Add(&KISS_acked[snd_ch], duplicateString(myTemp));
|
|
|
|
|
|
|
|
mydelete(myTemp, 0, 3);
|
|
|
|
myTemp->Length -= sizeof(void *);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// Just remove control
|
|
|
|
|
|
|
|
mydelete(myTemp, 0, 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
tx_data[snd_ch] = duplicateString(myTemp); // so can free original below
|
|
|
|
|
|
|
|
Delete(frame_stream, 0); // This will invalidate temp
|
|
|
|
|
|
|
|
AGW_AX25_frame_analiz(snd_ch, FALSE, tx_data[snd_ch]);
|
|
|
|
|
|
|
|
put_frame(snd_ch, tx_data[snd_ch], "", TRUE, FALSE);
|
|
|
|
|
|
|
|
if (tx_data[snd_ch]->Length == 0 || modem_mode[snd_ch] != MODE_MPSK)
|
|
|
|
return;
|
|
|
|
|
|
|
|
// Reformat MPSK Data
|
|
|
|
|
|
|
|
//Take data 8 bytes at a time and add 8 bytes of RS data
|
|
|
|
|
|
|
|
LineLen = 0;
|
|
|
|
|
|
|
|
while (tx_data[snd_ch]->Length > 0)
|
|
|
|
{
|
|
|
|
size = tx_data[snd_ch]->Length;
|
|
|
|
|
|
|
|
if (size > 8)
|
|
|
|
size = 8;
|
|
|
|
|
|
|
|
memcpy(temp, tx_data[snd_ch]->Data, size);
|
|
|
|
|
|
|
|
// Delete the chars from tx_data
|
|
|
|
|
|
|
|
mydelete(tx_data[snd_ch], 0, 8);
|
|
|
|
|
|
|
|
memset(xData, 0, sizeof(xData));
|
|
|
|
memset(xEncoded, 0, sizeof(xEncoded));
|
|
|
|
|
|
|
|
memcpy(xData, temp, size);
|
|
|
|
|
|
|
|
InitBuffers();
|
|
|
|
EncodeRS(xData, xEncoded); // This puts the 8 RS bytes in xEncoded
|
|
|
|
|
|
|
|
memcpy(&line[LineLen], xData, size);
|
|
|
|
memcpy(&line[LineLen + size], xEncoded, MaxErrors * 2);
|
|
|
|
|
|
|
|
LineLen += size + (MaxErrors * 2);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
len = LineLen;
|
|
|
|
|
|
|
|
interleave(line, LineLen);
|
|
|
|
scrambler(line, LineLen);
|
|
|
|
|
|
|
|
header[0] = 0x7e;
|
|
|
|
header[1] = 0x7e;
|
|
|
|
header[2] = len >> 8;
|
|
|
|
header[3] = len;
|
|
|
|
|
|
|
|
crc = get_fcs(header, 4);
|
|
|
|
|
|
|
|
header[4] = crc >> 8;
|
|
|
|
header[5] = crc;
|
|
|
|
|
|
|
|
memset(xData, 0, sizeof(xData));
|
|
|
|
memset(xEncoded, 0, sizeof(xEncoded));
|
|
|
|
memmove(xData, header, 6);
|
|
|
|
|
|
|
|
|
|
|
|
// RSEncode(xData, xEncoded, 6 + (MaxErrors * 2), MaxErrors * 2);
|
|
|
|
|
|
|
|
InitBuffers();
|
|
|
|
EncodeRS(xData, xEncoded);
|
|
|
|
|
|
|
|
fx25_encode_rs(xData, xEncoded, 0, 8);
|
|
|
|
|
|
|
|
|
|
|
|
// We should now have RS Encoded Header in xEncoded;
|
|
|
|
|
|
|
|
// I think we send encoded header then line
|
|
|
|
|
|
|
|
tx_data[snd_ch]->Length = 0;
|
|
|
|
|
|
|
|
stringAdd(tx_data[snd_ch], xData, 6);
|
|
|
|
stringAdd(tx_data[snd_ch], xEncoded, MaxErrors * 2);
|
|
|
|
stringAdd(tx_data[snd_ch], line, LineLen);
|
|
|
|
|
|
|
|
// For testing, descramble and de-interleve
|
|
|
|
|
|
|
|
scrambler(line, LineLen); // should look like interleaved
|
|
|
|
{
|
|
|
|
Byte unscrambled[1024];
|
|
|
|
int count, len;
|
|
|
|
int origlen;
|
|
|
|
|
|
|
|
len = LineLen;
|
|
|
|
count = (len + 15) / 16;
|
|
|
|
|
|
|
|
int j1, j2, j3, i, j;
|
|
|
|
|
|
|
|
j3 = 0;
|
|
|
|
|
|
|
|
for (j1 = 0; j1 < 16; j1++)
|
|
|
|
{
|
|
|
|
// Each char in block
|
|
|
|
|
|
|
|
for (j2 = 0; j2 < count; j2++)
|
|
|
|
{
|
|
|
|
// Blocks
|
|
|
|
|
|
|
|
unscrambled[j2 * 16 + j1] = line[j3];
|
|
|
|
j3++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Now remove RS (will check later)
|
|
|
|
|
|
|
|
i = 0;
|
|
|
|
j = 0;
|
|
|
|
|
|
|
|
while (j < len)
|
|
|
|
{
|
|
|
|
Byte line1[256];
|
|
|
|
int nErr, eras_pos = 0;
|
|
|
|
Byte rs_block[256];
|
|
|
|
|
|
|
|
memcpy(line1, &unscrambled[j], 16);
|
|
|
|
|
|
|
|
memset(xEncoded, 0, sizeof(xEncoded));
|
|
|
|
memset(xDecoded, 0, sizeof(xDecoded));
|
|
|
|
|
|
|
|
memcpy(xEncoded, &unscrambled[j], 16);
|
|
|
|
|
|
|
|
// nErr = DecodeRS(xEncoded, xDecoded);
|
|
|
|
|
|
|
|
memset(rs_block, 0, 255);
|
|
|
|
memcpy(rs_block, &unscrambled[j], 8);
|
|
|
|
memcpy(&rs_block[255 - 8], &unscrambled[j+8], 8);
|
|
|
|
|
|
|
|
nErr = fx25_decode_rs(rs_block, &eras_pos, 0, 0, 8);
|
|
|
|
|
|
|
|
|
|
|
|
// line1 = '';
|
|
|
|
// for j1 = MaxErrors * 2 to size - 1 do line1 = line1 + chr(xDecoded[j1]);
|
|
|
|
|
|
|
|
|
|
|
|
memcpy(&unscrambled[i], &unscrambled[j], 8);
|
|
|
|
i += 8;
|
|
|
|
j += 16;
|
|
|
|
}
|
|
|
|
|
|
|
|
j3 = j3;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
int get_new_bit(Byte snd_ch, Byte bit)
|
|
|
|
{
|
|
|
|
unsigned short len;
|
|
|
|
string * s;
|
|
|
|
|
|
|
|
if (tx_frame_status[snd_ch] == FRAME_FULL)
|
|
|
|
{
|
|
|
|
if (tx_byte_status[snd_ch] == BYTE_EMPTY)
|
|
|
|
{
|
|
|
|
len = tx_data[snd_ch]->Length;
|
|
|
|
|
|
|
|
if (len > 0)
|
|
|
|
{
|
|
|
|
s = tx_data[snd_ch];
|
|
|
|
tx_bit_stream[snd_ch] = (s->Data[0]);
|
|
|
|
tx_frame_status[snd_ch] = FRAME_FULL;
|
|
|
|
tx_byte_status[snd_ch] = BYTE_FULL;
|
|
|
|
tx_bit_cnt[snd_ch] = 0;
|
|
|
|
mydelete(tx_data[snd_ch], 0, 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
else tx_frame_status[snd_ch] = FRAME_EMPTY;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (tx_byte_status[snd_ch] == BYTE_FULL)
|
|
|
|
bit = tx_bit_stream[snd_ch] & TX_BIT1;
|
|
|
|
|
|
|
|
if (modem_mode[snd_ch] == MODE_MPSK)
|
|
|
|
{
|
|
|
|
tx_bit_cnt[snd_ch]++;
|
|
|
|
tx_bit_stream[snd_ch] = tx_bit_stream[snd_ch] >> 1;
|
|
|
|
if (tx_bit_cnt[snd_ch] >= 8)
|
|
|
|
tx_byte_status[snd_ch] = BYTE_EMPTY;
|
|
|
|
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (tx_bs_bit[snd_ch])
|
|
|
|
bit = TX_BIT0;
|
|
|
|
|
|
|
|
tx_bs_bit[snd_ch] = tx_bit_stuffing(snd_ch, bit);
|
|
|
|
|
|
|
|
if (!tx_bs_bit[snd_ch])
|
|
|
|
{
|
|
|
|
tx_bit_cnt[snd_ch]++;
|
|
|
|
tx_bit_stream[snd_ch] >>= 1;
|
|
|
|
if (tx_bit_cnt[snd_ch] >= 8 && !tx_bs_bit[snd_ch])
|
|
|
|
tx_byte_status[snd_ch] = BYTE_EMPTY;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (tx_frame_status[snd_ch] == FRAME_EMPTY)
|
|
|
|
get_new_frame(snd_ch, &all_frame_buf[snd_ch]);
|
|
|
|
|
|
|
|
if ((tx_frame_status[snd_ch] == FRAME_NEW_FRAME) || (tx_frame_status[snd_ch] == FRAME_NO_FRAME))
|
|
|
|
{
|
|
|
|
bit = tx_bit_stream[snd_ch] & TX_BIT1;
|
|
|
|
tx_flag_cnt[snd_ch]++;
|
|
|
|
tx_bit_stream[snd_ch] >>= 1;
|
|
|
|
|
|
|
|
if (tx_flag_cnt[snd_ch] == 8)
|
|
|
|
{
|
|
|
|
switch (tx_frame_status[snd_ch])
|
|
|
|
{
|
|
|
|
case FRAME_NEW_FRAME:
|
|
|
|
|
|
|
|
tx_frame_status[snd_ch] = FRAME_FULL;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case FRAME_NO_FRAME:
|
|
|
|
|
|
|
|
tx_tail_cnt[snd_ch] = 0;
|
|
|
|
tx_frame_status[snd_ch] = FRAME_EMPTY;
|
|
|
|
tx_status[snd_ch] = TX_TAIL;
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return bit;
|
|
|
|
}
|
|
|
|
|
|
|
|
////// FX.25 //////
|
|
|
|
|
|
|
|
|
|
|
|
void bit_to_fx25(Byte * tx_byte, Byte * bit_cnt, Byte bit, string * data, int * data_cnt)
|
|
|
|
{
|
|
|
|
*tx_byte = (*tx_byte >> 1) | (bit << 7);
|
|
|
|
(*bit_cnt)++;
|
|
|
|
|
|
|
|
if (*bit_cnt == 8)
|
|
|
|
{
|
|
|
|
stringAdd(data, tx_byte, 1);
|
|
|
|
*bit_cnt = 0;
|
|
|
|
}
|
|
|
|
(*data_cnt)++;
|
|
|
|
}
|
|
|
|
|
|
|
|
string * fill_fx25_data(int snd_ch, string * data)
|
|
|
|
{
|
|
|
|
#define nr_tags 5
|
|
|
|
|
|
|
|
string * result;
|
|
|
|
|
|
|
|
Byte rs_roots[nr_tags + 1] = { 16, 32, 64, 32, 16, 16 };
|
|
|
|
word rs_payload[nr_tags + 1] = { 1912, 1784, 1528, 1024, 512, 256 }; // 239, 233, 191, 128, 64, 32
|
|
|
|
|
|
|
|
unsigned long long rs_tag[nr_tags + 1] =
|
|
|
|
{
|
|
|
|
0xB74DB7DF8A532F3E, // 255 / 16 (239)
|
|
|
|
0x6E260B1AC5835FAE, // 255 / 32 (223)
|
|
|
|
0x3ADB0C13DEAE2836, // 255 / 64 (191)
|
|
|
|
0xFF94DC634F1CFF4E, // 160 / 32 (128)
|
|
|
|
0xC7DC0508F3D9B09E, // 80 / 16 (64)
|
|
|
|
0x8F056EB4369660EE // 48 / 16 (32)
|
|
|
|
};
|
|
|
|
|
|
|
|
// 0x26FF60A600CC8FDE) 144; = 16;
|
|
|
|
// 0x1EB7B9CDBC09C00E) 96; 32;
|
|
|
|
// 0xDBF869BD2DBB1776) 64;= 32;
|
|
|
|
// 0xAB69DB6A543188D6) 192; = 64;
|
|
|
|
// 0x4A4ABEC4A724B796) 128; = 64;
|
|
|
|
|
|
|
|
string * ax25_data = newString();
|
|
|
|
|
|
|
|
int i, ax25_size;
|
|
|
|
Byte a, bit, bit_cnt, bit_cnt1, bs, tx_byte;
|
|
|
|
Byte rs_id;
|
|
|
|
Byte rs_block[256], parity[256];
|
|
|
|
|
|
|
|
ax25_size = 0;
|
|
|
|
bs = 0;
|
|
|
|
tx_byte = 0;
|
|
|
|
bit_cnt = 0;
|
|
|
|
|
|
|
|
// Load start flag
|
|
|
|
a = FRAME_FLAG;
|
|
|
|
|
|
|
|
for (i = 0; i < 8; i++)
|
|
|
|
{
|
|
|
|
bit = a & 1;
|
|
|
|
a = a >> 1;
|
|
|
|
bit_to_fx25(&tx_byte, &bit_cnt, bit, ax25_data, &ax25_size);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Load body
|
|
|
|
for (i = 0; i < data->Length; i++)
|
|
|
|
{
|
|
|
|
bit_cnt1 = 0;
|
|
|
|
a = data->Data[i];
|
|
|
|
do
|
|
|
|
{
|
|
|
|
if (bs == 5)
|
|
|
|
{
|
|
|
|
bit = TX_BIT0;
|
|
|
|
bs = 0;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
bit = a & 1;
|
|
|
|
a = a >> 1;
|
|
|
|
bit_cnt1++;
|
|
|
|
|
|
|
|
if (bit == TX_BIT1)
|
|
|
|
bs++;
|
|
|
|
else
|
|
|
|
bs = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
bit_to_fx25(&tx_byte, &bit_cnt, bit, ax25_data, &ax25_size);
|
|
|
|
|
|
|
|
} while (bit_cnt1 != 8 || bs == 5);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Load close flag
|
|
|
|
|
|
|
|
a = FRAME_FLAG;
|
|
|
|
|
|
|
|
for (i = 0; i < 8; i++)
|
|
|
|
{
|
|
|
|
bit = a & 1;
|
|
|
|
a = a >> 1;
|
|
|
|
bit_to_fx25(&tx_byte, &bit_cnt, bit, ax25_data, &ax25_size);
|
|
|
|
}
|
|
|
|
|
|
|
|
a = FRAME_FLAG;
|
|
|
|
|
|
|
|
// if too short or too long
|
|
|
|
|
|
|
|
if (ax25_size < 168 || ax25_size > 1912) // < 21 or > 239
|
|
|
|
{
|
|
|
|
// Send as normal ax25 packet
|
|
|
|
|
|
|
|
if (bit_cnt > 0)
|
|
|
|
{
|
|
|
|
do
|
|
|
|
{
|
|
|
|
tx_byte = tx_byte >> 1;
|
|
|
|
bit_cnt++;
|
|
|
|
if (bit_cnt == 8)
|
|
|
|
stringAdd(ax25_data, &tx_byte, 1);
|
|
|
|
} while (bit_cnt < 8);
|
|
|
|
}
|
|
|
|
tx_fx25_size[snd_ch] = ax25_size;
|
|
|
|
return ax25_data;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Send as FX25 Message
|
|
|
|
|
|
|
|
// find RS block size
|
|
|
|
|
|
|
|
rs_id = 0;
|
|
|
|
|
|
|
|
for (i = 0; i <= nr_tags; i++)
|
|
|
|
if (ax25_size <= rs_payload[i])
|
|
|
|
rs_id = i;
|
|
|
|
|
|
|
|
// Padding to block size
|
|
|
|
|
|
|
|
while (ax25_size != rs_payload[rs_id])
|
|
|
|
{
|
|
|
|
bit = a & 1;
|
|
|
|
a = (a >> 1) | (bit << 7);
|
|
|
|
bit_to_fx25(&tx_byte, &bit_cnt, bit, ax25_data, &ax25_size);
|
|
|
|
}
|
|
|
|
|
|
|
|
memset(rs_block, 0, 255);
|
|
|
|
move(&ax25_data->Data[0], &rs_block[0], ax25_data->Length);
|
|
|
|
|
|
|
|
fx25_encode_rs(rs_block, parity, 0, rs_roots[rs_id]);
|
|
|
|
|
|
|
|
result = newString();
|
|
|
|
|
|
|
|
stringAdd(result, (Byte *)&rs_tag[rs_id], 8);
|
|
|
|
stringAdd(result, ax25_data->Data, ax25_data->Length);
|
|
|
|
stringAdd(result, parity, rs_roots[rs_id]);
|
|
|
|
|
|
|
|
tx_fx25_size[snd_ch] = result->Length << 3;
|
|
|
|
|
|
|
|
freeString(ax25_data);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
void fx25_get_new_frame(int snd_ch, TStringList * frame_stream)
|
|
|
|
{
|
|
|
|
string * myTemp;
|
|
|
|
|
|
|
|
tx_bs_bit[snd_ch] = 0;
|
|
|
|
tx_bit_cnt[snd_ch] = 0;
|
|
|
|
tx_flag_cnt[snd_ch] = 0;
|
|
|
|
tx_bit_stuff_cnt[snd_ch] = 0;
|
|
|
|
tx_fx25_size_cnt[snd_ch] = 0;
|
|
|
|
tx_fx25_size[snd_ch] = 1;
|
|
|
|
tx_frame_status[snd_ch] = FRAME_NEW_FRAME;
|
|
|
|
tx_byte_status[snd_ch] = BYTE_EMPTY;
|
|
|
|
if (frame_stream->Count == 0)
|
|
|
|
tx_frame_status[snd_ch] = FRAME_NO_FRAME;
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// We now pass control byte and ack bytes on front and pointer to socket on end if ackmode
|
|
|
|
|
|
|
|
myTemp = Strings(frame_stream, 0); // get message
|
|
|
|
|
|
|
|
if ((myTemp->Data[0] & 0x0f) == 12) // ACKMODE
|
|
|
|
{
|
|
|
|
// Save copy then copy data up 3 bytes
|
|
|
|
|
|
|
|
Add(&KISS_acked[snd_ch], duplicateString(myTemp));
|
|
|
|
|
|
|
|
mydelete(myTemp, 0, 3);
|
|
|
|
myTemp->Length -= sizeof(void *);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// Just remove control
|
|
|
|
|
|
|
|
mydelete(myTemp, 0, 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
AGW_AX25_frame_analiz(snd_ch, FALSE, myTemp);
|
|
|
|
put_frame(snd_ch, myTemp, "", TRUE, FALSE);
|
|
|
|
|
|
|
|
tx_data[snd_ch] = fill_fx25_data(snd_ch, myTemp);
|
|
|
|
|
|
|
|
Delete(frame_stream, 0); // This will invalidate temp
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int fx25_get_new_bit(int snd_ch, Byte bit)
|
|
|
|
{
|
|
|
|
string *s;
|
|
|
|
|
|
|
|
if (tx_frame_status[snd_ch] == FRAME_EMPTY)
|
|
|
|
{
|
|
|
|
fx25_get_new_frame(snd_ch, &all_frame_buf[snd_ch]);
|
|
|
|
if (tx_frame_status[snd_ch] == FRAME_NEW_FRAME)
|
|
|
|
tx_frame_status[snd_ch] = FRAME_FULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (tx_frame_status[snd_ch] == FRAME_FULL)
|
|
|
|
{
|
|
|
|
if (tx_byte_status[snd_ch] == BYTE_EMPTY)
|
|
|
|
{
|
|
|
|
if (tx_data[snd_ch]->Length)
|
|
|
|
{
|
|
|
|
s = tx_data[snd_ch];
|
|
|
|
|
|
|
|
tx_bit_stream[snd_ch] = s->Data[0];
|
|
|
|
tx_frame_status[snd_ch] = FRAME_FULL;
|
|
|
|
tx_byte_status[snd_ch] = BYTE_FULL;
|
|
|
|
tx_bit_cnt[snd_ch] = 0;
|
|
|
|
mydelete(tx_data[snd_ch], 0, 1);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
tx_frame_status[snd_ch] = FRAME_EMPTY;
|
|
|
|
}
|
|
|
|
if (tx_byte_status[snd_ch] == BYTE_FULL)
|
|
|
|
{
|
|
|
|
bit = tx_bit_stream[snd_ch] & TX_BIT1;
|
|
|
|
tx_bit_stream[snd_ch] = tx_bit_stream[snd_ch] >> 1;
|
|
|
|
tx_bit_cnt[snd_ch]++;
|
|
|
|
tx_fx25_size_cnt[snd_ch]++;
|
|
|
|
if (tx_bit_cnt[snd_ch] >= 8)
|
|
|
|
tx_byte_status[snd_ch] = BYTE_EMPTY;
|
|
|
|
if (tx_fx25_size_cnt[snd_ch] == tx_fx25_size[snd_ch])
|
|
|
|
tx_frame_status[snd_ch] = FRAME_EMPTY;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (tx_frame_status[snd_ch] == FRAME_EMPTY)
|
|
|
|
{
|
|
|
|
fx25_get_new_frame(snd_ch, &all_frame_buf[snd_ch]);
|
|
|
|
|
|
|
|
switch (tx_frame_status[snd_ch])
|
|
|
|
{
|
|
|
|
case FRAME_NEW_FRAME:
|
|
|
|
tx_frame_status[snd_ch] = FRAME_FULL;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case FRAME_NO_FRAME:
|
|
|
|
tx_tail_cnt[snd_ch] = 0;
|
|
|
|
tx_frame_status[snd_ch] = FRAME_EMPTY;
|
|
|
|
tx_status[snd_ch] = TX_TAIL;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return bit;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//////////////////
|
|
|
|
|
|
|
|
|
|
|
|
int get_new_bit_tail(UCHAR snd_ch, UCHAR bit)
|
|
|
|
{
|
|
|
|
long _txtail = 0;
|
|
|
|
UCHAR _diddles;
|
|
|
|
|
|
|
|
if (modem_mode[snd_ch] == MODE_FSK)
|
|
|
|
_diddles = diddles;
|
|
|
|
else
|
|
|
|
_diddles = 0;
|
|
|
|
|
|
|
|
if (modem_mode[snd_ch] == MODE_FSK)
|
|
|
|
_txtail = txtail[snd_ch];
|
|
|
|
|
|
|
|
else if (modem_mode[snd_ch] == MODE_BPSK)
|
|
|
|
_txtail = txtail[snd_ch];
|
|
|
|
|
|
|
|
else if (modem_mode[snd_ch] == MODE_8PSK)
|
|
|
|
_txtail = txtail[snd_ch] * 3;
|
|
|
|
|
|
|
|
else if (modem_mode[snd_ch] == MODE_QPSK || modem_mode[snd_ch] == MODE_PI4QPSK)
|
|
|
|
_txtail = txtail[snd_ch] << 1;
|
|
|
|
|
|
|
|
else if (modem_mode[snd_ch] == MODE_MPSK)
|
|
|
|
_txtail = txtail[snd_ch] << 2;
|
|
|
|
|
|
|
|
_txtail = (_txtail * tx_baudrate[snd_ch]) / 1000;
|
|
|
|
|
|
|
|
if (qpsk_set[snd_ch].mode == QPSK_V26 || modem_mode[snd_ch] == MODE_8PSK)
|
|
|
|
_diddles = 2;
|
|
|
|
|
|
|
|
switch (_diddles)
|
|
|
|
{
|
|
|
|
case 0:
|
|
|
|
|
|
|
|
if (tx_tail_cnt[snd_ch] < _txtail)
|
|
|
|
{
|
|
|
|
bit = TX_BIT0;
|
|
|
|
tx_tail_cnt[snd_ch]++;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
tx_status[snd_ch] = TX_WAIT_BPF;
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 1:
|
|
|
|
|
|
|
|
if (tx_tail_cnt[snd_ch] < _txtail)
|
|
|
|
{
|
|
|
|
if (tx_last_diddle[snd_ch] == TX_BIT0)
|
|
|
|
bit = TX_BIT1;
|
|
|
|
else
|
|
|
|
bit = TX_BIT0;
|
|
|
|
|
|
|
|
tx_tail_cnt[snd_ch]++;
|
|
|
|
tx_last_diddle[snd_ch] = bit;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
tx_status[snd_ch] = TX_WAIT_BPF;
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 2:
|
|
|
|
|
|
|
|
if (tx_tail_cnt[snd_ch] < _txtail)
|
|
|
|
{
|
|
|
|
bit = FRAME_FLAG >> (tx_tail_cnt[snd_ch] % 8) & 1;
|
|
|
|
tx_tail_cnt[snd_ch]++;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
tx_status[snd_ch] = TX_WAIT_BPF;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return bit;
|
|
|
|
}
|
|
|
|
|
|
|
|
int get_new_bit_delay(UCHAR snd_ch, UCHAR bit)
|
|
|
|
{
|
|
|
|
ULONG _txdelay = 0;
|
|
|
|
UCHAR _diddles;
|
|
|
|
|
|
|
|
_diddles = 0;
|
|
|
|
|
|
|
|
switch (modem_mode[snd_ch])
|
|
|
|
{
|
|
|
|
case MODE_FSK:
|
|
|
|
|
|
|
|
_diddles = diddles;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case MODE_PI4QPSK:
|
|
|
|
case MODE_8PSK:
|
|
|
|
|
|
|
|
_diddles = 2;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case MODE_QPSK:
|
|
|
|
|
|
|
|
if (qpsk_set[snd_ch].mode == QPSK_V26)
|
|
|
|
_diddles = 2;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (modem_mode[snd_ch] == MODE_FSK)
|
|
|
|
_txdelay = txdelay[snd_ch];
|
|
|
|
|
|
|
|
else if (modem_mode[snd_ch] == MODE_BPSK)
|
|
|
|
_txdelay = txdelay[snd_ch];
|
|
|
|
|
|
|
|
else if (modem_mode[snd_ch] == MODE_8PSK)
|
|
|
|
_txdelay = txdelay[snd_ch] * 3;
|
|
|
|
|
|
|
|
else if (modem_mode[snd_ch] == MODE_QPSK || modem_mode[snd_ch] == MODE_PI4QPSK)
|
|
|
|
_txdelay = txdelay[snd_ch] << 1;
|
|
|
|
|
|
|
|
else if (modem_mode[snd_ch] == MODE_MPSK)
|
|
|
|
{
|
|
|
|
if (txdelay[snd_ch] < 400)
|
|
|
|
_txdelay = 400 << 2; //AFC delay
|
|
|
|
else
|
|
|
|
_txdelay = txdelay[snd_ch] << 2;
|
|
|
|
}
|
|
|
|
|
|
|
|
_txdelay = (_txdelay * tx_baudrate[snd_ch]) / 1000;
|
|
|
|
|
|
|
|
switch (_diddles)
|
|
|
|
{
|
|
|
|
case 0:
|
|
|
|
|
|
|
|
if (tx_delay_cnt[snd_ch] < _txdelay)
|
|
|
|
{
|
|
|
|
bit = TX_BIT0;
|
|
|
|
tx_delay_cnt[snd_ch]++;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
tx_status[snd_ch] = TX_FRAME;
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 1:
|
|
|
|
|
|
|
|
if (tx_delay_cnt[snd_ch] < _txdelay)
|
|
|
|
{
|
|
|
|
if (tx_last_diddle[snd_ch] == TX_BIT0)
|
|
|
|
bit = TX_BIT1;
|
|
|
|
else
|
|
|
|
bit = TX_BIT0;
|
|
|
|
|
|
|
|
tx_delay_cnt[snd_ch]++;
|
|
|
|
tx_last_diddle[snd_ch] = bit;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
tx_status[snd_ch] = TX_FRAME;
|
|
|
|
Debugprintf("End TXD %d", SampleNo);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 2:
|
|
|
|
|
|
|
|
// Send Flags
|
|
|
|
|
|
|
|
if (tx_delay_cnt[snd_ch] < _txdelay)
|
|
|
|
{
|
|
|
|
bit = FRAME_FLAG >> ((8 - (_txdelay % 8) + tx_delay_cnt[snd_ch]) % 8) & 1;
|
|
|
|
tx_delay_cnt[snd_ch]++;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
tx_status[snd_ch] = TX_FRAME;
|
|
|
|
Debugprintf("End TXD %d", SampleNo);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return bit;
|
|
|
|
}
|
|
|
|
|
|
|
|
// is this waiting for the filter to fill?
|
|
|
|
// No, flushing BPF
|
|
|
|
|
|
|
|
void get_wait_bpf(UCHAR snd_ch)
|
|
|
|
{
|
|
|
|
tx_BPF_timer[snd_ch]++;
|
|
|
|
|
|
|
|
if (tx_BPF_timer[snd_ch] == tx_BPF_tap[snd_ch] )
|
|
|
|
{
|
|
|
|
tx_status[snd_ch] = TX_NO_DATA;
|
|
|
|
tx_BPF_timer[snd_ch] = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//procedure modulator(snd_ch: byte; var buf: array of single; buf_size: word};
|
|
|
|
//{
|
|
|
|
/*
|
|
|
|
function filter(x,k: single}: single;
|
|
|
|
begin
|
|
|
|
result = k*cos(x};
|
|
|
|
if result>1 then result = 1;
|
|
|
|
if result<-1 then result = -1;
|
|
|
|
end;
|
|
|
|
}
|
|
|
|
*/
|
|
|
|
|
|
|
|
single filter(single x)
|
|
|
|
{
|
|
|
|
if (x <= PI25)
|
|
|
|
return 1.0f;
|
|
|
|
|
|
|
|
if (x >= PI75)
|
|
|
|
return -1.0f;
|
|
|
|
|
|
|
|
return cosf(2.0f * x -PI5);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// make_samples return one sample of the waveform
|
|
|
|
|
|
|
|
// But seems to be called only once per bit ??
|
|
|
|
|
|
|
|
// No, but needs to preserve bit between calls
|
|
|
|
|
|
|
|
float make_samples(unsigned char snd_ch, unsigned char * bitptr)
|
|
|
|
{
|
|
|
|
float pi2, x1, x;
|
|
|
|
Byte i,qbit,tribit,dibit;
|
|
|
|
float z1,z2,z3,z4;
|
|
|
|
unsigned short b, msb, lsb;
|
|
|
|
unsigned char bit = *bitptr;
|
|
|
|
|
|
|
|
float amp = 0;
|
|
|
|
|
|
|
|
pi2 = 2 * pi / TX_Samplerate;
|
|
|
|
x1 = pi * tx_baudrate[snd_ch] / TX_Samplerate;
|
|
|
|
|
|
|
|
if (modem_mode[snd_ch] == MODE_FSK)
|
|
|
|
{
|
|
|
|
if (bit == TX_BIT0)
|
|
|
|
x = pi2*(tx_freq[snd_ch] + 0.5f * tx_shift[snd_ch]);
|
|
|
|
else
|
|
|
|
x = pi2*(tx_freq[snd_ch] - 0.5f * tx_shift[snd_ch]);
|
|
|
|
|
|
|
|
amp = 1.0f;
|
|
|
|
|
|
|
|
if (tx_baudrate[snd_ch] > 600)
|
|
|
|
{
|
|
|
|
if (tx_hitoneraisedb[snd_ch] < 0 && bit == TX_BIT0)
|
|
|
|
amp = tx_hitoneraise[snd_ch];
|
|
|
|
|
|
|
|
if (tx_hitoneraisedb[snd_ch] > 0 && bit == TX_BIT1)
|
|
|
|
amp = tx_hitoneraise[snd_ch];
|
|
|
|
}
|
|
|
|
|
|
|
|
tx_osc[snd_ch] = tx_osc[snd_ch] + x;
|
|
|
|
|
|
|
|
if (tx_osc[snd_ch] > 2*pi)
|
|
|
|
tx_osc[snd_ch] = tx_osc[snd_ch] - 2*pi;
|
|
|
|
}
|
|
|
|
|
|
|
|
else if (modem_mode[snd_ch] == MODE_BPSK)
|
|
|
|
{
|
|
|
|
if (tx_change_phase[snd_ch])
|
|
|
|
tx_bit_mod[snd_ch] = tx_inv[snd_ch] * cos(tx_bit_osc[snd_ch]);
|
|
|
|
|
|
|
|
x = pi2 * (tx_freq[snd_ch]);
|
|
|
|
|
|
|
|
tx_osc[snd_ch] = tx_osc[snd_ch] + x;
|
|
|
|
|
|
|
|
if (tx_osc[snd_ch] > 2 * pi)
|
|
|
|
tx_osc[snd_ch] = tx_osc[snd_ch] - 2 * pi;
|
|
|
|
}
|
|
|
|
|
|
|
|
else if (modem_mode[snd_ch] == MODE_QPSK)
|
|
|
|
{
|
|
|
|
if (tx_QPSK_old_I[snd_ch] != tx_QPSK_I[snd_ch])
|
|
|
|
|
|
|
|
tx_I_mod[snd_ch] = tx_QPSK_avg_I[snd_ch] + tx_QPSK_df_I[snd_ch] * filter(tx_bit_osc[snd_ch]);
|
|
|
|
else
|
|
|
|
tx_I_mod[snd_ch] = tx_QPSK_I[snd_ch];
|
|
|
|
|
|
|
|
if (tx_QPSK_old_Q[snd_ch] != tx_QPSK_Q[snd_ch])
|
|
|
|
tx_Q_mod[snd_ch] = tx_QPSK_avg_Q[snd_ch] + tx_QPSK_df_Q[snd_ch] * filter(tx_bit_osc[snd_ch]);
|
|
|
|
else
|
|
|
|
tx_Q_mod[snd_ch] = tx_QPSK_Q[snd_ch];
|
|
|
|
|
|
|
|
x = pi2 * (tx_freq[snd_ch]);
|
|
|
|
tx_osc[snd_ch] = tx_osc[snd_ch] + x;
|
|
|
|
if (tx_osc[snd_ch] > 2 * pi)
|
|
|
|
tx_osc[snd_ch] = tx_osc[snd_ch] - 2 * pi;
|
|
|
|
}
|
|
|
|
|
|
|
|
else if (modem_mode[snd_ch] == MODE_8PSK || modem_mode[snd_ch] == MODE_PI4QPSK)
|
|
|
|
{
|
|
|
|
if (tx_8PSK_old_I[snd_ch] != tx_8PSK_I[snd_ch])
|
|
|
|
tx_I_mod[snd_ch] = tx_8PSK_avg_I[snd_ch] + tx_8PSK_df_I[snd_ch] * filter(tx_bit_osc[snd_ch]);
|
|
|
|
else
|
|
|
|
tx_I_mod[snd_ch] = tx_8PSK_I[snd_ch];
|
|
|
|
|
|
|
|
if (tx_8PSK_old_Q[snd_ch] != tx_8PSK_Q[snd_ch])
|
|
|
|
tx_Q_mod[snd_ch] = tx_8PSK_avg_Q[snd_ch] + tx_8PSK_df_Q[snd_ch] * filter(tx_bit_osc[snd_ch]);
|
|
|
|
else
|
|
|
|
tx_Q_mod[snd_ch] = tx_8PSK_Q[snd_ch];
|
|
|
|
|
|
|
|
x = pi2 * (tx_freq[snd_ch]);
|
|
|
|
tx_osc[snd_ch] = tx_osc[snd_ch] + x;
|
|
|
|
|
|
|
|
if (tx_osc[snd_ch] > 2 * pi)
|
|
|
|
tx_osc[snd_ch] = tx_osc[snd_ch] - 2 * pi;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
else if (modem_mode[snd_ch] == MODE_MPSK)
|
|
|
|
{
|
|
|
|
z1 = pi2 * (tx_freq[snd_ch] + ch_offset[0]);
|
|
|
|
z2 = pi2 * (tx_freq[snd_ch] + ch_offset[1]);
|
|
|
|
z3 = pi2 * (tx_freq[snd_ch] + ch_offset[2]);
|
|
|
|
z4 = pi2 * (tx_freq[snd_ch] + ch_offset[3]);
|
|
|
|
|
|
|
|
tx_osc1[snd_ch] = tx_osc1[snd_ch] + z1;
|
|
|
|
tx_osc2[snd_ch] = tx_osc2[snd_ch] + z2;
|
|
|
|
tx_osc3[snd_ch] = tx_osc3[snd_ch] + z3;
|
|
|
|
tx_osc4[snd_ch] = tx_osc4[snd_ch] + z4;
|
|
|
|
|
|
|
|
if (tx_osc1[snd_ch] > 2 * pi)
|
|
|
|
tx_osc1[snd_ch] = tx_osc1[snd_ch] - 2 * pi;
|
|
|
|
|
|
|
|
if (tx_osc2[snd_ch] > 2 * pi)
|
|
|
|
tx_osc2[snd_ch] = tx_osc2[snd_ch] - 2 * pi;
|
|
|
|
|
|
|
|
if (tx_osc3[snd_ch] > 2 * pi)
|
|
|
|
tx_osc3[snd_ch] = tx_osc3[snd_ch] - 2 * pi;
|
|
|
|
|
|
|
|
if (tx_osc4[snd_ch] > 2 * pi)
|
|
|
|
tx_osc4[snd_ch] = tx_osc4[snd_ch] - 2 * pi;
|
|
|
|
|
|
|
|
if (tx_old_inv1[snd_ch] != tx_inv1[snd_ch])
|
|
|
|
tx_bit1_mod[snd_ch] = tx_inv1[snd_ch] * cos(tx_bit_osc[snd_ch]);
|
|
|
|
else
|
|
|
|
tx_bit1_mod[snd_ch] = -tx_inv1[snd_ch];
|
|
|
|
|
|
|
|
if (tx_old_inv2[snd_ch] != tx_inv2[snd_ch])
|
|
|
|
tx_bit2_mod[snd_ch] = tx_inv2[snd_ch] * cos(tx_bit_osc[snd_ch]);
|
|
|
|
else
|
|
|
|
tx_bit2_mod[snd_ch] = -tx_inv2[snd_ch];
|
|
|
|
|
|
|
|
if (tx_old_inv3[snd_ch] != tx_inv3[snd_ch])
|
|
|
|
tx_bit3_mod[snd_ch] = tx_inv3[snd_ch] * cos(tx_bit_osc[snd_ch]);
|
|
|
|
else
|
|
|
|
tx_bit3_mod[snd_ch] = -tx_inv3[snd_ch];
|
|
|
|
|
|
|
|
if (tx_old_inv4[snd_ch] != tx_inv4[snd_ch])
|
|
|
|
tx_bit4_mod[snd_ch] = tx_inv4[snd_ch] * cos(tx_bit_osc[snd_ch]);
|
|
|
|
else
|
|
|
|
tx_bit4_mod[snd_ch] = -tx_inv4[snd_ch];
|
|
|
|
}
|
|
|
|
|
|
|
|
tx_bit_osc[snd_ch] = tx_bit_osc[snd_ch] + x1;
|
|
|
|
|
|
|
|
if (tx_bit_osc[snd_ch] > pi)
|
|
|
|
{
|
|
|
|
// This seems to get the next bit,
|
|
|
|
// but why?? - end of samples for last bit
|
|
|
|
|
|
|
|
tx_bit_osc[snd_ch] = tx_bit_osc[snd_ch] - pi;
|
|
|
|
|
|
|
|
// FSK Mode
|
|
|
|
if (modem_mode[snd_ch] == MODE_FSK)
|
|
|
|
{
|
|
|
|
bit = 0;
|
|
|
|
|
|
|
|
if (tx_status[snd_ch] == TX_SILENCE)
|
|
|
|
{
|
|
|
|
tx_delay_cnt[snd_ch] = 0;
|
|
|
|
tx_status[snd_ch] = TX_DELAY;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (il2p_mode[snd_ch] >= IL2P_MODE_TXRX)
|
|
|
|
{
|
2023-09-12 21:38:15 +01:00
|
|
|
// il2p generates TXDELAY as part of the frame, so go straight to TX_FRAME
|
|
|
|
|
2023-09-04 19:06:44 +01:00
|
|
|
if (tx_status[snd_ch] == TX_DELAY)
|
2023-09-12 21:38:15 +01:00
|
|
|
tx_status[snd_ch] = TX_FRAME;
|
|
|
|
|
2023-09-04 19:06:44 +01:00
|
|
|
if (tx_status[snd_ch] == TX_TAIL)
|
2024-07-23 21:26:30 +01:00
|
|
|
bit = il2p_get_new_bit_tail(snd_ch, bit);
|
2023-09-04 19:06:44 +01:00
|
|
|
|
|
|
|
if (tx_status[snd_ch] == TX_FRAME)
|
|
|
|
bit = il2p_get_new_bit(snd_ch, bit);
|
|
|
|
|
|
|
|
|
|
|
|
// No nrzi for il2p
|
|
|
|
|
|
|
|
*bitptr = bit;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// ax25/fx25
|
|
|
|
|
|
|
|
if (tx_status[snd_ch] == TX_DELAY)
|
|
|
|
bit = get_new_bit_delay(snd_ch, bit);
|
|
|
|
|
|
|
|
if (tx_status[snd_ch] == TX_TAIL)
|
|
|
|
bit = get_new_bit_tail(snd_ch, bit);
|
|
|
|
|
|
|
|
if (tx_status[snd_ch] == TX_FRAME)
|
|
|
|
{
|
|
|
|
if (tx_fx25_mode[snd_ch])
|
|
|
|
bit = fx25_get_new_bit(snd_ch, bit);
|
|
|
|
else
|
|
|
|
bit = get_new_bit(snd_ch, bit);
|
|
|
|
}
|
|
|
|
|
|
|
|
*bitptr = tx_nrzi(snd_ch, bit);
|
|
|
|
|
|
|
|
}
|
2024-07-23 21:26:30 +01:00
|
|
|
|
2023-09-04 19:06:44 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// BPSK Mode
|
|
|
|
if (modem_mode[snd_ch] == MODE_BPSK)
|
|
|
|
{
|
|
|
|
bit = 0;
|
|
|
|
|
|
|
|
if (tx_status[snd_ch] == TX_SILENCE)
|
|
|
|
{
|
|
|
|
tx_delay_cnt[snd_ch] = 0;
|
|
|
|
tx_status[snd_ch] = TX_DELAY;
|
|
|
|
}
|
|
|
|
|
2024-07-23 21:26:30 +01:00
|
|
|
// il2p generates TXDELAY as part of the frame, so go straight to TX_FRAME
|
2023-09-04 19:06:44 +01:00
|
|
|
|
|
|
|
if (tx_status[snd_ch] == TX_DELAY)
|
|
|
|
if (il2p_mode[snd_ch] >= IL2P_MODE_TXRX)
|
|
|
|
tx_status[snd_ch] = TX_FRAME;
|
|
|
|
else
|
|
|
|
bit = get_new_bit_delay(snd_ch, bit);
|
|
|
|
|
|
|
|
if (tx_status[snd_ch] == TX_TAIL)
|
2024-07-23 21:26:30 +01:00
|
|
|
{
|
|
|
|
if (il2p_mode[snd_ch] >= IL2P_MODE_TXRX)
|
|
|
|
bit = il2p_get_new_bit_tail(snd_ch, bit);
|
|
|
|
else
|
|
|
|
bit = get_new_bit_tail(snd_ch, bit);
|
|
|
|
}
|
2023-09-04 19:06:44 +01:00
|
|
|
|
|
|
|
if (tx_status[snd_ch] == TX_FRAME)
|
|
|
|
{
|
|
|
|
if (il2p_mode[snd_ch] >= IL2P_MODE_TXRX)
|
|
|
|
bit = il2p_get_new_bit(snd_ch, bit);
|
|
|
|
else if (tx_fx25_mode[snd_ch])
|
|
|
|
bit = fx25_get_new_bit(snd_ch, bit);
|
|
|
|
else
|
|
|
|
bit = get_new_bit(snd_ch, bit);
|
|
|
|
}
|
2024-07-23 21:26:30 +01:00
|
|
|
|
2023-09-04 19:06:44 +01:00
|
|
|
// ?? *bitptr = tx_nrzi(snd_ch, bit);
|
|
|
|
|
|
|
|
if (bit == 0)
|
|
|
|
{
|
|
|
|
tx_inv[snd_ch] = -tx_inv[snd_ch];
|
|
|
|
tx_change_phase[snd_ch] = TRUE;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
tx_change_phase[snd_ch] = FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
// QPSK Mode
|
|
|
|
|
2023-09-12 21:38:15 +01:00
|
|
|
else if (modem_mode[snd_ch] == MODE_QPSK)
|
2023-09-04 19:06:44 +01:00
|
|
|
{
|
|
|
|
dibit = 0;
|
|
|
|
for (i = 0; i < 2; i++)
|
|
|
|
{
|
|
|
|
bit = 0;
|
|
|
|
if (tx_status[snd_ch] == TX_SILENCE)
|
|
|
|
{
|
|
|
|
tx_delay_cnt[snd_ch] = 0;
|
|
|
|
tx_status[snd_ch] = TX_DELAY;
|
|
|
|
}
|
2023-09-12 21:38:15 +01:00
|
|
|
|
|
|
|
if (il2p_mode[snd_ch] >= IL2P_MODE_TXRX)
|
|
|
|
{
|
|
|
|
if (tx_status[snd_ch] == TX_DELAY)
|
|
|
|
tx_status[snd_ch] = TX_FRAME; // il2p generates TXDELAY as part of the frame, so go straight to TX_FRAME
|
|
|
|
|
|
|
|
if (tx_status[snd_ch] == TX_TAIL)
|
2024-07-23 21:26:30 +01:00
|
|
|
bit = il2p_get_new_bit_tail(snd_ch, bit);
|
2023-09-12 21:38:15 +01:00
|
|
|
|
|
|
|
if (tx_status[snd_ch] == TX_FRAME)
|
|
|
|
bit = il2p_get_new_bit(snd_ch, bit);
|
|
|
|
|
|
|
|
// No nrzi for il2p
|
|
|
|
|
|
|
|
dibit = (dibit << 1) | bit;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// ax25/fx25
|
|
|
|
|
|
|
|
if (tx_status[snd_ch] == TX_DELAY)
|
|
|
|
bit = get_new_bit_delay(snd_ch, bit);
|
|
|
|
if (tx_status[snd_ch] == TX_TAIL)
|
|
|
|
bit = get_new_bit_tail(snd_ch, bit);
|
|
|
|
if (tx_status[snd_ch] == TX_FRAME)
|
|
|
|
bit = get_new_bit(snd_ch, bit);
|
|
|
|
dibit = (dibit << 1) | tx_nrzi(snd_ch, bit);
|
|
|
|
|
|
|
|
}
|
2023-09-04 19:06:44 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
dibit = qpsk_set[snd_ch].tx[dibit & 3];
|
|
|
|
tx_QPSK[snd_ch] = (tx_QPSK[snd_ch] + dibit) & 3;
|
|
|
|
tx_QPSK_old_I[snd_ch] = tx_QPSK_I[snd_ch];
|
|
|
|
tx_QPSK_old_Q[snd_ch] = tx_QPSK_Q[snd_ch];
|
|
|
|
|
|
|
|
switch (tx_QPSK[snd_ch])
|
|
|
|
{
|
|
|
|
case 0:
|
|
|
|
|
|
|
|
tx_QPSK_I[snd_ch] = COS45;
|
|
|
|
tx_QPSK_Q[snd_ch] = COS45;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 1:
|
|
|
|
|
|
|
|
tx_QPSK_I[snd_ch] = -COS45;
|
|
|
|
tx_QPSK_Q[snd_ch] = COS45;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 2:
|
|
|
|
|
|
|
|
tx_QPSK_I[snd_ch] = -COS45;
|
|
|
|
tx_QPSK_Q[snd_ch] = -COS45;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 3:
|
|
|
|
|
|
|
|
tx_QPSK_I[snd_ch] = COS45;
|
|
|
|
tx_QPSK_Q[snd_ch] = -COS45;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
tx_QPSK_avg_I[snd_ch] = 0.5f*(tx_QPSK_old_I[snd_ch] + tx_QPSK_I[snd_ch]);
|
|
|
|
tx_QPSK_df_I[snd_ch] = 0.5f*(tx_QPSK_old_I[snd_ch] - tx_QPSK_I[snd_ch]);
|
|
|
|
tx_QPSK_avg_Q[snd_ch] = 0.5f*(tx_QPSK_old_Q[snd_ch] + tx_QPSK_Q[snd_ch]);
|
|
|
|
tx_QPSK_df_Q[snd_ch] = 0.5f*(tx_QPSK_old_Q[snd_ch] - tx_QPSK_Q[snd_ch]);
|
|
|
|
}
|
|
|
|
|
|
|
|
// PI/4 QPSK Mode
|
|
|
|
|
|
|
|
if (modem_mode[snd_ch] == MODE_PI4QPSK)
|
|
|
|
{
|
|
|
|
dibit = 0;
|
|
|
|
|
|
|
|
for (i = 0; i < 2; i++)
|
|
|
|
{
|
|
|
|
bit = 0;
|
|
|
|
if (tx_status[snd_ch] == TX_SILENCE)
|
|
|
|
{
|
|
|
|
tx_delay_cnt[snd_ch] = 0;
|
|
|
|
tx_status[snd_ch] = TX_DELAY;
|
|
|
|
}
|
|
|
|
|
2023-09-12 21:38:15 +01:00
|
|
|
if (il2p_mode[snd_ch] >= IL2P_MODE_TXRX)
|
|
|
|
{
|
|
|
|
if (tx_status[snd_ch] == TX_DELAY)
|
|
|
|
tx_status[snd_ch] = TX_FRAME; // il2p generates TXDELAY as part of the frame, so go straight to TX_FRAME
|
2023-09-04 19:06:44 +01:00
|
|
|
|
2023-09-12 21:38:15 +01:00
|
|
|
if (tx_status[snd_ch] == TX_TAIL)
|
2024-07-23 21:26:30 +01:00
|
|
|
bit = il2p_get_new_bit_tail(snd_ch, bit);
|
2023-09-04 19:06:44 +01:00
|
|
|
|
2023-09-12 21:38:15 +01:00
|
|
|
if (tx_status[snd_ch] == TX_FRAME)
|
|
|
|
bit = il2p_get_new_bit(snd_ch, bit);
|
2023-09-04 19:06:44 +01:00
|
|
|
|
2023-09-12 21:38:15 +01:00
|
|
|
// No nrzi for il2p
|
|
|
|
|
|
|
|
dibit = (dibit << 1) | bit;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// ax25/fx25
|
|
|
|
|
|
|
|
if (tx_status[snd_ch] == TX_DELAY)
|
|
|
|
bit = get_new_bit_delay(snd_ch, bit);
|
2023-09-04 19:06:44 +01:00
|
|
|
|
2023-09-12 21:38:15 +01:00
|
|
|
if (tx_status[snd_ch] == TX_TAIL)
|
|
|
|
bit = get_new_bit_tail(snd_ch, bit);
|
|
|
|
|
|
|
|
if (tx_status[snd_ch] == TX_FRAME)
|
|
|
|
bit = get_new_bit(snd_ch, bit);
|
2023-09-04 19:06:44 +01:00
|
|
|
|
2023-09-12 21:38:15 +01:00
|
|
|
*bitptr = tx_nrzi(snd_ch, bit);
|
|
|
|
|
|
|
|
dibit = (dibit << 1) | *bitptr;
|
|
|
|
}
|
2023-09-04 19:06:44 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// This returns 3,1,5 or 7 so we use the odd enties in the 8PSK table
|
|
|
|
|
|
|
|
dibit = gray_PI4QPSK[dibit & 3];
|
|
|
|
|
|
|
|
tx_8PSK[snd_ch] = (tx_8PSK[snd_ch] + dibit) & 7;
|
|
|
|
tx_8PSK_old_I[snd_ch] = tx_8PSK_I[snd_ch];
|
|
|
|
tx_8PSK_old_Q[snd_ch] = tx_8PSK_Q[snd_ch];
|
|
|
|
|
|
|
|
switch (tx_8PSK[snd_ch])
|
|
|
|
{
|
|
|
|
case 0:
|
|
|
|
tx_8PSK_I[snd_ch] = 0;
|
|
|
|
tx_8PSK_Q[snd_ch] = 1;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 1:
|
|
|
|
tx_8PSK_I[snd_ch] = COS45;
|
|
|
|
tx_8PSK_Q[snd_ch] = COS45;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 2:
|
|
|
|
tx_8PSK_I[snd_ch] = 1;
|
|
|
|
tx_8PSK_Q[snd_ch] = 0;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 3:
|
|
|
|
tx_8PSK_I[snd_ch] = COS45;
|
|
|
|
tx_8PSK_Q[snd_ch] = -COS45;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 4:
|
|
|
|
tx_8PSK_I[snd_ch] = 0;
|
|
|
|
tx_8PSK_Q[snd_ch] = -1;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 5:
|
|
|
|
tx_8PSK_I[snd_ch] = -COS45;
|
|
|
|
tx_8PSK_Q[snd_ch] = -COS45;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 6:
|
|
|
|
tx_8PSK_I[snd_ch] = -1;
|
|
|
|
tx_8PSK_Q[snd_ch] = 0;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 7:
|
|
|
|
tx_8PSK_I[snd_ch] = -COS45;
|
|
|
|
tx_8PSK_Q[snd_ch] = COS45;
|
|
|
|
break;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
tx_8PSK_avg_I[snd_ch] = 0.5*(tx_8PSK_old_I[snd_ch] + tx_8PSK_I[snd_ch]);
|
|
|
|
tx_8PSK_df_I[snd_ch] = 0.5*(tx_8PSK_old_I[snd_ch] - tx_8PSK_I[snd_ch]);
|
|
|
|
tx_8PSK_avg_Q[snd_ch] = 0.5*(tx_8PSK_old_Q[snd_ch] + tx_8PSK_Q[snd_ch]);
|
|
|
|
tx_8PSK_df_Q[snd_ch] = 0.5*(tx_8PSK_old_Q[snd_ch] - tx_8PSK_Q[snd_ch]);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
// 8PSK Mode
|
|
|
|
|
|
|
|
if (modem_mode[snd_ch] == MODE_8PSK)
|
|
|
|
{
|
|
|
|
tribit = 0;
|
|
|
|
for (i = 0; i < 3; i++)
|
|
|
|
{
|
|
|
|
bit = 0;
|
|
|
|
|
|
|
|
if (tx_status[snd_ch] == TX_SILENCE)
|
|
|
|
{
|
|
|
|
tx_delay_cnt[snd_ch] = 0;
|
|
|
|
tx_status[snd_ch] = TX_DELAY;
|
|
|
|
}
|
|
|
|
|
2023-09-12 21:38:15 +01:00
|
|
|
if (il2p_mode[snd_ch] >= IL2P_MODE_TXRX)
|
|
|
|
{
|
|
|
|
if (tx_status[snd_ch] == TX_DELAY)
|
|
|
|
tx_status[snd_ch] = TX_FRAME; // il2p generates TXDELAY as part of the frame, so go straight to TX_FRAME
|
|
|
|
|
|
|
|
if (tx_status[snd_ch] == TX_TAIL)
|
2024-07-23 21:26:30 +01:00
|
|
|
bit = il2p_get_new_bit_tail(snd_ch, bit);
|
2023-09-12 21:38:15 +01:00
|
|
|
|
|
|
|
if (tx_status[snd_ch] == TX_FRAME)
|
|
|
|
bit = il2p_get_new_bit(snd_ch, bit);
|
|
|
|
|
|
|
|
// No nrzi for il2p
|
2023-09-04 19:06:44 +01:00
|
|
|
|
2023-09-12 21:38:15 +01:00
|
|
|
tribit = (tribit << 1) | bit;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// ax25/fx25
|
|
|
|
|
|
|
|
if (tx_status[snd_ch] == TX_DELAY)
|
|
|
|
bit = get_new_bit_delay(snd_ch, bit);
|
|
|
|
if (tx_status[snd_ch] == TX_TAIL)
|
|
|
|
bit = get_new_bit_tail(snd_ch, bit);
|
|
|
|
if (tx_status[snd_ch] == TX_FRAME)
|
|
|
|
bit = get_new_bit(snd_ch, bit);
|
|
|
|
|
|
|
|
tribit = (tribit << 1) | tx_nrzi(snd_ch, bit);
|
|
|
|
}
|
|
|
|
}
|
2023-09-04 19:06:44 +01:00
|
|
|
tribit = gray_8PSK[tribit & 7];
|
|
|
|
|
|
|
|
tx_8PSK[snd_ch] = (tx_8PSK[snd_ch] + tribit) & 7;
|
|
|
|
tx_8PSK_old_I[snd_ch] = tx_8PSK_I[snd_ch];
|
|
|
|
tx_8PSK_old_Q[snd_ch] = tx_8PSK_Q[snd_ch];
|
|
|
|
|
|
|
|
switch (tx_8PSK[snd_ch])
|
|
|
|
{
|
|
|
|
case 0:
|
|
|
|
|
|
|
|
tx_8PSK_I[snd_ch] = 0;
|
|
|
|
tx_8PSK_Q[snd_ch] = 1;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 1:
|
|
|
|
|
|
|
|
tx_8PSK_I[snd_ch] = COS45;
|
|
|
|
tx_8PSK_Q[snd_ch] = COS45;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 2:
|
|
|
|
|
|
|
|
tx_8PSK_I[snd_ch] = 1;
|
|
|
|
tx_8PSK_Q[snd_ch] = 0;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 3:
|
|
|
|
|
|
|
|
tx_8PSK_I[snd_ch] = COS45;
|
|
|
|
tx_8PSK_Q[snd_ch] = -COS45;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 4:
|
|
|
|
|
|
|
|
tx_8PSK_I[snd_ch] = 0;
|
|
|
|
tx_8PSK_Q[snd_ch] = -1;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 5:
|
|
|
|
|
|
|
|
tx_8PSK_I[snd_ch] = -COS45;
|
|
|
|
tx_8PSK_Q[snd_ch] = -COS45;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 6:
|
|
|
|
|
|
|
|
tx_8PSK_I[snd_ch] = -1;
|
|
|
|
tx_8PSK_Q[snd_ch] = 0;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 7:
|
|
|
|
|
|
|
|
tx_8PSK_I[snd_ch] = -COS45;
|
|
|
|
tx_8PSK_Q[snd_ch] = COS45;
|
|
|
|
break;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
tx_8PSK_avg_I[snd_ch] = 0.5f*(tx_8PSK_old_I[snd_ch] + tx_8PSK_I[snd_ch]);
|
|
|
|
tx_8PSK_df_I[snd_ch] = 0.5f*(tx_8PSK_old_I[snd_ch] - tx_8PSK_I[snd_ch]);
|
|
|
|
tx_8PSK_avg_Q[snd_ch] = 0.5f*(tx_8PSK_old_Q[snd_ch] + tx_8PSK_Q[snd_ch]);
|
|
|
|
tx_8PSK_df_Q[snd_ch] = 0.5f*(tx_8PSK_old_Q[snd_ch] - tx_8PSK_Q[snd_ch]);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (modem_mode[snd_ch] == MODE_MPSK)
|
|
|
|
{
|
|
|
|
qbit = 0;
|
|
|
|
|
|
|
|
// get the bits for each of 4 carriers
|
|
|
|
|
|
|
|
for (i = 1; i <= 4; i++)
|
|
|
|
{
|
|
|
|
bit = 0;
|
|
|
|
|
|
|
|
if (tx_status[snd_ch] == TX_SILENCE)
|
|
|
|
{
|
|
|
|
tx_delay_cnt[snd_ch] = 0;
|
|
|
|
tx_status[snd_ch] = TX_DELAY;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (tx_status[snd_ch] == TX_DELAY)
|
|
|
|
bit = get_new_bit_delay(snd_ch, bit);
|
|
|
|
|
|
|
|
if (tx_status[snd_ch] == TX_TAIL)
|
|
|
|
bit = get_new_bit_tail(snd_ch, bit);
|
|
|
|
|
|
|
|
if (tx_status[snd_ch] == TX_FRAME)
|
|
|
|
bit = get_new_bit(snd_ch, bit);
|
|
|
|
|
|
|
|
qbit = (qbit << 1) | bit;
|
|
|
|
}
|
|
|
|
|
|
|
|
tx_old_inv1[snd_ch] = tx_inv1[snd_ch];
|
|
|
|
tx_old_inv2[snd_ch] = tx_inv2[snd_ch];
|
|
|
|
tx_old_inv3[snd_ch] = tx_inv3[snd_ch];
|
|
|
|
tx_old_inv4[snd_ch] = tx_inv4[snd_ch];
|
|
|
|
|
|
|
|
if ((qbit & 8) == 0)
|
|
|
|
tx_inv1[snd_ch] = -tx_inv1[snd_ch];
|
|
|
|
if ((qbit & 4) == 0)
|
|
|
|
tx_inv2[snd_ch] = -tx_inv2[snd_ch];
|
|
|
|
if ((qbit & 2) == 0)
|
|
|
|
tx_inv3[snd_ch] = -tx_inv3[snd_ch];
|
|
|
|
if ((qbit & 1) == 0)
|
|
|
|
tx_inv4[snd_ch] = -tx_inv4[snd_ch];
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (tx_status[snd_ch] == TX_WAIT_BPF)
|
|
|
|
get_wait_bpf(snd_ch);
|
|
|
|
|
|
|
|
if (modem_mode[snd_ch] == MODE_FSK)
|
|
|
|
return amp * sinf(tx_osc[snd_ch]);
|
|
|
|
|
|
|
|
if (modem_mode[snd_ch] == MODE_BPSK)
|
|
|
|
return sinf(tx_osc[snd_ch]) * tx_bit_mod[snd_ch];
|
|
|
|
|
|
|
|
if (modem_mode[snd_ch] == MODE_QPSK || modem_mode[snd_ch] == MODE_8PSK || modem_mode[snd_ch] == MODE_PI4QPSK)
|
|
|
|
return sin(tx_osc[snd_ch]) * tx_I_mod[snd_ch] + cos(tx_osc[snd_ch]) * tx_Q_mod[snd_ch];
|
|
|
|
|
|
|
|
if (modem_mode[snd_ch] == MODE_MPSK)
|
|
|
|
return 0.35*(sinf(tx_osc1[snd_ch])*tx_bit1_mod[snd_ch] +
|
|
|
|
sinf(tx_osc2[snd_ch])*tx_bit2_mod[snd_ch] +
|
|
|
|
sinf(tx_osc3[snd_ch])*tx_bit3_mod[snd_ch] +
|
|
|
|
sinf(tx_osc4[snd_ch])*tx_bit4_mod[snd_ch]);
|
|
|
|
|
|
|
|
return 0.0f;
|
|
|
|
}
|
|
|
|
|
|
|
|
float make_samples_calib(UCHAR snd_ch, UCHAR tones)
|
|
|
|
{
|
|
|
|
float amp, pi2, x, x1;
|
|
|
|
|
|
|
|
x1 = pi * tx_baudrate[snd_ch] / TX_Samplerate;
|
|
|
|
pi2 = 2 * pi / TX_Samplerate;
|
|
|
|
|
|
|
|
switch (tones)
|
|
|
|
{
|
|
|
|
case 1:
|
|
|
|
|
|
|
|
tx_last_bit[snd_ch] = 1;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 2:
|
|
|
|
|
|
|
|
tx_last_bit[snd_ch] = 0;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 3:
|
|
|
|
|
|
|
|
tx_bit_osc[snd_ch] = tx_bit_osc[snd_ch] + x1;
|
|
|
|
|
|
|
|
if (tx_bit_osc[snd_ch] > pi)
|
|
|
|
{
|
|
|
|
tx_bit_osc[snd_ch] = tx_bit_osc[snd_ch] - pi;
|
|
|
|
tx_last_bit[snd_ch] = tx_last_bit[snd_ch] ^ 1;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
amp = 1;
|
|
|
|
|
|
|
|
if (tx_baudrate[snd_ch] > 600)
|
|
|
|
{
|
|
|
|
if (tx_hitoneraisedb[snd_ch] < 0 && tx_last_bit[snd_ch] == 0)
|
|
|
|
amp = tx_hitoneraise[snd_ch];
|
|
|
|
|
|
|
|
if (tx_hitoneraisedb[snd_ch] > 0 && tx_last_bit[snd_ch] == 1)
|
|
|
|
amp = tx_hitoneraise[snd_ch];
|
|
|
|
}
|
|
|
|
|
|
|
|
if (tx_last_bit[snd_ch] == 0)
|
|
|
|
x = pi2*(tx_freq[snd_ch] + 0.5f * tx_shift[snd_ch]);
|
|
|
|
else
|
|
|
|
x = pi2*(tx_freq[snd_ch] - 0.5f * tx_shift[snd_ch]);
|
|
|
|
|
|
|
|
tx_osc[snd_ch] = tx_osc[snd_ch] + x;
|
|
|
|
|
|
|
|
if (tx_osc[snd_ch] > 2*pi)
|
|
|
|
tx_osc[snd_ch] = tx_osc[snd_ch] - 2 * pi;
|
|
|
|
|
|
|
|
return amp * sinf(tx_osc[snd_ch]);
|
|
|
|
}
|
|
|
|
|
2023-09-12 21:38:15 +01:00
|
|
|
float amplitude = 32000;
|
2023-09-04 19:06:44 +01:00
|
|
|
|
|
|
|
void modulator(UCHAR snd_ch, int buf_size)
|
|
|
|
{
|
|
|
|
// We feed samples to samplesink instead of buffering them
|
|
|
|
|
|
|
|
// I think this is the top of the TX hierarchy
|
|
|
|
|
|
|
|
int i;
|
2023-09-12 21:38:15 +01:00
|
|
|
int Sample;
|
2023-09-04 19:06:44 +01:00
|
|
|
|
|
|
|
if (calib_mode[snd_ch] > 0)
|
|
|
|
{
|
|
|
|
if (calib_mode[snd_ch] == 4) // CWID
|
|
|
|
{
|
|
|
|
if (tx_status[snd_ch] == TX_SILENCE)
|
|
|
|
{
|
|
|
|
SoundIsPlaying = TRUE;
|
|
|
|
Debugprintf("Start CWID Chan %d", snd_ch);
|
|
|
|
RadioPTT(snd_ch, 1);
|
|
|
|
|
|
|
|
tx_status[snd_ch] = 6;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ARDOPSendToCard(snd_ch, SendSize) == 1)
|
|
|
|
{
|
|
|
|
// End of TX
|
|
|
|
|
|
|
|
tx_status[snd_ch] = TX_SILENCE; // Stop TX
|
|
|
|
Flush();
|
|
|
|
RadioPTT(snd_ch, 0);
|
|
|
|
Debugprintf("End CWID");
|
|
|
|
calib_mode[snd_ch] = 0;
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (tx_status[snd_ch] == TX_SILENCE)
|
|
|
|
{
|
|
|
|
SoundIsPlaying = TRUE;
|
|
|
|
Debugprintf("Start Calib Chan %d", snd_ch);
|
|
|
|
RadioPTT(snd_ch, 1);
|
|
|
|
|
|
|
|
tx_bit_osc[snd_ch] = 0;
|
|
|
|
tx_last_bit[snd_ch] = 0;
|
|
|
|
|
|
|
|
// fill filter
|
|
|
|
|
|
|
|
for (i = 0; i < tx_BPF_tap[snd_ch]; i++)
|
|
|
|
tx_prev_BPF_buf[snd_ch][buf_size + i] = make_samples_calib(snd_ch,calib_mode[snd_ch]);
|
|
|
|
}
|
|
|
|
tx_status[snd_ch] = TX_WAIT_BPF;
|
|
|
|
|
|
|
|
for (i = 0; i < buf_size; i++)
|
|
|
|
tx_src_BPF_buf[snd_ch][i] = make_samples_calib(snd_ch, calib_mode[snd_ch]);
|
|
|
|
|
|
|
|
FIR_filter(tx_src_BPF_buf[snd_ch],buf_size,tx_BPF_tap[snd_ch],tx_BPF_core[snd_ch],tx_BPF_buf[snd_ch],tx_prev_BPF_buf[snd_ch]);
|
|
|
|
|
|
|
|
for (i = 0; i < buf_size; i++)
|
|
|
|
{
|
|
|
|
Sample = tx_BPF_buf[snd_ch][i] * amplitude;
|
2023-09-12 21:38:15 +01:00
|
|
|
|
|
|
|
if (Sample < txmin)
|
|
|
|
txmin = Sample;
|
|
|
|
else if (Sample > txmax)
|
|
|
|
txmax = Sample;
|
|
|
|
|
2023-09-04 19:06:44 +01:00
|
|
|
SampleSink(modemtoSoundLR[snd_ch], Sample);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (tx_status[snd_ch] == TX_SILENCE)
|
|
|
|
{
|
|
|
|
if (fx25_mode[snd_ch] == FX25_MODE_TXRX)
|
|
|
|
tx_fx25_mode[snd_ch] = 1;
|
|
|
|
else
|
|
|
|
tx_fx25_mode[snd_ch] = 0;
|
|
|
|
|
|
|
|
tx_bit_osc[snd_ch] = 0;
|
|
|
|
tx_8PSK[snd_ch] = 0;
|
|
|
|
tx_QPSK[snd_ch] = 0;
|
|
|
|
tx_last_bit[snd_ch] = 0;
|
|
|
|
tx_inv1[snd_ch] = 1;
|
|
|
|
tx_inv2[snd_ch] = 1;
|
|
|
|
tx_inv3[snd_ch] = 1;
|
|
|
|
tx_inv4[snd_ch] = 1;
|
|
|
|
tx_8PSK_I[snd_ch] = 0;
|
|
|
|
tx_8PSK_Q[snd_ch] = 1;
|
|
|
|
tx_8PSK_old_I[snd_ch] = 0;
|
|
|
|
tx_8PSK_old_Q[snd_ch] = 1;
|
|
|
|
tx_QPSK_I[snd_ch] = COS45;
|
|
|
|
tx_QPSK_Q[snd_ch] = COS45;
|
|
|
|
tx_QPSK_old_I[snd_ch] = COS45;
|
|
|
|
tx_QPSK_old_Q[snd_ch] = COS45;
|
|
|
|
|
|
|
|
for (i = 0; i < tx_BPF_tap[snd_ch]; i++)
|
|
|
|
tx_prev_BPF_buf[snd_ch][buf_size+i] = make_samples(snd_ch, &tx_pol[snd_ch]);
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0; i < buf_size; i++)
|
|
|
|
tx_src_BPF_buf[snd_ch][i] = make_samples(snd_ch, &tx_pol[snd_ch]);
|
|
|
|
|
|
|
|
FIR_filter(tx_src_BPF_buf[snd_ch], buf_size, tx_BPF_tap[snd_ch], tx_BPF_core[snd_ch], tx_BPF_buf[snd_ch], tx_prev_BPF_buf[snd_ch]);
|
|
|
|
|
|
|
|
for (i = 0; i < buf_size; i++)
|
|
|
|
{
|
2023-09-12 21:38:15 +01:00
|
|
|
Sample = tx_BPF_buf[snd_ch][i] * amplitude;
|
|
|
|
|
|
|
|
if (Sample < txmin)
|
|
|
|
txmin = Sample;
|
|
|
|
else if (Sample > txmax)
|
|
|
|
{
|
|
|
|
txmax = Sample;
|
|
|
|
|
|
|
|
if (txmax > 32767)
|
|
|
|
{
|
|
|
|
amplitude = amplitude * 32767 / txmax;
|
|
|
|
txmax = 32767;
|
|
|
|
Sample = 32767;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-09-04 19:06:44 +01:00
|
|
|
SampleSink(modemtoSoundLR[snd_ch], Sample);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|