This commit is contained in:
Kurt Moraw 2020-12-19 22:46:34 +01:00
parent f557132b02
commit 63fb166ac6
119 changed files with 5966 additions and 581 deletions

View File

@ -2,7 +2,7 @@
The purpose of this project is to transfer data (pictures...) via a 2,7kHz SSB channel on the narrow band transponder as fast as possible.
# this is work in progress
Version 0.4 is working on:
Version 0.52
Windows 10 (should work on Win7, not tested)
linux Desktop PC,
Odroid SBC
@ -63,21 +63,10 @@ then you find the program under the program group "amsat" in the Window's start
* QO-100 via IC-9700, IC-7300 or IC-7100 ... working
* Short Wave 6m band via IC-7300, IC-7100 ... working. In case of significant noise, use the lowest bit rate (3000 bit/s)
# Manual
# usage
In the IC-9700 activate the DATA mode and the RX filter FIL1 to full range of 3.6kHz.
In oscardata.exe go to the "BER" tab. Then click START. If you change the bitrate, wait a few seconds before starting again.
The program is now sending test data frames to the default sound card. If your sound card is properly connected to the transceiver then switch the transceiver to TX and the data will be sent to QO-100.
Receive your transmission, feed it to the default soundcard. As soon as oscardata.exe detects a correct data frame it will display status messages on the screen.
(For testing purposes you can just connect Line-Out of your soundcard with Line-IN with a cable.)
To assign the soundcard to the modem I recommend to use pavucontrol. Using the TX volume set a signal level of about 20 to 24 dB over noise floor. You will need about -10dB compared to the BPSK400 beacon. The received audio volume can be adjusted with help of the spectrum display in oscardata.exe-
Now as the transmission is OK, you can go to the "Image RX/TX" tab. First, select a picture quality then load a picture and finally press SEND to send it to QO-100. When you correctly receive your own transmission the RX picture will be displayed line by line.
complete documentation see:
https://hsmodem.dj0abr.de
vy 73, DJ0ABR

BIN
WinRelease/audio/1200.pcm Normal file

Binary file not shown.

BIN
WinRelease/audio/2400.pcm Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
WinRelease/audio/bpsk.pcm Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -684,6 +684,21 @@ item: Install File
Destination=%MAINDIR%\Satellite-icon.ico
Flags=0000000010000010
end
item: Install File
Source=c:\tmp\WinRelease\audio\1200.pcm
Destination=%MAINDIR%\audio\1200.pcm
Flags=0000000010000010
end
item: Install File
Source=c:\tmp\WinRelease\audio\2400.pcm
Destination=%MAINDIR%\audio\2400.pcm
Flags=0000000010000010
end
item: Install File
Source=c:\tmp\WinRelease\audio\bpsk.pcm
Destination=%MAINDIR%\audio\bpsk.pcm
Flags=0000000010000010
end
item: Install File
Source=c:\tmp\WinRelease\audio\3000.pcm
Destination=%MAINDIR%\audio\3000.pcm
@ -745,14 +760,9 @@ item: Install File
Flags=0000000010000010
end
item: Install File
Source=c:\tmp\WinRelease\bass.dll
Destination=%MAINDIR%\bass.dll
Flags=0000000010000011
end
item: Install File
Source=c:\tmp\WinRelease\basswasapi.dll
Destination=%MAINDIR%\basswasapi.dll
Flags=0000000010000011
Source=c:\tmp\WinRelease\libsoundio.dll
Destination=%MAINDIR%\libsoundio.dll
Flags=0000000010000010
end
item: Install File
Source=c:\tmp\WinRelease\hsmodem.exe

BIN
hsmodem/Release/announcement.obj Executable file

Binary file not shown.

BIN
hsmodem/Release/codec2.obj Executable file

Binary file not shown.

BIN
hsmodem/Release/constellation.obj Executable file

Binary file not shown.

BIN
hsmodem/Release/crc16.obj Executable file

Binary file not shown.

BIN
hsmodem/Release/fec.obj Executable file

Binary file not shown.

BIN
hsmodem/Release/fft.obj Executable file

Binary file not shown.

BIN
hsmodem/Release/fifo.obj Executable file

Binary file not shown.

BIN
hsmodem/Release/fifo_voice.obj Executable file

Binary file not shown.

BIN
hsmodem/Release/frame_packer.obj Executable file

Binary file not shown.

View File

7
hsmodem/Release/hsmodem.log Executable file
View File

@ -0,0 +1,7 @@
 hsmodem.cpp
Code wird generiert.
1 of 599 functions ( 0.2%) were compiled, the rest were copied from previous compilation.
0 functions were new in current compilation
1 functions had inline decision re-evaluated but remain unchanged
Codegenerierung ist abgeschlossen.
hsmodem.vcxproj -> E:\funk\hsmodem\hsmodem\..\WinRelease\hsmodem.exe

BIN
hsmodem/Release/hsmodem.obj Executable file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,2 @@
#TargetFrameworkVersion=v4.0:PlatformToolSet=v140_xp:EnableManagedIncrementalBuild=false:VCToolArchitecture=Native32Bit:WindowsTargetPlatformVersion=8.1
Release|Win32|E:\funk\hsmodem\|

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
hsmodem/Release/liquid_if.obj Executable file

Binary file not shown.

BIN
hsmodem/Release/main_helper.obj Executable file

Binary file not shown.

BIN
hsmodem/Release/scrambler.obj Executable file

Binary file not shown.

BIN
hsmodem/Release/soundio.obj Executable file

Binary file not shown.

BIN
hsmodem/Release/speed.obj Executable file

Binary file not shown.

BIN
hsmodem/Release/symboltracker.obj Executable file

Binary file not shown.

BIN
hsmodem/Release/udp.obj Executable file

Binary file not shown.

BIN
hsmodem/Release/vc140.pdb Executable file

Binary file not shown.

BIN
hsmodem/Release/voiceio.obj Executable file

Binary file not shown.

Binary file not shown.

View File

@ -1 +0,0 @@
libsoundio.so.2

Binary file not shown.

View File

@ -1 +0,0 @@
libsoundio.so.2.0.0

Binary file not shown.

View File

@ -1 +0,0 @@
libsoundio.so.2

Binary file not shown.

View File

@ -1 +0,0 @@
libsoundio.so.2.0.0

Binary file not shown.

View File

