spdxl/src/radio.c

399 wiersze
10 KiB
C

/*
* dxlAPRS toolchain
*
* Copyright (C) Christian Rabler <oe5dxl@oevsv.at>
*
* SPDX-License-Identifier: GPL-2.0+
*/
#define X2C_int32
#define X2C_index32
#ifndef X2C_H_
#include "X2C.h"
#endif
#define radio_C_
#ifndef osi_H_
#include "osi.h"
#endif
#include <osic.h>
#ifndef aprsstr_H_
#include "aprsstr.h"
#endif
#ifndef sdr_H_
#include "sdr.h"
#endif
/* fm stereo radio with rtl_tcp iq by OE5DXL */
struct STICKPARM;
struct STICKPARM {
uint32_t val;
char ok0;
};
#define radio_OFFSET 100
/* khz from center frequency to avoid adc dc error area */
#define radio_DEEMPHASIS 0.05
/* check value ... */
static int32_t fd;
static uint32_t sndw;
static short sndbuf[1024];
static struct sdr_RX rxx;
static sdr_pRX prx[17];
static short sampx[64];
static char url[1001];
static char port[1001];
static char soundfn[1001];
static char mono;
static char verb;
static char isstereo;
static uint32_t afc;
static uint32_t tune;
static struct STICKPARM stickparm[256];
static void Usage(char text[], uint32_t text_len)
{
X2C_PCOPY((void **)&text,text_len);
osi_WrStr(" usage: ", 9ul);
osi_WrStrLn(text, text_len);
X2C_ABORT();
X2C_PFREE(text);
} /* end Usage() */
static void fix(const char s[], uint32_t s_len, uint32_t * p,
float * x, char * ok0)
{
float m;
m = 1.0f;
*ok0 = 0;
*x = 0.0f;
while ((uint8_t)s[*p]>='0' && (uint8_t)s[*p]<='9' || s[*p]=='.') {
if (s[*p]=='.') m = 0.1f;
else if (m==1.0f) {
*x = *x*10.0f+(float)((uint32_t)(uint8_t)s[*p]-48UL);
}
else {
*x = *x+(float)((uint32_t)(uint8_t)s[*p]-48UL)*m;
m = m*0.1f;
}
*ok0 = 1;
++*p;
}
} /* end fix() */
static void Parms(void)
{
char s[1001];
float fr;
uint32_t n;
uint32_t m;
int32_t ni;
char ok0;
mono = 0;
verb = 0;
afc = 50UL;
strncpy(url,"127.0.0.1",1001u);
strncpy(port,"1234",1001u);
soundfn[0] = 0;
tune = 100000000UL;
for (;;) {
osi_NextArg(s, 1001ul);
if (s[0U]==0) break;
if ((s[0U]=='-' && s[1U]) && s[2U]==0) {
if (s[1U]=='m') mono = 1;
else if (s[1U]=='s') osi_NextArg(soundfn, 1001ul);
else if (s[1U]=='t') {
osi_NextArg(s, 1001ul); /* url */
n = 0UL;
while ((n<1000UL && s[n]) && s[n]!=':') {
if (n<1000UL) url[n] = s[n];
++n;
}
if (n>1000UL) n = 1000UL;
url[n] = 0;
if (s[n]==':') {
m = 0UL;
++n;
while ((n<1000UL && s[n]) && m<1000UL) {
port[m] = s[n];
++n;
++m;
}
if (m>1000UL) m = 1000UL;
port[m] = 0;
}
}
else if (s[1U]=='f') {
/* MHz */
osi_NextArg(s, 1001ul);
n = 0UL;
fix(s, 1001ul, &n, &fr, &ok0);
if (ok0) {
if (fr>2000.0f && fr<=4.294967295E+9f) {
tune = (uint32_t)X2C_TRUNCC(fr+0.5f,0UL,
X2C_max_longcard);
}
else if (fr>1.0f && fr<=4294.0f) {
tune = (uint32_t)X2C_TRUNCC(fr*1.E+6f+0.5f,0UL,
X2C_max_longcard);
}
else Usage(" -f <mhz> or -f <hz>", 21ul);
}
else Usage(" -f <mhz> or -f <hz>", 21ul);
}
else if (s[1U]=='p') {
osi_NextArg(s, 1001ul);
if (aprsstr_StrToCard(s, 1001ul, &m) && m<256UL) {
osi_NextArg(s, 1001ul);
if (aprsstr_StrToInt(s, 1001ul, &ni)) {
stickparm[m].val = (uint32_t)ni; /* stick parameter */
stickparm[m].ok0 = 1;
}
else Usage(" -p <cmd> <value>", 18ul);
}
else Usage(" -p <cmd> <value>", 18ul);
}
else if (s[1U]=='a') {
/* afc */
osi_NextArg(s, 1001ul);
if (!aprsstr_StrToCard(s, 1001ul, &afc) || afc>200UL) {
Usage(" -a <khz>", 10ul);
}
}
else if (s[1U]=='v') verb = 1;
else if (s[1U]=='h') {
osi_WrStrLn("Stereo UKW Radio from rtl:tcp (8 bit IQ via tcpip to\
2 channel 16 bit pcm file/pipe ", 85ul);
osi_WrStrLn(" -a <khz> max afc <kHz> (50) or 0 for off",
48ul);
osi_WrStrLn(" -f <freq> tune to MHz or Hz", 34ul);
osi_WrStrLn(" -h help", 21ul);
osi_WrStrLn(" -p <cmd> <value> send rtl_tcp parameter, ppm, tuner\
gain ...", 61ul);
osi_WrStrLn(" -s <soundfilename> 16bit 48kHz signed 2 channel so\
und stream/pipe", 68ul);
osi_WrStrLn(" -t <url:port> connect rtl:tcp server (127.0.0.1:12\
34)", 56ul);
osi_WrStrLn(" -v show rssi (dB) and afc (khz)",
45ul);
osi_WrStrLn("example: radio -f 101.2 -s /tmp/sound.pcm -t 192.168\
.1.1:1234 -p 5 72 -p 8 1 -v", 80ul);
X2C_ABORT();
}
else Usage("-h", 3ul);
}
else Usage("-h", 3ul);
}
} /* end Parms() */
static void showrssi(void)
{
char ss[31];
char s[31];
if (isstereo) strncpy(s,"S ",31u);
else strncpy(s,"M ",31u);
aprsstr_FixToStr(osic_ln((rxx.rssit[0]+1.0f)*3.0517578125E-5f)*4.342944819f,2UL, ss, 31ul);
aprsstr_Append(s, 31ul, ss, 31ul);
aprsstr_Append(s, 31ul, "dB ", 4ul);
aprsstr_IntToStr(rxx.afckhz, 0UL, ss, 31ul);
aprsstr_Append(s, 31ul, ss, 31ul);
aprsstr_Append(s, 31ul, " \015", 7ul);
osi_Werr(s, 31ul);
} /* end showrssi() */
static float uc19;
static float il19;
static float uc20;
static float il20;
static float um20;
static float sqv;
#define radio_F19 0.3741
/* 19khz with 192khz sampelrate */
#define radio_LRLEVEL 1.633E-3
/* check value ... */
#define radio_STEREOSQUELCH 1.37172
static void stereo(float ui, float * left0, float * right0)
{
float uq;
uc19 = uc19+(ui-uc19)*0.001f+il19; /* 19khz resonant filter */
il19 = il19-uc19*0.3741f;
uc20 = uc20*0.999f+il20; /* 19khz resonant filter */
if (uc19>=0.0f) uc20 = uc20+1.633E-3f;
else uc20 = uc20-1.633E-3f;
il20 = il20-uc20*0.3741f;
sqv = sqv+((float)fabs(uc20)-sqv)*0.0001f; /* level of pilot tone */
if (sqv>1.37172f) {
uq = uc20*uc20; /* make 38khz sine + dc out of 19khz*/
um20 = um20+(uq-um20)*0.01f; /* get dc */
uq = (uq-um20)*ui; /* subtract dc and mix left-right signal with 38khz */
isstereo = 1;
}
else {
uq = 0.0f; /* mono */
isstereo = 0;
}
*left0 = ui-uq; /* mono - (left - right) */
*right0 = ui+uq; /* mono + (left - right) */
} /* end stereo() */
static short lim(float v)
{
if (v>=32767.0f) return 32767;
else if (v<(-3.2767E+4f)) return -32767;
else return (short)X2C_TRUNCI(v,-32768,32767);
return 0;
} /* end lim() */
static int32_t sp;
static int32_t sn;
static uint32_t pc;
static float left;
static float right;
static uint32_t tshow;
static char recon;
static float lp;
static float u11;
static float u21;
static float il1;
static float u12;
static float u22;
static float il2;
static float deem1;
static float deem2;
X2C_STACK_LIMIT(100000l)
extern int main(int argc, char **argv)
{
int32_t tmp;
X2C_BEGIN(&argc,argv,1,4000000l,8000000l);
sdr_BEGIN();
aprsstr_BEGIN();
osi_BEGIN();
memset((char *) &rxx,(char)0,sizeof(struct sdr_RX));
prx[0U] = &rxx;
prx[1U] = 0;
memset((char *)stickparm,(char)0,sizeof(struct STICKPARM [256]));
Parms();
{ /* with */
struct sdr_RX * anonym = &rxx;
anonym->df = 100UL;
anonym->maxafc = (int32_t)afc;
anonym->width = 192000UL;
anonym->modulation = 'f';
anonym->samples = (sdr_pAUDIOSAMPLE)sampx;
}
if (sdr_startsdr(url, 1001ul, port, 1001ul, 2048000UL, 192000UL, 1)) {
sdr_setparm(1UL, tune-100000UL);
for (pc = 0UL; pc<=255UL; pc++) {
if (stickparm[pc].ok0) sdr_setparm(pc, stickparm[pc].val);
} /* end for */
lp = 0.14f;
sndw = 0UL;
fd = osi_OpenWrite(soundfn, 1001ul);
if (fd>=0L) {
recon = 1;
for (;;) {
sn = sdr_getsdr(64UL, prx, 17ul);
if (sn<0L) {
osi_WerrLn("connection lost", 16ul);
recon = 1;
}
else {
tmp = sn-1L;
sp = 0L;
if (sp<=tmp) for (;; sp++) {
left = (float)X2C_CHKNIL(sdr_pAUDIOSAMPLE,
rxx.samples)[sp];
if (mono) right = left;
else stereo(left, &left, &right);
u11 = u11+((left-u11)-il1)*lp; /* lowpass 14khz */
u21 = u21+(il1-u21)*lp;
il1 = il1+(u11-u21)*lp*2.0f;
u12 = u12+((right-u12)-il2)*lp;
u22 = u22+(il2-u22)*lp;
il2 = il2+(u12-u22)*lp*2.0f;
deem1 = deem1+(u21-deem1)*0.05f; /* deemphasis */
deem2 = deem2+(u22-deem2)*0.05f;
if ((sp&3L)==3L) {
/* 192 > 48khz samperate */
sndbuf[sndw] = lim(deem1*2.0f);
++sndw;
sndbuf[sndw] = lim(deem2*2.0f);
++sndw;
if (sndw>1023UL) {
osi_WrBin(fd, (char *)sndbuf, 2048u/1u, sndw*2UL);
sndw = 0UL;
if (tshow>20UL) {
recon = 0;
/*WrFixed(sqv, 2,1); WrStr(" "); */
if (verb) showrssi();
tshow = 0UL;
}
else ++tshow;
}
}
if (sp==tmp) break;
} /* end for */
}
}
}
osi_WrStr(soundfn, 1001ul);
osi_WrStrLn(" sound file open error", 23ul);
osi_WrStrLn("connection lost", 16ul);
}
else osi_WrStrLn("not connected", 14ul);
X2C_EXIT();
return 0;
}
X2C_MAIN_DEFINITION