mirror of
https://github.com/dj0abr/SSB_HighSpeed_Modem.git
synced 2026-06-02 05:54:39 -04:00
update
This commit is contained in:
+1
-1
@@ -2,7 +2,7 @@
|
||||
|
||||
CXXFLAGS = -Wall -O3 -std=c++0x -Wno-write-strings -Wno-narrowing
|
||||
LDFLAGS = -lpthread -lrt -lsndfile -lasound -lm -lbass -lbassflac -lfftw3 -lfftw3_threads -lliquid
|
||||
OBJ = hsmodem.o constellation.o crc16.o frame_packer.o main_helper.o scrambler.o speed.o fec.o audio.o udp.o fft.o liquid_if.o
|
||||
OBJ = hsmodem.o constellation.o crc16.o frame_packer.o main_helper.o scrambler.o speed.o fec.o audio.o udp.o fft.o liquid_if.o symboltracker.o
|
||||
|
||||
default: $(OBJ)
|
||||
g++ $(CXXFLAGS) -o ../LinuxRelease/hsmodem $(OBJ) $(LDFLAGS)
|
||||
|
||||
+8
-3
@@ -369,7 +369,7 @@ void setPBvolume(int v)
|
||||
float vf = v;
|
||||
vf /= 100;
|
||||
|
||||
printf("set PB volume to:%d / %f [0..1]\n", v, vf );
|
||||
//printf("set PB volume to:%d / %f [0..1]\n", v, vf );
|
||||
|
||||
selectPBdevice();
|
||||
if (!BASS_SetVolume(vf))
|
||||
@@ -383,7 +383,7 @@ void setCAPvolume(int v)
|
||||
float vf = v;
|
||||
vf /= 100;
|
||||
|
||||
printf("set CAP volume to:%d / %f [0..1]\n", v, vf);
|
||||
//printf("set CAP volume to:%d / %f [0..1]\n", v, vf);
|
||||
|
||||
selectCAPdevice();
|
||||
if (!BASS_RecordSetInput(-1,BASS_INPUT_ON,vf))
|
||||
@@ -462,7 +462,7 @@ void PB_UNLOCK() { pthread_mutex_unlock(&pb_crit_sec); }
|
||||
|
||||
#define AUDIO_BUFFERMAXTIME 2 // fifo can buffer this time in [s]
|
||||
#define AUDIO_PLAYBACK_BUFLEN (48000 * 10) // space for 10 seconds of samples
|
||||
#define AUDIO_CAPTURE_BUFLEN (48000 * 10)
|
||||
#define AUDIO_CAPTURE_BUFLEN (48000) // space for 1s
|
||||
|
||||
int cap_wridx=0;
|
||||
int cap_rdidx=0;
|
||||
@@ -487,6 +487,11 @@ void init_pipes()
|
||||
// overwrite old data if the fifo is full
|
||||
void cap_write_fifo(float sample)
|
||||
{
|
||||
if (((cap_wridx + 1) % AUDIO_CAPTURE_BUFLEN) == cap_rdidx)
|
||||
{
|
||||
printf("cap fifo full\n");
|
||||
}
|
||||
|
||||
CAP_LOCK;
|
||||
cap_buffer[cap_wridx] = sample;
|
||||
if(++cap_wridx >= AUDIO_CAPTURE_BUFLEN) cap_wridx = 0;
|
||||
|
||||
Binary file not shown.
@@ -157,7 +157,7 @@ void setPBvolume(int v)
|
||||
if (vf < minPBvol) vf = minPBvol;
|
||||
if (vf > maxPBvol) vf = maxPBvol;
|
||||
|
||||
printf("set PB volume to:%d / %f [%f..%f]\n", v, vf, minPBvol, maxPBvol);
|
||||
//printf("set PB volume to:%d / %f [%f..%f]\n", v, vf, minPBvol, maxPBvol);
|
||||
|
||||
selectPBdevice_wasapi();
|
||||
if (!BASS_WASAPI_SetVolume(BASS_WASAPI_CURVE_DB, vf))
|
||||
@@ -216,16 +216,55 @@ DWORD CALLBACK PBcallback_wasapi(void* buffer, DWORD length, void* user)
|
||||
free(fdata);
|
||||
return length;
|
||||
}
|
||||
/*
|
||||
#define MCHECK 10
|
||||
void nullChecker(float fv, float *pbuf, DWORD len)
|
||||
{
|
||||
static float farr[MCHECK];
|
||||
static int idx = 0;
|
||||
static int f = 1;
|
||||
static int anz = 0;
|
||||
|
||||
if (f)
|
||||
{
|
||||
f = 0;
|
||||
for (int i = 0; i < MCHECK; i++)
|
||||
farr[i] = 1;
|
||||
}
|
||||
|
||||
farr[idx] = fv;
|
||||
idx++;
|
||||
if (idx == MCHECK) idx = 0;
|
||||
|
||||
float nu = 0;
|
||||
for (int i = 0; i < MCHECK; i++)
|
||||
{
|
||||
nu += farr[i];
|
||||
}
|
||||
|
||||
if (nu == 0)
|
||||
{
|
||||
// how many 00s ar in the current buffer
|
||||
int a = 0;
|
||||
for (unsigned int i = 0; i < len-1; i++)
|
||||
{
|
||||
if (pbuf[i] == 0 && pbuf[i+1] == 0) a++;
|
||||
}
|
||||
printf("=============== null sequence detected: %d len:%d nullanz:%d\n",anz++,len,a);
|
||||
}
|
||||
}
|
||||
*/
|
||||
DWORD CALLBACK CAPcallback_wasapi(void* buffer, DWORD length, void* user)
|
||||
{
|
||||
//printf("CAP callback, len:%d\n",length);
|
||||
//measure_speed_bps(length/sizeof(float)/ WASAPI_CHANNELS);
|
||||
|
||||
float* fbuffer = (float*)buffer;
|
||||
//showbytestringf((char*)"rx: ", fbuffer, 20);
|
||||
//showbytestringf((char*)"rx: ", fbuffer, 10);
|
||||
//printf("%10.6f\n", fbuffer[0]);
|
||||
for (unsigned int i = 0; i < (length / sizeof(float)); i += WASAPI_CHANNELS)
|
||||
{
|
||||
//nullChecker(fbuffer[i],fbuffer, length / sizeof(float));
|
||||
cap_write_fifo(fbuffer[i]);
|
||||
}
|
||||
|
||||
|
||||
+54
-16
@@ -32,6 +32,7 @@ uint8_t rx_status = 0;
|
||||
|
||||
int framecounter = 0;
|
||||
int lastframenum = 0;
|
||||
int getPayload_error = 0;
|
||||
|
||||
// header for TX,
|
||||
uint8_t TXheaderbytes[HEADERLEN] = {0x53, 0xe1, 0xa6};
|
||||
@@ -95,7 +96,7 @@ uint8_t *Pack(uint8_t *payload, int type, int status, int *plen)
|
||||
// polulate the raw frame
|
||||
|
||||
// make the frame counter
|
||||
if(status & (1<<4))
|
||||
if(status == 0)
|
||||
framecounter = 0; // first block of a stream
|
||||
else
|
||||
framecounter++;
|
||||
@@ -131,30 +132,46 @@ uint8_t *Pack(uint8_t *payload, int type, int status, int *plen)
|
||||
return txblock;
|
||||
}
|
||||
|
||||
#ifdef _WIN32_
|
||||
#define MAXHEADERRS 5
|
||||
#endif
|
||||
|
||||
#define MAXHEADERRS 0
|
||||
#ifdef _LINUX_
|
||||
#define MAXHEADERRS 2 // takes less CPU time, important for Rasberry PI
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Header erros will not cause any data errors because the CRC will filter out
|
||||
* false header detects,
|
||||
* but it will cause higher CPU load due to excessive execution of FEC and CRC
|
||||
*/
|
||||
int seekHeadersyms()
|
||||
*/
|
||||
|
||||
int seekHeadersyms(int symnum)
|
||||
{
|
||||
int ret = -1;
|
||||
int errs = 0;
|
||||
|
||||
int exp_hdr = (UDPBLOCKLEN * 8) / bitsPerSymbol; // we expect a new header at this symbol number
|
||||
symnum %= exp_hdr;
|
||||
|
||||
int maxerr = MAXHEADERRS;
|
||||
|
||||
if(constellationSize == 4)
|
||||
{
|
||||
// QPSK
|
||||
for(int tab=0; tab<4; tab++)
|
||||
{
|
||||
int errs = 0;
|
||||
errs = 0;
|
||||
for(int i=0; i<HEADERLEN*8/2; i++)
|
||||
{
|
||||
if(rxbuffer[i] != QPSK_headertab[tab][i])
|
||||
{
|
||||
errs++;
|
||||
}
|
||||
}
|
||||
if(errs <= MAXHEADERRS) return tab;
|
||||
if (errs <= maxerr)
|
||||
{
|
||||
ret = tab;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -162,17 +179,27 @@ int seekHeadersyms()
|
||||
// 8PSK
|
||||
for(int tab=0; tab<8; tab++)
|
||||
{
|
||||
int errs = 0;
|
||||
errs = 0;
|
||||
for(int i=0; i<HEADERLEN*8/3; i++)
|
||||
{
|
||||
if(rxbuffer[i] != _8PSK_headertab[tab][i])
|
||||
{
|
||||
errs++;
|
||||
}
|
||||
}
|
||||
if(errs <= MAXHEADERRS) return tab;
|
||||
if(errs <= maxerr)
|
||||
{
|
||||
ret = tab;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (ret != -1)
|
||||
{
|
||||
//printf("header detected at symbol:%d, headererrors:%d\n", symnum,errs);
|
||||
return ret;
|
||||
}
|
||||
|
||||
//if (symnum == 0) printf("header expected at symbol:%d but not found\n", symnum);
|
||||
|
||||
return -1;
|
||||
}
|
||||
@@ -184,6 +211,7 @@ uint8_t *unpack_data(uint8_t *rxd, int len)
|
||||
{
|
||||
int framerdy = 0;
|
||||
static uint8_t payload[PAYLOADLEN+10];
|
||||
static int symnum = 0;
|
||||
|
||||
rx_status = 0;
|
||||
// shift all received symbols through rxbuffer
|
||||
@@ -196,13 +224,13 @@ uint8_t *unpack_data(uint8_t *rxd, int len)
|
||||
memmove(rxbuffer,rxbuffer+1,frmlen-1);
|
||||
// insert new symbol at the top
|
||||
rxbuffer[frmlen-1] = rxd[sym];
|
||||
symnum++;
|
||||
|
||||
//showbytestring((char*)"rx: ",rxbuffer,30);
|
||||
|
||||
int rotations = seekHeadersyms();
|
||||
int rotations = seekHeadersyms(symnum);
|
||||
if(rotations != -1)
|
||||
{
|
||||
//printf("Header found, rotation: %d\n",rotations);
|
||||
|
||||
// rxbuffer contains all symbols of the received frame
|
||||
// convert to bytes
|
||||
@@ -226,6 +254,14 @@ uint8_t *unpack_data(uint8_t *rxd, int len)
|
||||
{
|
||||
memcpy(payload,pl, PAYLOADLEN+10);
|
||||
framerdy = 1;
|
||||
if(symnum != 688)
|
||||
printf("Header found, rotation: %d at symbol no.: %d result: OK\n", rotations, symnum);
|
||||
symnum = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
if((symnum % ((UDPBLOCKLEN * 8) / bitsPerSymbol)) == 0)
|
||||
printf("Header found, rotation: %d at symbol no.: %d result: %d\n", rotations, symnum, getPayload_error);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -273,6 +309,7 @@ uint8_t *getPayload(uint8_t *rxb)
|
||||
if(ret == 0)
|
||||
{
|
||||
//printf("fec ERROR\n");
|
||||
getPayload_error = 1;
|
||||
return NULL; // fec impossible
|
||||
}
|
||||
//printf("fec ok\n");
|
||||
@@ -285,6 +322,7 @@ uint8_t *getPayload(uint8_t *rxb)
|
||||
if (crc != rxcrc)
|
||||
{
|
||||
//printf("crc ERROR\n");
|
||||
getPayload_error = 2;
|
||||
return NULL; // no data found
|
||||
}
|
||||
//printf("crc OK\n");
|
||||
@@ -308,8 +346,8 @@ uint8_t *getPayload(uint8_t *rxb)
|
||||
payload[4] = rx_status; // frame lost information
|
||||
payload[5] = speed >> 8; // measured line speed
|
||||
payload[6] = speed;
|
||||
payload[7] = 0; // free for later use
|
||||
payload[8] = 0;
|
||||
payload[7] = maxLevel; // actual max level on sound capture in %
|
||||
payload[8] = 0; // free for later use
|
||||
payload[9] = 0;
|
||||
|
||||
//printf("Frame no.: %d, type:%d, minfo:%d\n",framenumrx,payload[0],payload[3]);
|
||||
|
||||
+14
-5
@@ -91,6 +91,7 @@ int UdpDataPort_fromGR_I_Q = 40137;
|
||||
int speedmode = 2;
|
||||
int bitsPerSymbol = 2; // QPSK=2, 8PSK=3
|
||||
int constellationSize = 4; // QPSK=4, 8PSK=8
|
||||
int psk8mode=0; // 0=APSK8, 1=PSK8
|
||||
|
||||
char localIP[] = { "127.0.0.1" };
|
||||
char ownfilename[] = { "hsmodem" };
|
||||
@@ -202,7 +203,7 @@ int main(int argc, char* argv[])
|
||||
//doArraySend();
|
||||
|
||||
if (demodulator() == 0)
|
||||
sleep_ms(100);
|
||||
sleep_ms(10);
|
||||
}
|
||||
printf("stopped: %d\n", keeprunning);
|
||||
|
||||
@@ -242,6 +243,14 @@ SPEEDRATE sr[8] = {
|
||||
|
||||
void startModem()
|
||||
{
|
||||
if (speedmode >= 8)
|
||||
{
|
||||
speedmode = speedmode - 4;
|
||||
psk8mode = 1;
|
||||
}
|
||||
else
|
||||
psk8mode = 0;
|
||||
|
||||
bitsPerSymbol = sr[speedmode].bpsym;
|
||||
constellationSize = (1 << bitsPerSymbol); // QPSK=4, 8PSK=8
|
||||
|
||||
@@ -394,7 +403,7 @@ void appdata_rxdata(uint8_t* pdata, int len, struct sockaddr_in* rxsock)
|
||||
|
||||
//if (getSending() == 1) return; // already sending (Array sending)
|
||||
|
||||
if (minfo == 0)
|
||||
if (minfo == 0 || minfo == 3)
|
||||
{
|
||||
// this is the first frame of a larger file
|
||||
sendAnnouncement();
|
||||
@@ -403,8 +412,8 @@ void appdata_rxdata(uint8_t* pdata, int len, struct sockaddr_in* rxsock)
|
||||
// caprate: samples/s. This are symbols: caprate/txinterpolfactor
|
||||
// and bits: symbols * bitsPerSymbol
|
||||
// and bytes/second: bits/8 = (caprate/txinterpolfactor) * bitsPerSymbol / 8
|
||||
// one frame has 258 bytes, so we need for 5s: 5* ((caprate/txinterpolfactor) * bitsPerSymbol / 8) /258 + 1 frames
|
||||
int numframespreamble = 5 * ((caprate / txinterpolfactor) * bitsPerSymbol / 8) / 258 + 1;
|
||||
// one frame has 258 bytes, so we need for 6s: 6* ((caprate/txinterpolfactor) * bitsPerSymbol / 8) /258 + 1 frames
|
||||
int numframespreamble = 6 * ((caprate / txinterpolfactor) * bitsPerSymbol / 8) / 258 + 1;
|
||||
for (int i = 0; i < numframespreamble; i++)
|
||||
toGR_sendData(pdata + 2, type, minfo);
|
||||
}
|
||||
@@ -460,7 +469,7 @@ void GRdata_rxdata(uint8_t* pdata, int len, struct sockaddr_in* rxsock)
|
||||
if (++fnd >= (wt * ws))
|
||||
{
|
||||
fnd = 0;
|
||||
//printf("no signal detected %d, reset RX modem\n", wt);
|
||||
printf("no signal detected %d, reset RX modem\n", wt);
|
||||
resetModem();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef _WIN32
|
||||
#define _WIN32_
|
||||
@@ -60,6 +61,7 @@
|
||||
#include "frameformat.h"
|
||||
#include "fec.h"
|
||||
#include "udp.h"
|
||||
#include "symboltracker.h"
|
||||
|
||||
#define jpg_tempfilename "rxdata.jpg"
|
||||
|
||||
@@ -129,6 +131,14 @@ void exit_fft();
|
||||
void showbytestringf(char* title, float* data, int anz);
|
||||
uint16_t* make_waterfall(float fre, int* retlen);
|
||||
|
||||
void km_symtrack_cccf_create(int _ftype,
|
||||
unsigned int _k,
|
||||
unsigned int _m,
|
||||
float _beta,
|
||||
int _ms);
|
||||
void km_symtrack_cccf_reset(int mode);
|
||||
void km_symtrack_cccf_set_bandwidth(float _bw);
|
||||
void km_symtrack_execute(liquid_float_complex _x, liquid_float_complex* _y, unsigned int* _ny, unsigned int* psym_out);
|
||||
|
||||
extern int speedmode;
|
||||
extern int bitsPerSymbol;
|
||||
@@ -146,6 +156,8 @@ extern int announcement;
|
||||
extern int ann_running;
|
||||
extern int transmissions;
|
||||
extern int linespeed;
|
||||
extern uint8_t maxLevel;
|
||||
extern int psk8mode;
|
||||
|
||||
#ifdef _LINUX_
|
||||
int isRunning(char* prgname);
|
||||
|
||||
@@ -228,6 +228,7 @@
|
||||
<ClInclude Include="frameformat.h" />
|
||||
<ClInclude Include="hsmodem.h" />
|
||||
<ClInclude Include="liquid.h" />
|
||||
<ClInclude Include="symboltracker.h" />
|
||||
<ClInclude Include="udp.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
@@ -243,6 +244,7 @@
|
||||
<ClCompile Include="main_helper.cpp" />
|
||||
<ClCompile Include="scrambler.cpp" />
|
||||
<ClCompile Include="speed.cpp" />
|
||||
<ClCompile Include="symboltracker.cpp" />
|
||||
<ClCompile Include="udp.cpp" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
|
||||
@@ -54,6 +54,9 @@
|
||||
<ClCompile Include="audio_wasapi.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="symboltracker.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="hsmodem.h">
|
||||
@@ -86,5 +89,8 @@
|
||||
<ClInclude Include="bassflac.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="symboltracker.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
+52
-25
@@ -51,9 +51,13 @@ modulation_scheme getMod()
|
||||
{
|
||||
if(bitsPerSymbol == 2)
|
||||
return LIQUID_MODEM_QPSK;
|
||||
//return LIQUID_MODEM_APSK4;
|
||||
else
|
||||
return LIQUID_MODEM_APSK8;
|
||||
else
|
||||
{
|
||||
if(psk8mode == 0)
|
||||
return LIQUID_MODEM_APSK8;
|
||||
else
|
||||
return LIQUID_MODEM_PSK8;
|
||||
}
|
||||
}
|
||||
|
||||
// =========== MODULATOR ==================================================
|
||||
@@ -209,7 +213,6 @@ void modulator(uint8_t sym_in)
|
||||
|
||||
nco_crcf dnnco = NULL;
|
||||
symtrack_cccf symtrack = NULL;
|
||||
modem demod = NULL;
|
||||
firdecim_crcf decim = NULL;
|
||||
|
||||
// decimator parameters
|
||||
@@ -221,8 +224,9 @@ int ftype_st = LIQUID_FIRFILT_RRC;
|
||||
unsigned int k_st = 4; // samples per symbol
|
||||
unsigned int m_st = 7; // filter delay (symbols)
|
||||
float beta_st = beta_excessBW;//0.30f; // filter excess bandwidth factor
|
||||
float bandwidth_st = 0.7f; // loop filter bandwidth
|
||||
float bandwidth_st = 0.9f; // loop filter bandwidth
|
||||
|
||||
uint8_t maxLevel = 0; // maximum level over the last x samples in %
|
||||
|
||||
void init_demodulator()
|
||||
{
|
||||
@@ -241,34 +245,23 @@ void init_demodulator()
|
||||
|
||||
// create symbol tracking synchronizer
|
||||
//k_st = txinterpolfactor;
|
||||
symtrack = symtrack_cccf_create(ftype_st,k_st,m_st,beta_st,getMod());
|
||||
symtrack_cccf_set_bandwidth(symtrack,bandwidth_st);
|
||||
|
||||
int ret = symtrack_cccf_set_eq_dd(symtrack);
|
||||
if (ret != LIQUID_OK)
|
||||
{
|
||||
printf("symtrack_cccf_set_eq_dd failed\n");
|
||||
}
|
||||
|
||||
// demodulator
|
||||
demod = modem_create(getMod());
|
||||
printf("RX demodulator running\n");
|
||||
//symtrack = km_symtrack_cccf_create(ftype_st,k_st,m_st,beta_st,getMod());
|
||||
km_symtrack_cccf_create(ftype_st, k_st, m_st, beta_st, getMod());
|
||||
//symtrack_cccf_set_bandwidth(symtrack,bandwidth_st);
|
||||
km_symtrack_cccf_set_bandwidth(bandwidth_st);
|
||||
}
|
||||
|
||||
void close_demodulator()
|
||||
{
|
||||
if(symtrack != NULL) symtrack_cccf_destroy(symtrack);
|
||||
if(demod != NULL) modem_destroy(demod);
|
||||
if(decim != NULL) firdecim_crcf_destroy(decim);
|
||||
symtrack = NULL;
|
||||
demod = NULL;
|
||||
decim = NULL;
|
||||
}
|
||||
|
||||
void resetModem()
|
||||
{
|
||||
//printf("Reset Symtrack\n");
|
||||
symtrack_cccf_reset(symtrack);
|
||||
km_symtrack_cccf_reset(0xff);
|
||||
}
|
||||
|
||||
// called for Audio-Samples (FFT)
|
||||
@@ -301,6 +294,34 @@ void make_FFTdata(float f)
|
||||
}
|
||||
}
|
||||
|
||||
#define MCHECK 1000
|
||||
void getMax(float fv)
|
||||
{
|
||||
static float farr[MCHECK];
|
||||
static int idx = 0;
|
||||
static int f = 1;
|
||||
|
||||
if (f)
|
||||
{
|
||||
f = 0;
|
||||
for (int i = 0; i < MCHECK; i++)
|
||||
farr[i] = 1;
|
||||
}
|
||||
|
||||
farr[idx] = fv;
|
||||
idx++;
|
||||
if (idx == MCHECK)
|
||||
{
|
||||
idx = 0;
|
||||
float max = 0;
|
||||
for (int i = 0; i < MCHECK; i++)
|
||||
{
|
||||
if (farr[i] > max) max = farr[i];
|
||||
}
|
||||
maxLevel = (uint8_t)(max*100);
|
||||
//printf("max: %10.6f\n", max);
|
||||
}
|
||||
}
|
||||
|
||||
int demodulator()
|
||||
{
|
||||
@@ -313,13 +334,16 @@ static int ccol_idx = 0;
|
||||
float f;
|
||||
int ret = cap_read_fifo(&f);
|
||||
if(ret == 0) return 0;
|
||||
|
||||
|
||||
// input volume
|
||||
#ifdef _WIN32_
|
||||
f *= softwareCAPvolume;
|
||||
#endif
|
||||
|
||||
make_FFTdata(f*120);
|
||||
getMax(f);
|
||||
|
||||
make_FFTdata(f*60);
|
||||
|
||||
// downconvert into baseband
|
||||
// still at soundcard sample rate
|
||||
@@ -340,19 +364,22 @@ static int ccol_idx = 0;
|
||||
ccol_idx = 0;
|
||||
|
||||
// we have rxPreInterpolfactor samples in ccol
|
||||
//printf("sc:%10.6f dn:%10.6f j%10.6f ", f, c.real, c.imag);
|
||||
liquid_float_complex y;
|
||||
firdecim_crcf_execute(decim, ccol, &y);
|
||||
|
||||
unsigned int num_symbols_sync;
|
||||
liquid_float_complex syms;
|
||||
symtrack_cccf_execute(symtrack, y, &syms, &num_symbols_sync);
|
||||
//symtrack_cccf_execute(symtrack, y, &syms, &num_symbols_sync);
|
||||
unsigned int nsym_out; // output symbol
|
||||
km_symtrack_execute(y, &syms, &num_symbols_sync,&nsym_out);
|
||||
|
||||
if(num_symbols_sync > 1) printf("symtrack_cccf_execute %d output symbols ???\n",num_symbols_sync);
|
||||
if(num_symbols_sync != 0)
|
||||
{
|
||||
unsigned int sym_out; // output symbol
|
||||
modem_demodulate(demod, syms, &sym_out);
|
||||
|
||||
sym_out = nsym_out;
|
||||
|
||||
measure_speed_syms(1);
|
||||
|
||||
// try to extract a complete frame
|
||||
|
||||
@@ -89,7 +89,7 @@ void showbytestringf(char* title, float* data, int anz)
|
||||
{
|
||||
printf("%s. Len %d: ", title, anz);
|
||||
for (int i = 0; i < anz; i++)
|
||||
printf("%.6f ", data[i]);
|
||||
printf("%7.4f ", data[i]);
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
|
||||
Executable
+173
@@ -0,0 +1,173 @@
|
||||
#include "hsmodem.h"
|
||||
|
||||
SYMTRACK km_symtrack;
|
||||
SYMTRACK* q = &km_symtrack;
|
||||
|
||||
// create km_symtrack object with basic parameters
|
||||
// _ftype : filter type (e.g. LIQUID_FIRFILT_RRC)
|
||||
// _k : samples per symbol
|
||||
// _m : filter delay (symbols)
|
||||
// _beta : filter excess bandwidth
|
||||
// _ms : modulation scheme (e.g. LIQUID_MODEM_QPSK)
|
||||
void km_symtrack_cccf_create(int _ftype,
|
||||
unsigned int _k,
|
||||
unsigned int _m,
|
||||
float _beta,
|
||||
int _ms)
|
||||
{
|
||||
// validate input
|
||||
if (_k < 2)
|
||||
printf((char *)"symtrack_cccf_create(), filter samples/symbol must be at least 2\n");
|
||||
if (_m == 0)
|
||||
printf((char*)"symtrack_cccf_create(), filter delay must be greater than zero\n");
|
||||
if (_beta <= 0.0f || _beta > 1.0f)
|
||||
printf((char*)"symtrack_cccf_create(), filter excess bandwidth must be in [0,1]\n");
|
||||
if (_ms == LIQUID_MODEM_UNKNOWN || _ms >= LIQUID_MODEM_NUM_SCHEMES)
|
||||
printf((char*)"symtrack_cccf_create(), invalid modulation scheme\n");
|
||||
|
||||
// set input parameters
|
||||
q->filter_type = _ftype;
|
||||
q->k = _k;
|
||||
q->m = _m;
|
||||
q->beta = _beta;
|
||||
q->mod_scheme = _ms == LIQUID_MODEM_UNKNOWN ? LIQUID_MODEM_BPSK : _ms;
|
||||
|
||||
// create automatic gain control
|
||||
q->agc = agc_crcf_create();
|
||||
|
||||
// create symbol synchronizer (output rate: 2 samples per symbol)
|
||||
if (q->filter_type == LIQUID_FIRFILT_UNKNOWN)
|
||||
q->symsync = symsync_crcf_create_kaiser(q->k, q->m, 0.9f, 16);
|
||||
else
|
||||
q->symsync = symsync_crcf_create_rnyquist(q->filter_type, q->k, q->m, q->beta, 16);
|
||||
symsync_crcf_set_output_rate(q->symsync, 2);
|
||||
|
||||
// create equalizer as default low-pass filter with integer symbol delay (2 samples/symbol)
|
||||
q->eq_len = 2 * 4 + 1;
|
||||
q->eq = eqlms_cccf_create_lowpass(q->eq_len, 0.45f);
|
||||
q->eq_strategy = q->SYMTRACK_EQ_DD;
|
||||
|
||||
// nco and phase-locked loop
|
||||
q->nco = nco_crcf_create(LIQUID_VCO);
|
||||
|
||||
// demodulator
|
||||
q->demod = modem_create((modulation_scheme)q->mod_scheme);
|
||||
|
||||
// set default bandwidth
|
||||
km_symtrack_cccf_set_bandwidth(0.9f);
|
||||
|
||||
// reset and return main object
|
||||
km_symtrack_cccf_reset(0xff);
|
||||
}
|
||||
|
||||
void km_symtrack_cccf_reset(int mode)
|
||||
{
|
||||
// reset objects
|
||||
if (mode & 1) agc_crcf_reset(q->agc);
|
||||
if (mode & 2) symsync_crcf_reset(q->symsync);
|
||||
if (mode & 4) eqlms_cccf_reset(q->eq);
|
||||
if (mode & 8) nco_crcf_reset(q->nco);
|
||||
if (mode & 0x10) modem_reset(q->demod);
|
||||
|
||||
// reset internal counters
|
||||
q->symsync_index = 0;
|
||||
q->num_syms_rx = 0;
|
||||
}
|
||||
|
||||
void km_symtrack_cccf_set_bandwidth(float _bw)
|
||||
{
|
||||
// validate input
|
||||
if (_bw < 0)
|
||||
printf("symtrack_set_bandwidth(), bandwidth must be in [0,1]\n");
|
||||
|
||||
// set bandwidths accordingly
|
||||
float agc_bandwidth = 0.02f * _bw;
|
||||
float symsync_bandwidth = 0.001f * _bw;
|
||||
float eq_bandwidth = 0.02f * _bw;
|
||||
float pll_bandwidth = 0.001f * _bw;
|
||||
|
||||
// automatic gain control
|
||||
agc_crcf_set_bandwidth(q->agc, agc_bandwidth);
|
||||
|
||||
// symbol timing recovery
|
||||
symsync_crcf_set_lf_bw(q->symsync, symsync_bandwidth);
|
||||
|
||||
// equalizer
|
||||
eqlms_cccf_set_bw(q->eq, eq_bandwidth);
|
||||
|
||||
// phase-locked loop
|
||||
nco_crcf_pll_set_bandwidth(q->nco, pll_bandwidth);
|
||||
}
|
||||
|
||||
#define MX 10
|
||||
|
||||
// execute synchronizer on single input sample
|
||||
// _q : synchronizer object
|
||||
// _x : input data sample
|
||||
// _y : output data array
|
||||
// _ny : number of samples written to output buffer
|
||||
void km_symtrack_execute(liquid_float_complex _x, liquid_float_complex* _y, unsigned int* _ny, unsigned int *psym_out)
|
||||
{
|
||||
liquid_float_complex v; // output sample
|
||||
unsigned int i;
|
||||
unsigned int num_outputs = 0;
|
||||
|
||||
// run sample through automatic gain control
|
||||
agc_crcf_execute(q->agc, _x, &v);
|
||||
|
||||
// symbol synchronizer
|
||||
unsigned int nw = 0;
|
||||
symsync_crcf_execute(q->symsync, &v, 1, q->symsync_buf, &nw);
|
||||
|
||||
// process each output sample
|
||||
for (i = 0; i < nw; i++) {
|
||||
// update phase-locked loop
|
||||
nco_crcf_step(q->nco);
|
||||
nco_crcf_mix_down(q->nco, q->symsync_buf[i], &v);
|
||||
|
||||
// equalizer/decimator
|
||||
eqlms_cccf_push(q->eq, v);
|
||||
|
||||
// decimate result, noting that symsync outputs at exactly 2 samples/symbol
|
||||
q->symsync_index++;
|
||||
if (!(q->symsync_index % 2))
|
||||
continue;
|
||||
|
||||
// increment number of symbols received
|
||||
q->num_syms_rx++;
|
||||
|
||||
// compute equalizer filter output; updating coefficients is dependent upon strategy
|
||||
liquid_float_complex d_hat;
|
||||
eqlms_cccf_execute(q->eq, &d_hat);
|
||||
|
||||
// demodulate result, apply phase correction
|
||||
unsigned int sym_out;
|
||||
modem_demodulate(q->demod, d_hat, &sym_out);
|
||||
*psym_out = sym_out;
|
||||
float phase_error = modem_get_demodulator_phase_error(q->demod);
|
||||
|
||||
// update pll
|
||||
nco_crcf_pll_step(q->nco, phase_error);
|
||||
|
||||
// update equalizer independent of the signal: estimate error
|
||||
// assuming constant modulus signal
|
||||
// TODO: check lock conditions of previous object to determine when to run equalizer
|
||||
liquid_float_complex d_prime;
|
||||
d_prime.real = d_prime.imag = 0;
|
||||
if (q->num_syms_rx > 200)
|
||||
{
|
||||
modem_get_demodulator_sample(q->demod, &d_prime);
|
||||
eqlms_cccf_step(q->eq, d_prime, d_hat);
|
||||
}
|
||||
|
||||
// save result to output
|
||||
_y[num_outputs++] = d_hat;
|
||||
}
|
||||
|
||||
/*float fr = nco_crcf_get_frequency(q->nco);
|
||||
float ph = nco_crcf_get_phase(q->nco);
|
||||
|
||||
printf("%10.6f %10.6f %10.6f %10.6f\n", fr, ph, _x.real, _x.imag);*/
|
||||
|
||||
* _ny = num_outputs;
|
||||
}
|
||||
Executable
+42
@@ -0,0 +1,42 @@
|
||||
#pragma once
|
||||
|
||||
#include "liquid.h"
|
||||
|
||||
typedef struct _SYMTRACK_ {
|
||||
// parameters
|
||||
int filter_type; // filter type (e.g. LIQUID_FIRFILT_RRC)
|
||||
unsigned int k; // samples/symbol
|
||||
unsigned int m; // filter semi-length
|
||||
float beta; // filter excess bandwidth
|
||||
int mod_scheme; // demodulator
|
||||
|
||||
// automatic gain control
|
||||
agc_crcf agc; // agc object
|
||||
float agc_bandwidth; // agc bandwidth
|
||||
|
||||
// symbol timing recovery
|
||||
symsync_crcf symsync; // symbol timing recovery object
|
||||
float symsync_bandwidth; // symsync loop bandwidth
|
||||
liquid_float_complex symsync_buf[8]; // symsync output buffer
|
||||
unsigned int symsync_index; // symsync output sample index
|
||||
|
||||
// equalizer/decimator
|
||||
eqlms_cccf eq; // equalizer (LMS)
|
||||
unsigned int eq_len; // equalizer length
|
||||
float eq_bandwidth; // equalizer bandwidth
|
||||
enum {
|
||||
SYMTRACK_EQ_CM, // equalizer strategy: constant modulus
|
||||
SYMTRACK_EQ_DD, // equalizer strategy: decision directed
|
||||
SYMTRACK_EQ_OFF, // equalizer strategy: disabled
|
||||
} eq_strategy;
|
||||
|
||||
// nco/phase-locked loop
|
||||
nco_crcf nco; // nco (carrier recovery)
|
||||
float pll_bandwidth; // phase-locked loop bandwidth
|
||||
|
||||
// demodulator
|
||||
modem demod; // linear modem demodulator
|
||||
|
||||
// state and counters
|
||||
unsigned int num_syms_rx; // number of symbols recovered
|
||||
} SYMTRACK;
|
||||
Reference in New Issue
Block a user