@ -27,43 +27,60 @@
#include "hsmodem.h"
typedef struct _AUDIOFILES_ {
char fn[256];
int duration;
} AUDIOFILES;
void close_a();
AUDIOFILES audiofiles[12] =
{
{"amsat", 1100},
{"qpsk", 1100},
{"psk8", 1100},
{"3000", 600},
{"4000", 600},
{"4410", 900},
{"4800", 900},
{"5500", 900},
{"6000", 600},
{"6600", 900},
{"7200", 900},
{"kbps", 1000},
};
const int h_len = 57;
float h[h_len];
firfilt_crcf qfilt = NULL;
float ratio;
msresamp_crcf anndecim = NULL;
char* getAudiofn(int aidx, char* ext)
void create_a()
{
static char filename[300];
strcpy(filename, "audio/");
strcat(filename, audiofiles[aidx].fn);
strcat(filename, ext);
return filename;
close_a();
// audio filter to reduce Audio BW for the 2k7 SSB channel
float fc = 0.055f; // cut off freq. normalized to 48000 (= 1.0)
liquid_firdes_kaiser(h_len, fc, 60.0f, 0.0f, h);
qfilt = firfilt_crcf_create(h, h_len);
// create arbitrary pre decimator
// if Audio SR is 48000 but caprate is 44100
ratio = (float)((float)caprate / 48000.0);
anndecim = msresamp_crcf_create(ratio, 40.0f);
}
void playAudioFLAC(int aidx)
void close_a()
{
if(qfilt) firfilt_crcf_destroy(qfilt);
qfilt = NULL;
if (anndecim) msresamp_crcf_destroy(anndecim);
anndecim = NULL;
}
float lowpass(float f)
{
if (qfilt == NULL)
{
printf("low pass filter not initialized\n");
return f;
}
liquid_float_complex inp, outp;
inp.real = f/5;
inp.imag = 0;
firfilt_crcf_push(qfilt, inp); // push input sample
firfilt_crcf_execute(qfilt, &outp); // compute output
return outp.real;
}
// destination: 1=transceiver, 2=loudspeaker, 3=both
void playAudioPCM(char* fn, int destination)
{
int resamp = 0;
int len;
int16_t d[100];
printf("play:%s, caprate:%d\n", getAudiofn(aidx, ".pcm"),caprate);
FILE* fp = fopen(getAudiofn(aidx, ".pcm"), "rb");
printf("play:%s, caprate:%d\n", fn,caprate);
FILE* fp = fopen(fn, "rb");
if (fp)
{
while ((len = fread(d, sizeof(int16_t), 100, fp)))
@ -71,24 +88,61 @@ void playAudioFLAC(int aidx)
for (int i = 0; i < len; i++)
{
float f = (float)d[i];
f /= 32768;
io_pb_write_fifo(f);
if (caprate == 48000)
// local playback at 48k, no filtering, no interpolation
if ((destination & 2) == 2)
{
if (++resamp >= 9)
int to = 4000;
int res;
while ((res = io_ls_fifo_usedspace()) > 10000)
{
resamp = 0;
io_pb_write_fifo(f);
if (--to == 0)
{
printf("timed out waiting for LS fifo. Res:%d\n",res);
fclose(fp);
return;
}
sleep_ms(1);
}
io_ls_write_fifo(f / 32768);
}
if (caprate == 44100)
{
unsigned int num_written = 0;
liquid_float_complex in;
liquid_float_complex out;
in.real = f;
in.imag = 0;
msresamp_crcf_execute(anndecim, &in, 1, &out, &num_written);
if (num_written != 1) continue;
f = out.real;
}
f = lowpass(f);
f /= 32768;
if ((destination & 1) == 1)
{
int to = 4000;
while (io_pb_fifo_usedspace() > 10000)
{
if (--to == 0)
{
printf("timed out waiting for PB fifo\n");
fclose(fp);
return;
}
sleep_ms(1);
}
io_pb_write_fifo(f);
}
// sync with soundcard
while (io_pb_fifo_usedspace() > 10000) sleep_ms(1);
}
if (len != 100) break;
}
fclose(fp);
printf("finished playing audio file\n");
}
else
printf("audio file not found\n");
@ -103,25 +157,44 @@ void sendAnnouncement()
if (++transmissions >= announcement)
{
create_a();
ann_running = 1;
transmissions = 0;
playAudioFLAC(0);
if (bitsPerSymbol == 2) playAudioFLAC(1);
else playAudioFLAC(2);
switch (linespeed)
if (sendIntro == 1)
{
case 3000: playAudioFLAC(3); break;
case 4000: playAudioFLAC(4); break;
case 4410: playAudioFLAC(5); break;
case 4800: playAudioFLAC(6); break;
case 5500: playAudioFLAC(7); break;
case 6000: playAudioFLAC(8); break;
case 6600: playAudioFLAC(9); break;
case 7200: playAudioFLAC(10); break;
char fn[500];
snprintf(fn, 499, "%s/oscardata/intro/intro.pcm", homepath);
fn[499] = 0;
playAudioPCM(fn, 1);
}
playAudioFLAC(11);
playAudioPCM("audio/amsat.pcm",1);
if (bitsPerSymbol == 1) playAudioPCM("audio/bpsk.pcm",1);
else if (bitsPerSymbol == 2) playAudioPCM("audio/qpsk.pcm",1);
else playAudioPCM("audio/psk8.pcm",1);
char s[100];
sprintf(s,"audio/%d.pcm", linespeed);
playAudioPCM(s,1);
playAudioPCM("audio/kbps.pcm",1);
ann_running = 0;
close_a();
}
}
void playIntro()
{
char fn[500];
snprintf(fn, 499, "%s/oscardata/intro/intro.pcm", homepath);
fn[499] = 0;
io_clear_voice_fifos();
create_a();
playAudioPCM(fn, 3);
close_a();
}

BIN
hsmodem/audio/1200.pcm Normal file

Binary file not shown.

BIN
hsmodem/audio/2400.pcm Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
hsmodem/audio/bpsk.pcm Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -31,22 +31,29 @@ void sendCodecToModulator(uint8_t* pdata, int len);
struct CODEC2 *pc2 = NULL;
int samplesPerPacket = 160;
int bytesPerPacket = 8;
firdecim_crcf decim48_8 = NULL;
firinterp_crcf interp8_48;
const int decfactor = 6;
void init_codec2()
{
close_codec2();
if (speedmode == 0)
pc2 = codec2_create(CODEC2_MODE_1600);
else if(speedmode == 1)
pc2 = codec2_create(CODEC2_MODE_2400);
else
pc2 = codec2_create(CODEC2_MODE_3200);
// create pre-decimator
decim48_8 = firdecim_crcf_create_kaiser(decfactor, 7, 40.0f);
firdecim_crcf_set_scale(decim48_8, 1.0f / (float)decfactor);
if (pc2 == NULL)
// create post-interpolator
interp8_48 = firinterp_crcf_create_kaiser(decfactor, 7, 40.0f);
switch (speedmode)
{
printf("cannot create CODEC2\n");
case 0: pc2 = codec2_create(CODEC2_MODE_700C); break;
case 1: pc2 = codec2_create(CODEC2_MODE_1600); break;
case 2: pc2 = codec2_create(CODEC2_MODE_2400); break;
default: pc2 = codec2_create(CODEC2_MODE_3200); break;
}
codec2_set_natural_or_gray(pc2, 0);
bytesPerPacket = codec2_bits_per_frame(pc2) / 8;
samplesPerPacket = codec2_samples_per_frame(pc2);
@ -56,10 +63,16 @@ void init_codec2()
void close_codec2()
{
if (pc2 != NULL)
{
codec2_destroy(pc2);
}
pc2 = NULL;
if (decim48_8 != NULL)
firdecim_crcf_destroy(decim48_8);
decim48_8 = NULL;
if (interp8_48 != NULL)
firinterp_crcf_destroy(interp8_48);
interp8_48 = NULL;
}
// encode 160 voice samples (8kS/s) into 64 bits output
@ -69,18 +82,24 @@ void encode_codec2(float f)
static int16_t sbuf[500]; // this is easily more than "samplesPerPacket" in any cases
static int fbuf_idx = 0;
uint8_t outbuf[50]; // this is easily more than "bytesPerPacket" in any cases
static liquid_float_complex fdec[decfactor];
if (pc2 == NULL) return;
if (pc2 == NULL || decim48_8 == NULL) return;
// this encoder is called with a sound card sample rate of 48000
// codec2 needs 8 kS/s, so we have to decimate by 6
fdec[decim].real = f;
fdec[decim].imag = 0;
if (++decim >= 6)
{
decim = 0;
liquid_float_complex y;
firdecim_crcf_execute(decim48_8, fdec, &y);
// here we have a sample rate of 8 kS/s
// one encoding call needs 160 samples
sbuf[fbuf_idx] = (int16_t)(f * 32768); // convert to short
sbuf[fbuf_idx] = (int16_t)(y.real * 32768); // convert to short
if (++fbuf_idx >= samplesPerPacket)
{
fbuf_idx = 0;
@ -109,8 +128,15 @@ void encode_codec2(float f)
float f = (float)spbbuf[i];
f /= 32768;
// here we have 8kS/s, need to interpolate to 48 kS/s
for(int x=0; x<6; x++)
io_ls_write_fifo(f);
liquid_float_complex inp;
inp.real = f;
inp.imag = 0;
liquid_float_complex outp[decfactor];
firinterp_crcf_execute(interp8_48, inp, outp);
for (int x = 0; x < decfactor; x++)
io_ls_write_fifo(outp[x].real);
}
}
}
@ -147,10 +173,9 @@ void toCodecDecoder_codec2(uint8_t* pdata, int len)
if (mfound)
{
//showbytestring("OPUS:", chunk + 1, opusPacketSize, opusPacketSize);
// codec loop mode: decode and play it
int16_t spbbuf[500];
//showbytestring("CODEC2:", chunk + 1, 3, 3);
codec2_decode(pc2, spbbuf, chunk+1);
for (int i = 0; i < samplesPerPacket; i++)
@ -158,8 +183,15 @@ void toCodecDecoder_codec2(uint8_t* pdata, int len)
float f = (float)spbbuf[i];
f /= 32768;
// here we have 8kS/s, need to interpolate to 48 kS/s
for (int x = 0; x < 6; x++)
io_ls_write_fifo(f);
liquid_float_complex inp;
inp.real = f;
inp.imag = 0;
liquid_float_complex outp[decfactor];
firinterp_crcf_execute(interp8_48, inp, outp);
for (int x = 0; x < decfactor; x++)
io_ls_write_fifo(outp[x].real);
}
}
}

View File

@ -205,3 +205,65 @@ void shiftleft(uint8_t *data, int shiftnum, int len)
}
}
}
void rotateBPSKsyms(uint8_t* src, uint8_t* dst, int len)
{
for (int i = 0; i < len; i++)
{
if (src[i] == 0) dst[i] = 1;
else dst[i] = 0;
}
}
uint8_t BPSK_backbuf[UDPBLOCKLEN * 8 / 1];
uint8_t* rotateBackBPSK(uint8_t* buf, int len, int rotations)
{
memcpy(BPSK_backbuf, buf, len);
if (rotations == 1)
{
for (unsigned int i = 0; i < (unsigned int)len; i++)
{
if (BPSK_backbuf[i] == 1) BPSK_backbuf[i] = 0;
else BPSK_backbuf[i] = 1;
}
}
return BPSK_backbuf;
}
uint8_t* convertBPSKSymToBytes(uint8_t* rxsymbols)
{
int sidx = 0;
for (int i = 0; i < UDPBLOCKLEN; i++)
{
rxbytebuf[i] = rxsymbols[sidx++] << (bitsPerSymbol * 7);
rxbytebuf[i] |= rxsymbols[sidx++] << (bitsPerSymbol * 6);
rxbytebuf[i] |= rxsymbols[sidx++] << (bitsPerSymbol * 5);
rxbytebuf[i] |= rxsymbols[sidx++] << (bitsPerSymbol * 4);
rxbytebuf[i] |= rxsymbols[sidx++] << (bitsPerSymbol * 3);
rxbytebuf[i] |= rxsymbols[sidx++] << (bitsPerSymbol * 2);
rxbytebuf[i] |= rxsymbols[sidx++] << (bitsPerSymbol * 1);
rxbytebuf[i] |= rxsymbols[sidx++] << (bitsPerSymbol * 0);
}
return rxbytebuf;
}
void convertBytesToSyms_BPSK(uint8_t* bytes, uint8_t* syms, int bytenum)
{
unsigned int symidx = 0;
for (int i = 0; i < bytenum; i++)
{
// 1 sym per 1 bit:
// convert next byte to 8 syms
syms[symidx++] = (bytes[i] >> 7) & 1;
syms[symidx++] = (bytes[i] >> 6) & 1;
syms[symidx++] = (bytes[i] >> 5) & 1;
syms[symidx++] = (bytes[i] >> 4) & 1;
syms[symidx++] = (bytes[i] >> 3) & 1;
syms[symidx++] = (bytes[i] >> 2) & 1;
syms[symidx++] = (bytes[i] >> 1) & 1;
syms[symidx++] = bytes[i] & 1;
}
}

View File

@ -30,60 +30,50 @@
#include <fftw3.h>
#endif
#define FFT_AUDIOSAMPLERATE 8000
uint16_t* mean(uint16_t* f);
#define FFT_AUDIOSAMPLERATE 800
double *din = NULL; // input data for fft
fftw_complex *cpout = NULL; // ouput data from fft
fftw_plan plan = NULL;
#define fft_rate (FFT_AUDIOSAMPLERATE / 10) // resolution: 10 Hz
int fftidx = 0;
int fftcnt = fft_rate/2+1; // number of output values
uint16_t fftout[FFT_AUDIOSAMPLERATE / 10/2+1];
float f_fftout[FFT_AUDIOSAMPLERATE / 10 / 2 + 1];
int fftcount; // number of output values
uint16_t fftout[FFT_AUDIOSAMPLERATE / 2 + 1];
float f_fftout[FFT_AUDIOSAMPLERATE / 2 + 1];
int downsamp = 0;
int downphase = 0;
int rxlevel_deteced = 0;
int rx_in_sync = 0;
msresamp_crcf fftdecim = NULL;
uint16_t *make_waterfall(float fre, int *retlen)
{
// Downsampling:
// needed 8000 bit/s
// caprate 48k: downsample by 6
// caprate 44,1k: downsample by 5,5
if (physcaprate == 48000)
if (++downsamp < 6) return NULL;
// TODO: the following simple resamp results in double peeks in fft
if (physcaprate == 44100)
{
if (downphase <= 1100)
{
if (++downsamp < 5) return NULL;
}
else
{
if (++downsamp < 6) return NULL;
}
if(++downphase >= 2000) downphase = 0;
}
downsamp = 0;
int fftrdy = 0;
// data come with 44.1k or 48k sample rate
// downsample to 800 to get 0-4k in 10 Hz steps
unsigned int num_written = 0;
liquid_float_complex in;
liquid_float_complex out;
in.real = fre;
in.imag = 0;
msresamp_crcf_execute(fftdecim, &in, 1, &out, &num_written);
if (num_written != 1) return NULL;
fre = out.real;
// fre are the float samples
// fill into the fft input buffer
din[fftidx++] = fre;
if(fftidx == fft_rate)
if(fftidx == FFT_AUDIOSAMPLERATE)
{
fftidx = 0;
// the fft buffer is full, execute the FFT
fftw_execute(plan);
for (int j = 0; j < fftcnt; j++)
for (int j = 0; j < fftcount; j++)
{
// calculate absolute value (magnitute without phase)
float fre = (float)cpout[j][0];
@ -111,9 +101,9 @@ uint16_t *make_waterfall(float fre, int *retlen)
// measure level at mid band
float midlevel = 0;
for (int e = 100; e < 300; e++)
for (int e = 100; e < 200; e++)
midlevel += f_fftout[e];
midlevel /= 200;
midlevel /= 100;
//calc difference in %
int idiff = (int)((edgelevel * 100) / midlevel);
@ -145,7 +135,7 @@ uint16_t *make_waterfall(float fre, int *retlen)
{
if (sig == 1)
{
printf("===>>> level detected, reset modem\n");
//printf("===>>> level detected, reset modem\n");
trigger_resetmodem = 1;
}
}
@ -155,33 +145,82 @@ uint16_t *make_waterfall(float fre, int *retlen)
if(fftrdy == 1)
{
*retlen = fftcnt;
return fftout;
*retlen = fftcount;
return mean(fftout);
}
return NULL;
}
void init_fft()
// smooth fft output
const int smoothX = 6; // must be an even number !
const int smoothY = 3;
int yidx = 0;
uint16_t* mean(uint16_t* f)
{
/*
char fn[300];
* storing to a file in the working directory may be a problem under Windows, so we do not use wisdom files
sprintf(fn, "capture_fft_%d", fft_rate); // wisdom file for each capture rate
static uint16_t fa[FFT_AUDIOSAMPLERATE / 2 + 1];
// first smooth X values
for (int x = 0; x < smoothX / 2; x++)
fa[x] = f[x];
fftw_import_wisdom_from_filename(fn);*/
for (int x = smoothX / 2; x < fftcount - smoothX / 2; x++)
{
fa[x] = 0;
for (int i = -smoothX/2; i < smoothX/2; i++)
fa[x] += f[x+i];
fa[x] /= smoothX;
}
din = (double *)fftw_malloc(sizeof(double) * fft_rate);
cpout = (fftw_complex *)fftw_malloc(sizeof(fftw_complex) * fft_rate);
for (int x = fftcount - smoothX / 2; x < fftcount; x++)
fa[x] = f[x];
plan = fftw_plan_dft_r2c_1d(fft_rate, din, cpout, FFTW_MEASURE);
//fftw_export_wisdom_to_filename(fn);
// smooth Y values
static uint16_t yarr[smoothY][FFT_AUDIOSAMPLERATE / 2 + 1];
for (int i = 0; i < fftcount; i++)
yarr[yidx][i] = fa[i];
if (++yidx >= smoothY) yidx = 0;
memset(fa, 0, FFT_AUDIOSAMPLERATE / 2 + 1);
for (int i = 0; i < fftcount; i++)
{
for (int j = 0; j < smoothY; j++)
fa[i] += yarr[j][i];
fa[i] /= smoothY;
}
return fa;
}
void exit_fft()
void _init_fft()
{
fftcount = FFT_AUDIOSAMPLERATE / 2 + 1; // number of output samples
// the FFT outputs 400 values from 0 to 4kHz with a resolution of 10 Hz
_exit_fft();
din = (double *)fftw_malloc(sizeof(double) * FFT_AUDIOSAMPLERATE);
cpout = (fftw_complex *)fftw_malloc(sizeof(fftw_complex) * fftcount);
plan = fftw_plan_dft_r2c_1d(FFT_AUDIOSAMPLERATE, din, cpout, FFTW_MEASURE);
// create arbitrary pre decimator
// decimate 44.1k or 48k down to 8000Hz
// the FFT rate is 800, but we feed it with 8000 Samplerate
// this results in a new fft every 100ms with a resolution of 10 Hz
float ratio = 10.0f * (float)FFT_AUDIOSAMPLERATE / (float)caprate;
fftdecim = msresamp_crcf_create(ratio, 40.0f);
}
void _exit_fft()
{
if(plan) fftw_destroy_plan(plan);
if(din) fftw_free(din);
if(cpout) fftw_free(cpout);
plan = NULL;
din = NULL;
cpout = NULL;
if (fftdecim) msresamp_crcf_destroy(fftdecim);
fftdecim = NULL;
}

View File

@ -85,17 +85,14 @@ void io_cap_write_fifo(float sample)
{
if (((io_cap_wridx + 1) % AUDIO_CAPTURE_BUFLEN) == io_cap_rdidx)
{
printf("cap fifo full\n");
//printf("cap fifo full\n");
return;
}
IO_CAP_LOCK;
io_cap_buffer[io_cap_wridx] = sample;
if (++io_cap_wridx >= AUDIO_CAPTURE_BUFLEN) io_cap_wridx = 0;
IO_CAP_UNLOCK();
// if monitoring is activated then write it also to the voice fifo
/*if (VoiceAudioMode == VOICEMODE_LISTENAUDIOIN)
toVoice(sample);*/
}
int io_cap_read_fifo(float* data)

View File

@ -31,7 +31,7 @@
CRITICAL_SECTION io_mic_crit_sec;
CRITICAL_SECTION io_ls_crit_sec;
#define IO_MIC_LOCK EnterCriticalSection(&io_mic_crit_sec)
#define IO_LS_LOCK EnterCriticalSection(&io_ls_crit_sec)
#define IO_LS_LOCK EnterCriticalSection(&io_ls_crit_sec)
void IO_MIC_UNLOCK()
{
if (&io_mic_crit_sec != NULL)

View File

@ -27,7 +27,7 @@
void Insert(uint8_t bit);
uint8_t* getPayload(uint8_t* rxb);
uint8_t rxbuffer[UDPBLOCKLEN*8/2+100]; // 3...bits per symbol QPSK, enough space also for QPSK and 8PSK, +100 ... reserve, just to be sure
uint8_t rxbuffer[UDPBLOCKLEN*8/1+100]; // 3...bits per symbol QPSK, enough space also for QPSK and 8PSK, +100 ... reserve, just to be sure
uint8_t rx_status = 0;
int framecounter = 0;
@ -38,20 +38,27 @@ int getPayload_error = 0;
uint8_t TXheaderbytes[HEADERLEN] = {0x53, 0xe1, 0xa6};
// corresponds to these QPSK symbols:
// bits: 01010011 11100001 10100110
// BPSK:
// syms: 01010011 11100001 10100110
// QPSK:
// syms: 1 1 0 3 3 2 0 1 2 2 1 2
// 8PSK:
// syms: 2 4 7 6 0 6 4 6
// BPSK
// each header has 24 symbols
// we have 2 constellations
uint8_t BPSK_headertab[2][HEADERLEN * 8 / 1];
// QPSK
// each header has 12 symbols
// we have 4 constellations
uint8_t QPSK_headertab[4][HEADERLEN*8/2];
uint8_t QPSK_headertab[4][HEADERLEN * 8 / 2];
// 8PSK
// each header has 8 symbols
// we have 8 constellations
uint8_t _8PSK_headertab[8][HEADERLEN*8/3];
uint8_t _8PSK_headertab[8][HEADERLEN * 8 / 3];
/*
8CONST: . Len 8: 02 04 07 06 00 06 04 06
@ -67,6 +74,12 @@ uint8_t _8PSK_headertab[8][HEADERLEN*8/3];
// init header tables
void init_packer()
{
// create the BPSK symbol table for the HEADER
// in all possible rotations
convertBytesToSyms_BPSK(TXheaderbytes, BPSK_headertab[0], 3);
for (int i = 1; i < 2; i++)
rotateBPSKsyms(BPSK_headertab[i - 1], BPSK_headertab[i], 24);
// create the QPSK symbol table for the HEADER
// in all possible rotations
convertBytesToSyms_QPSK(TXheaderbytes, QPSK_headertab[0], 3);
@ -77,9 +90,7 @@ void init_packer()
// in all possible rotations
convertBytesToSyms_8PSK(TXheaderbytes, _8PSK_headertab[0], 3);
for(int i=1; i<8; i++)
{
rotate8APSKsyms(_8PSK_headertab[i-1], _8PSK_headertab[i], 8);
}
/*for(int i=0; i<8; i++)
showbytestring((char*)"8CONST: ",_8PSK_headertab[i],8);*/
@ -160,7 +171,25 @@ int seekHeadersyms(int symnum)
int maxerr = MAXHEADERRS;
if(constellationSize == 4)
if (constellationSize == 2)
{
// BPSK
for (int tab = 0; tab < 2; tab++)
{
errs = 0;
for (int i = 0; i < HEADERLEN * 8 / 1; i++)
{
if (rxbuffer[i] != BPSK_headertab[tab][i])
errs++;
}
if (errs <= maxerr)
{
ret = tab;
break;
}
}
}
else if(constellationSize == 4)
{
// QPSK
for(int tab=0; tab<4; tab++)
@ -199,7 +228,7 @@ int seekHeadersyms(int symnum)
if (ret != -1)
{
//printf("header detected at symbol:%d, headererrors:%d\n", symnum,errs);
//printf("header detected at symbol:%d, headererrors:%d rotation:%d\n", symnum,errs,ret);
return ret;
}
@ -230,21 +259,26 @@ uint8_t *unpack_data(uint8_t *rxd, int len)
rxbuffer[frmlen-1] = rxd[sym];
symnum++;
//showbytestring((char*)"rx: ",rxbuffer,30);
int rotations = seekHeadersyms(symnum);
if(rotations != -1)
{
//showbytestring((char*)"rx: ", rxbuffer, 30,30);
// rxbuffer contains all symbols of the received frame
// convert to bytes
uint8_t *rxbytes = NULL;
if(constellationSize == 4)
if (constellationSize == 2)
{
uint8_t* backbuf = rotateBackBPSK(rxbuffer, frmlen, rotations);
rxbytes = convertBPSKSymToBytes(backbuf);
}
else if(constellationSize == 4)
{
uint8_t *backbuf = rotateBackQPSK(rxbuffer,frmlen,rotations);
rxbytes = convertQPSKSymToBytes(backbuf);
}
else
else if (constellationSize == 8)
{
uint8_t *backbuf = rotateBack8APSK(rxbuffer,frmlen,rotations);
rxbytes = convert8PSKSymToBytes(backbuf,UDPBLOCKLEN);
@ -301,7 +335,6 @@ uint8_t *getPayload(uint8_t *rxb)
//showbytestring((char *)"orig: ",rxb+HEADERLEN,30);
// unscramble
uint8_t *rxarray = RX_Scramble(rxb+HEADERLEN, FECBLOCKLEN);
// calculate the FEC over the received data
// and fill a frame structure

View File

@ -55,7 +55,7 @@ int UdpDataPort_ModemToApp = 40133;
// op mode depending values
// default mode if not set by the app
int speedmode = 2;
int speedmode = 4;
int bitsPerSymbol = 2; // QPSK=2, 8PSK=3
int constellationSize = 4; // QPSK=4, 8PSK=8
@ -65,6 +65,7 @@ char appIP[20] = { 0 };
int fixappIP = 0;
int restart_modems = 0;
int trigger_resetmodem = 0;
char homepath[1000] = { 0 };
int caprate = 44100;
int physcaprate = 44100;
@ -90,6 +91,7 @@ int init_audio_result = 0;
int init_voice_result = 0;
int safemode = 0;
int sendIntro = 0;
int main(int argc, char* argv[])
{
@ -150,10 +152,47 @@ int main(int argc, char* argv[])
}
}
#endif
// get user home path
#ifdef _WIN32_
strcpy(homepath, getenv("USERPROFILE"));
char nd[1000];
sprintf(nd, "%s/oscardata", homepath);
if (CreateDirectory(nd, NULL) || ERROR_ALREADY_EXISTS == GetLastError())
{
sprintf(nd, "%s\\oscardata\\intro", homepath);
CreateDirectory(nd, NULL);
}
#endif
#ifdef _LINUX_
char* ph;
if ((ph = getenv("HOME")) == NULL)
{
ph = getpwuid(getuid())->pw_dir;
}
if (ph != NULL)
strcpy(homepath, ph);
else
*homepath = 0;
struct stat st = { 0 };
char nd[1000];
sprintf(nd, "%s/oscardata", homepath);
if (stat(nd, &st) == -1)
{
mkdir(nd, 0755);
sprintf(nd, "%s/oscardata/intro", homepath);
if (stat(nd, &st) == -1)
mkdir(nd, 0755);
}
#endif
printf("user home path:<%s>\n", homepath);
init_packer();
initFEC();
init_fft();
// start udp RX to listen for broadcast search message from Application
UdpRxInit(&BC_sock_AppToModem, UdpBCport_AppToModem, &bc_rxdata, &keeprunning);
@ -178,10 +217,27 @@ int main(int argc, char* argv[])
float f;
while (io_mic_read_fifo(&f))
{
io_ls_write_fifo(f);
io_ls_write_fifo(f);
}
}
if (VoiceAudioMode == VOICEMODE_RECORD)
{
// loop voice mic to LS, and record into PCM file
float f;
while (io_mic_read_fifo(&f))
{
io_saveStream(f);
io_ls_write_fifo(f);
}
}
if (VoiceAudioMode == VOICEMODE_PLAYBACK)
{
playIntro();
VoiceAudioMode = VOICEMODE_OFF;
}
if (VoiceAudioMode == VOICEMODE_CODECLOOP || VoiceAudioMode == VOICEMODE_DV_FULLDUPLEX)
{
// send mic to codec
@ -194,8 +250,8 @@ int main(int argc, char* argv[])
// demodulate incoming audio data stream
static int old_tm = 0;
int tm = getus();
if (tm >= (old_tm + 1000000))
int tm = getms();
if (tm >= (old_tm + 1000))
{
// read Audio device list every 1s
io_readAudioDevices();
@ -205,9 +261,9 @@ int main(int argc, char* argv[])
if (dret == 0)
{
// no new data in fifo
// not important how long to sleep, 10ms is fine
#ifdef _LINUX_
sleep_ms(10);
// not important how long to sleep, 10ms is fine
sleep_ms(10);
#endif
}
}
@ -235,7 +291,11 @@ typedef struct {
} SPEEDRATE;
// AudioRate, TX-Resampler, RX-Resampler/4, bit/symbol, Codec-Rate
SPEEDRATE sr[8] = {
SPEEDRATE sr[10] = {
// BPSK modes
{48000, 40,10, 1, 1200, 800},
{48000, 20, 5, 1, 2400, 2000},
// QPSK modes
{48000, 32, 8, 2, 3000, 2400},
{48000, 24, 6, 2, 4000, 3200},
@ -246,7 +306,7 @@ SPEEDRATE sr[8] = {
{44100, 24, 6, 3, 5500, 4400},
{48000, 24, 6, 3, 6000, 4800},
{44100, 20, 5, 3, 6600, 5200},
{48000, 20, 5, 3, 7200, 6000}
{48000, 20, 5, 3, 7200, 6000},
};
void startModem()
@ -263,6 +323,7 @@ void startModem()
// int TX audio and modulator
close_dsp();
init_audio_result = io_init_sound(playbackDeviceName, captureDeviceName);
_init_fft();
init_dsp();
}
@ -290,6 +351,9 @@ void io_setAudioDevices(uint8_t pbvol, uint8_t capvol, uint8_t announce, uint8_t
// called from UDP RX thread for Broadcast-search from App
void bc_rxdata(uint8_t* pdata, int len, struct sockaddr_in* rxsock)
{
static int lastms = 0; // time of last received BC message
int actms = getms();
if (len > 0 && pdata[0] == 0x3c)
{
/* searchmodem message received
@ -302,7 +366,8 @@ void bc_rxdata(uint8_t* pdata, int len, struct sockaddr_in* rxsock)
* 4 ... DV loudspeaker volume
* 5 ... DV mic volume
* 6 ... safe mode number
* 7..9 ... unused
* 7 ... send Intro
* 8..9 ... unused
* 10 .. 109 ... PB device name
* 110 .. 209 ... CAP device name
*/
@ -314,7 +379,16 @@ void bc_rxdata(uint8_t* pdata, int len, struct sockaddr_in* rxsock)
{
if (strcmp(appIP, rxip))
{
printf("new app IP: %s, restarting modems\n", rxip);
if (appIP[0] != 0)
{
// there was an appIP already
// before accepting this new one, wait 3 seconds
int ts = actms - lastms;
printf("new app IP: %s since %d, restarting modems\n", rxip,ts);
if (ts < 3000)
return;
}
printf("first app IP: %s, restarting modems\n", rxip);
restart_modems = 1;
}
strcpy(appIP, rxip);
@ -344,6 +418,9 @@ void bc_rxdata(uint8_t* pdata, int len, struct sockaddr_in* rxsock)
//printf("%d %d %d %d %d %d %d \n",pdata[1], pdata[2], pdata[3], pdata[4], pdata[5], pdata[6], pdata[7]);
io_setAudioDevices(pdata[1], pdata[2], pdata[3], pdata[4], pdata[5], (char*)(pdata + 10), (char*)(pdata + 110));
safemode = pdata[6];
sendIntro = pdata[7];
lastms = actms;
}
}
@ -357,7 +434,7 @@ void appdata_rxdata(uint8_t* pdata, int len, struct sockaddr_in* rxsock)
if (type == 16)
{
// Byte 1 contains the resampler ratio for TX and RX modem
if (pdata[1] >= 8)
if (pdata[1] >= 12)
{
printf("wrong speedmode %d, ignoring\n", pdata[1]);
return;
@ -406,6 +483,7 @@ void appdata_rxdata(uint8_t* pdata, int len, struct sockaddr_in* rxsock)
{
// reset liquid RX modem
resetModem();
io_clear_audio_fifos();
return;
}
@ -458,8 +536,9 @@ void appdata_rxdata(uint8_t* pdata, int len, struct sockaddr_in* rxsock)
printf("LS:<%s> MIC:<%s> Mode:%d codec:%d\n", lsDeviceName, micDeviceName, VoiceAudioMode, codec);
// init voice audio
if (VoiceAudioMode == 0)
if (VoiceAudioMode == VOICEMODE_OFF)
{
io_saveStream(0.0f); // close recording
close_voiceproc();
io_close_voice();
}
@ -554,7 +633,8 @@ void toGR_sendData(uint8_t* data, int type, int status, int repeat)
// called by liquid demodulator for received data
void GRdata_rxdata(uint8_t* pdata, int len, struct sockaddr_in* rxsock)
{
static int fnd = 0;
static int lasttime = -1;
static int triggertime = 0;
// raw symbols
uint8_t* pl = unpack_data(pdata, len);
@ -573,27 +653,63 @@ void GRdata_rxdata(uint8_t* pdata, int len, struct sockaddr_in* rxsock)
if (*(pl + 3) != 0) // minfo=0 ... just a filler, ignore
toCodecDecoder(pl + 10, PAYLOADLEN);
}
fnd = 0;
trigger_resetmodem = 0;
rx_in_sync = 1;
lasttime = getms();
}
else
{
// no frame found
// if longer ws seconds nothing found, reset liquid RX modem
// comes here with symbol rate, i.e. 4000 S/s
int ws = 5;
int wt = sr[speedmode].audio / sr[speedmode].tx;
if (++fnd >= (wt * ws) || trigger_resetmodem)
int bps = sr[speedmode].linespeed;
// time for one frame [ms]
int frmlen = UDPBLOCKLEN * 8;
int tmfrm_ms = (frmlen * 1000) / bps;
int acttm = getms();
if (lasttime == -1)
{
fnd = 0;
lasttime = acttm;
return;
}
int tdiff = (acttm - lasttime); //ms
int elapsed_frames = tdiff / tmfrm_ms;
//printf("difft:%d elfrm:%d\n", tdiff, elapsed_frames);
if (trigger_resetmodem == 1)
{
trigger_resetmodem = 2;
//printf("set triggertime\n");
triggertime = getms();
}
if ((getms() - triggertime) > 1000 && trigger_resetmodem == 2)
{
printf("signal detected, reset RX modem\n");
trigger_resetmodem = 0;
rx_in_sync = 0;
//printf("no signal detected %d, reset RX modem\n", wt);
resetModem();
lasttime = acttm;
}
else if (fnd >= wt)
if (tdiff > 5)
{
// in any case, only every 5s or longer
if (elapsed_frames > 2)
{
// reset modem if more than 2 frames have not been received
trigger_resetmodem = 0;
rx_in_sync = 0;
//printf("no signal detected, reset RX modem\n");
resetModem();
lasttime = acttm;
}
}
if (elapsed_frames > 5)
{
// if > 5 frames not recieved, mark "not in sync"
rx_in_sync = 0;
}
}

View File

@ -14,6 +14,7 @@
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <signal.h>
#include <stdlib.h>
@ -84,7 +85,9 @@ enum _VOICEMODES_ {
VOICEMODE_INTERNALLOOP,
VOICEMODE_CODECLOOP,
VOICEMODE_DV_FULLDUPLEX,
VOICEMODE_DV_RXONLY
VOICEMODE_DV_RXONLY,
VOICEMODE_RECORD,
VOICEMODE_PLAYBACK
};
void init_packer();
@ -97,6 +100,10 @@ void convertBytesToSyms_8PSK(uint8_t* bytes, uint8_t* syms, int bytenum);
uint8_t* convertQPSKSymToBytes(uint8_t* rxsymbols);
uint8_t* convert8PSKSymToBytes(uint8_t* rxsymbols, int len);
uint8_t* rotateBackBPSK(uint8_t* buf, int len, int rotations);
uint8_t* convertBPSKSymToBytes(uint8_t* rxsymbols);
void convertBytesToSyms_BPSK(uint8_t* bytes, uint8_t* syms, int bytenum);
void rotateBPSKsyms(uint8_t* src, uint8_t* dst, int len);
void rotateQPSKsyms(uint8_t* src, uint8_t* dst, int len);
void rotate8PSKsyms(uint8_t* src, uint8_t* dst, int len);
@ -140,7 +147,7 @@ void setVolume_voice(int pbcap, int v);
void sendAnnouncement();
void sleep_ms(int ms);
int getus();
int getms();
void GRdata_rxdata(uint8_t* pdata, int len, struct sockaddr_in* rxsock);
void toGR_sendData(uint8_t* data, int type, int status, int repeat);
@ -151,8 +158,8 @@ int demodulator();
void sendToModulator(uint8_t* d, int len);
void resetModem();
void close_dsp();
void init_fft();
void exit_fft();
void _init_fft();
void _exit_fft();
void showbytestringf(char* title, float* data, int totallen, int anz);
uint16_t* make_waterfall(float fre, int* retlen);
@ -193,6 +200,10 @@ 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);
void io_saveStream(float f);
void playIntro();
void io_clear_voice_fifos();
extern int speedmode;
extern int bitsPerSymbol;
@ -229,6 +240,8 @@ extern float softwareLSvolume;
extern int physcaprate;
extern int restart_modems;
extern int safemode;
extern char homepath[];
extern int sendIntro;
#ifdef _LINUX_
int isRunning(char* prgname);

View File

@ -49,12 +49,14 @@ void close_dsp()
modulation_scheme getMod()
{
if (bitsPerSymbol == 1)
return LIQUID_MODEM_BPSK;
if(bitsPerSymbol == 2)
return LIQUID_MODEM_QPSK;
else
{
if (bitsPerSymbol == 3)
return LIQUID_MODEM_APSK8;
}
return LIQUID_MODEM_QPSK;
}
// =========== MODULATOR ==================================================
@ -108,10 +110,10 @@ void init_modulator()
// calculate filter coeffs
unsigned int h_len_NumFilterCoeefs = 2 * k_SampPerSymb * m_filterDelay_Symbols + 1;
float h[1000];
if (h_len_NumFilterCoeefs >= 1000)
float h[4000];
if (h_len_NumFilterCoeefs >= 4000)
{
printf("h in h_len_NumFilterCoeefs too small\n");
printf("h in h_len_NumFilterCoeefs too small, need %d\n", h_len_NumFilterCoeefs);
return;
}
liquid_firdes_prototype( LIQUID_FIRFILT_RRC,
@ -150,9 +152,12 @@ void sendToModulator(uint8_t *d, int len)
printf("syms in symanz too small\n");
return;
}
if(bitsPerSymbol == 2)
if (bitsPerSymbol == 1)
convertBytesToSyms_BPSK(d, syms, len);
else if (bitsPerSymbol == 2)
convertBytesToSyms_QPSK(d, syms, len);
else
else if (bitsPerSymbol == 3)
convertBytesToSyms_8PSK(d, syms, len);
for(int i=0; i<symanz; i++)
@ -175,10 +180,10 @@ void modulator(uint8_t sym_in)
//printf("TX ================= sample: %f + i%f\n", sample.real, sample.imag);
// interpolate by k_SampPerSymb
liquid_float_complex y[100];
if (k_SampPerSymb >= 100)
liquid_float_complex y[400];
if (k_SampPerSymb >= 400)
{
printf("y in k_SampPerSymb too small\n");
printf("y in k_SampPerSymb too small, need %d\n", k_SampPerSymb);
return;
}
@ -224,7 +229,7 @@ float As_adecim = 60.0f; // resampling filter stop-band attenuation [dB]
// symtrack parameters
int ftype_st = LIQUID_FIRFILT_RRC;
unsigned int k_st = 4; // samples per symbol
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.9f; // loop filter bandwidth
@ -342,11 +347,15 @@ void getMax(float fv)
}
}
#define CONSTPOINTS 400
int demodulator()
{
static liquid_float_complex ccol[100];
static liquid_float_complex ccol[500];
static int ccol_idx = 0;
static int16_t const_re[CONSTPOINTS];
static int16_t const_im[CONSTPOINTS];
static int const_idx = 0;
if(dnnco == NULL) return 0;
// get one received sample
@ -355,21 +364,19 @@ static int ccol_idx = 0;
if(ret == 0) return 0;
if (VoiceAudioMode == VOICEMODE_LISTENAUDIOIN)
{
io_ls_write_fifo(f);
}
// input volume
f *= softwareCAPvolume;
getMax(f);
make_FFTdata(f * 60);
make_FFTdata(f * 100);
if (caprate == 44100 && physcaprate == 48000)
{
// the sound card capture for a VAC always works with 48000 because
// a VAC cannot be set to a specific cap rate in shared mode
// downsample 48k to 44.1k
unsigned int num_written = 0;
liquid_float_complex in;
liquid_float_complex out;
@ -380,8 +387,7 @@ static int ccol_idx = 0;
f = out.real;
}
// downconvert into baseband
// still at soundcard sample rate
// downconvert 1,5kHz into baseband, still at soundcard sample rate
nco_crcf_step(dnnco);
liquid_float_complex in;
@ -399,59 +405,46 @@ 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);
// the output of the pre decimator is exactly one sample in y
unsigned int num_written = 1;
unsigned int num_symbols_sync;
liquid_float_complex syms;
unsigned int nsym_out; // demodulated output symbol
km_symtrack_execute(y, &syms, &num_symbols_sync, &nsym_out);
for (unsigned int sa = 0; sa < num_written; sa++)
if (num_symbols_sync > 1) printf("symtrack_cccf_execute %d output symbols ???\n", num_symbols_sync);
if (num_symbols_sync != 0)
{
unsigned int num_symbols_sync;
liquid_float_complex syms;
unsigned int nsym_out; // output symbol
km_symtrack_execute(y, &syms, &num_symbols_sync, &nsym_out);
measure_speed_syms(1); // do NOT remove, used for speed display in GUI
if (num_symbols_sync > 1) printf("symtrack_cccf_execute %d output symbols ???\n", num_symbols_sync);
if (num_symbols_sync != 0)
// try to extract a complete frame
uint8_t symb = nsym_out;
if (bitsPerSymbol == 2) symb ^= (symb >> 1);
GRdata_rxdata(&symb, 1, NULL);
// send the data "as is" to app for Constellation Diagram
// collect values until a UDP frame is full
const_re[const_idx] = (int16_t)(syms.real * 15000.0f);
const_im[const_idx] = (int16_t)(syms.imag * 15000.0f);
if (++const_idx >= CONSTPOINTS)
{
unsigned int sym_out; // output symbol
sym_out = nsym_out;
uint8_t txpl[CONSTPOINTS * sizeof(int16_t) * 2 + 1];
int idx = 0;
txpl[idx++] = 5; // type 5: IQ data follows
measure_speed_syms(1); // do NOT remove, used for speed display in GUI
// try to extract a complete frame
uint8_t symb = sym_out;
if (bitsPerSymbol == 2) symb ^= (symb >> 1);
GRdata_rxdata(&symb, 1, NULL);
// send the data "as is" to app for Constellation Diagram
// we have about 2000 S/s, but this many points would make the GUI slow
// so we send only every x
static int ev = 0;
if (++ev >= 5)//10)
for (int i = 0; i < CONSTPOINTS; i++)
{
ev = 0;
int32_t re = (int32_t)(syms.real * 16777216.0);
int32_t im = (int32_t)(syms.imag * 16777216.0);
uint8_t txpl[13];
int idx = 0;
txpl[idx++] = 5; // type 5: IQ data follows
uint32_t sy = 0x3e8;
txpl[idx++] = sy >> 24;
txpl[idx++] = sy >> 16;
txpl[idx++] = sy >> 8;
txpl[idx++] = sy;
txpl[idx++] = re >> 24;
txpl[idx++] = re >> 16;
txpl[idx++] = re >> 8;
txpl[idx++] = re;
txpl[idx++] = im >> 24;
txpl[idx++] = im >> 16;
txpl[idx++] = im >> 8;
txpl[idx++] = im;
sendUDP(appIP, UdpDataPort_ModemToApp, txpl, 13);
txpl[idx++] = (uint8_t)(const_re[i] >> 8);
txpl[idx++] = (uint8_t)(const_re[i]);
txpl[idx++] = (uint8_t)(const_im[i] >> 8);
txpl[idx++] = (uint8_t)(const_im[i]);
}
sendUDP(appIP, UdpDataPort_ModemToApp, txpl, sizeof(txpl));
const_idx = 0;
}
}

View File

@ -43,7 +43,7 @@ int isRunning(char *prgname)
void sighandler(int signum)
{
printf("program stopped by signal\n");
exit_fft();
_exit_fft();
keeprunning = 0;
close(BC_sock_AppToModem);
}
@ -77,7 +77,7 @@ void closeAllandTerminate()
io_close_audio();
io_close_voice();
// close fft
exit_fft();
_exit_fft();
// close codec2 and opus
close_codec2();
close_voiceproc();

View File

@ -36,6 +36,8 @@ struct SoundIoDevice* io_cap_device = NULL;
struct SoundIoInStream* instream = NULL;
struct SoundIoOutStream* outstream = NULL;
float latenz = 0.1f;
typedef struct _AUDIODEV_ {
int in_out = 0; // 0=in, 1=out
@ -529,7 +531,7 @@ int io_init_sound(char *pbname, char *capname)
instream->sample_rate = AUDIO_SAMPRATE;
physcaprate = AUDIO_SAMPRATE;
}
instream->software_latency = 0.0;
instream->software_latency = latenz;
instream->read_callback = read_callback;
instream->overflow_callback = overflow_callback;
instream->userdata = NULL;
@ -593,7 +595,7 @@ int io_init_sound(char *pbname, char *capname)
outstream->format = SoundIoFormatFloat32NE;
outstream->sample_rate = caprate;
outstream->software_latency = 1.0;
outstream->software_latency = latenz;
outstream->write_callback = write_callback;
outstream->underflow_callback = underflow_callback;
outstream->userdata = NULL;

View File

@ -31,16 +31,18 @@ int spdarr[MAXSPDARR];
int spdarrbps[MAXSPDARR];
#ifdef _LINUX_
int getus()
int getms()
{
struct timeval tv;
gettimeofday(&tv, NULL);
return tv.tv_sec * 1000000 + tv.tv_usec;
uint64_t at = tv.tv_sec * 1000000 + tv.tv_usec;
at = at / 1000;
return (int)at;
}
#endif
#ifdef _WIN32_
int getus()
int getms()
{
int actms;
@ -48,7 +50,7 @@ int getus()
GetSystemTime(&st);
actms = st.wSecond * 1000 + (int)st.wMilliseconds;
return actms * 1000;
return actms;
}
#endif
@ -115,7 +117,7 @@ void measure_speed_syms(int len)
static int lasttim = 0;
static int elems = 0;
int tim = getus();
int tim = getms();
int timespan = tim - lasttim;
if(timespan < 0)
{
@ -125,10 +127,10 @@ void measure_speed_syms(int len)
elems += len;
if(timespan < 1000000) return;
if(timespan < 1000) return;
double dspd = elems;
dspd = dspd * 1e6 / timespan;
dspd = dspd * 1e3 / timespan;
speed = meanval((int)dspd) * bitsPerSymbol;
// here we have number of elements after 1s
@ -143,7 +145,7 @@ void measure_speed_bps(int len)
static int lasttim = 0;
static int elems = 0;
int tim = getus();
int tim = getms();
int timespan = tim - lasttim;
if (timespan < 0)
{
@ -153,14 +155,14 @@ void measure_speed_bps(int len)
elems += len;
if (timespan < 1000000) return;
if (timespan < 1000) return;
double dspd = elems;
dspd = dspd * 1e6 / timespan;
dspd = dspd * 1e3 / timespan;
speed = meanvalbps((int)dspd);
// here we have number of elements after 1s
//printf(" ======================= %d bit/s\n", speed);
printf(" ======================= %d bit/s\n", speed);
elems = 0;
lasttim = tim;

View File

@ -35,6 +35,8 @@ struct SoundIoInStream* inmicstream = NULL;
bool lsrawdev = true;
bool micrawdev = true;
float latency = 0.1f;
void read_voicecallback(struct SoundIoInStream* instream, int frame_count_min, int frame_count_max)
{
int err;
@ -241,10 +243,18 @@ int io_init_voice(char* lsname, char* micname)
}
char* lsdevid = getDevID(lsname, 1);
if (lsdevid == NULL) return 0;
if (lsdevid == NULL)
{
printf("no lsdevid for %s\n",lsname);
return 0;
}
char* micdevid = getDevID(micname, 0);
if (micdevid == NULL) return 0;
if (micdevid == NULL)
{
printf("no micdevid for %s\n", micname);
return 0;
}
soundio_flush_events(voice_soundio);
// under Windows we usually use raw devices. This does not work with
@ -304,7 +314,7 @@ int io_init_voice(char* lsname, char* micname)
else
inmicstream->format = SoundIoFormatFloat32NE;
inmicstream->sample_rate = VOICE_SAMPRATE;
inmicstream->software_latency = 0.0;
inmicstream->software_latency = latency;
inmicstream->read_callback = read_voicecallback;
inmicstream->overflow_callback = overflow_voicecallback;
inmicstream->userdata = NULL;
@ -315,7 +325,7 @@ int io_init_voice(char* lsname, char* micname)
}
if ((err = soundio_instream_start(inmicstream))) {
fprintf(stderr, "unable to start voice input device: %s", soundio_strerror(err));
printf("unable to start voice input device: %s", soundio_strerror(err));
return 0;
}
init_voice_result |= 2;
@ -354,8 +364,6 @@ int io_init_voice(char* lsname, char* micname)
return 0;
}
printf("pb raw: %s\n", io_ls_device->is_raw ? "raw" : "---");
// create playback callback
outlsstream = soundio_outstream_create(io_ls_device);
if (!outlsstream) {
@ -369,7 +377,7 @@ int io_init_voice(char* lsname, char* micname)
else
outlsstream->format = SoundIoFormatFloat32NE;
outlsstream->sample_rate = VOICE_SAMPRATE;
outlsstream->software_latency = 0.0;
outlsstream->software_latency = latency;
outlsstream->write_callback = write_voicecallback;
outlsstream->underflow_callback = underflow_voicecallback;
outlsstream->userdata = NULL;
@ -411,3 +419,44 @@ void io_close_voice()
voice_soundio = NULL;
}
void io_saveStream(float f)
{
static FILE* fw = NULL;
static int old_VoiceAudioMode = 0;
if (VoiceAudioMode != old_VoiceAudioMode)
{
if (old_VoiceAudioMode == VOICEMODE_OFF && VoiceAudioMode == VOICEMODE_RECORD)
{
char fn[500];
snprintf(fn, 499, "%s/oscardata/intro/intro.pcm", homepath);
fn[499] = 0;
// audio was switched on, open file to save PCM stream
if (fw) fclose(fw);
fw = fopen(fn, "wb");
if (!fw) printf("cannot open pcm file:%s\n",fn);
else printf("AUDIO RECORDING:%s ...\n Speak, then switch off\n",fn);
}
if (VoiceAudioMode == VOICEMODE_OFF)
{
// audio was switched off, stop recording
if (fw)
{
fclose(fw);
printf("AUDIO RECORDING off\n");
}
fw = NULL;
}
old_VoiceAudioMode = VoiceAudioMode;
}
if (fw)
{
if (f > 1) f = 1;
if (f < -1) f = -1;
int16_t sh = (int16_t)(f * 32768.0f);
fwrite(&sh, sizeof(int16_t), 1, fw);
}
}

BIN
hsmodemLinux/audio/1200.pcm Normal file

Binary file not shown.

BIN
hsmodemLinux/audio/2400.pcm Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Some files were not shown because too many files have changed in this diff Show More