mirror of
https://github.com/dj0abr/SSB_HighSpeed_Modem.git
synced 2026-06-02 14:04:49 -04:00
update
This commit is contained in:
+11
-4
@@ -20,7 +20,14 @@ libkmaudio/libkmaudio_init_linux.o\
|
||||
libkmaudio/libkmaudio_interface.o\
|
||||
libkmaudio/libkmaudio_capture_linux.o\
|
||||
libkmaudio/libkmaudio_playback_linux.o\
|
||||
libkmaudio/libkmaudio_resampler.o
|
||||
libkmaudio/libkmaudio_resampler.o\
|
||||
websocket/ws.o\
|
||||
websocket/ws_callbacks.o\
|
||||
websocket/websocketserver.o\
|
||||
websocket/sha1.o\
|
||||
websocket/base64.o\
|
||||
websocket/handshake.o\
|
||||
extdata.o distrubution.o kmtimer.o
|
||||
|
||||
default: $(OBJ)
|
||||
mkdir -p ../hsmodemLinux
|
||||
@@ -28,6 +35,6 @@ default: $(OBJ)
|
||||
g++ $(CXXFLAGS) -o ../hsmodemLinux/hsmodem $(OBJ) $(LDFLAGS)
|
||||
|
||||
clean:
|
||||
rm -f *.o
|
||||
rm -f libkmaudio/*.o
|
||||
rm -f libkmaudio/*.o
|
||||
rm -r *.o
|
||||
rm -r libkmaudio/*.o
|
||||
rm -r websocket/*.o
|
||||
|
||||
@@ -79,6 +79,7 @@ void playAudioPCM(char* fn, int destination)
|
||||
int16_t d[100];
|
||||
printf("play:%s, caprate:%d\n", fn,caprate);
|
||||
FILE* fp = fopen(fn, "rb");
|
||||
const float ann_volume = 0.3f; // volume reduction for announcement
|
||||
if (fp)
|
||||
{
|
||||
while ((len = fread(d, sizeof(int16_t), 100, fp)))
|
||||
@@ -102,6 +103,8 @@ void playAudioPCM(char* fn, int destination)
|
||||
}
|
||||
sleep_ms(1);
|
||||
}
|
||||
f = lowpass(f);
|
||||
f *= ann_volume; // reduce volume
|
||||
float f1 = f / 32768;
|
||||
kmaudio_playsamples(voice_pbidx, &f1, 1, lsvol);
|
||||
}
|
||||
@@ -119,6 +122,7 @@ void playAudioPCM(char* fn, int destination)
|
||||
}
|
||||
|
||||
f = lowpass(f);
|
||||
f *= ann_volume; // reduce volume
|
||||
f /= 32768;
|
||||
|
||||
if ((destination & 1) == 1)
|
||||
|
||||
Executable
+117
@@ -0,0 +1,117 @@
|
||||
/*
|
||||
* High Speed modem to transfer data in a 2,7kHz SSB channel
|
||||
* =========================================================
|
||||
* Author: DJ0ABR
|
||||
* made for: AMSAT-DL
|
||||
*
|
||||
* (c) DJ0ABR
|
||||
* www.dj0abr.de
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* distribution.cpp ... handles priorities for hsmodem TX
|
||||
*/
|
||||
|
||||
#include "hsmodem.h"
|
||||
|
||||
#ifdef _LINUX_
|
||||
void* dist_function(void* param);
|
||||
#endif
|
||||
#ifdef _WIN32_
|
||||
void dist_function(void* param);
|
||||
#endif
|
||||
|
||||
|
||||
void init_distributor()
|
||||
{
|
||||
#ifdef _LINUX_
|
||||
pthread_t dist_txthread;
|
||||
pthread_create(&dist_txthread, NULL, dist_function, NULL);
|
||||
#endif
|
||||
#ifdef _WIN32_
|
||||
_beginthread(dist_function, 0, NULL);
|
||||
#endif
|
||||
}
|
||||
|
||||
// TX thread
|
||||
#ifdef _LINUX_
|
||||
void* dist_function(void* param)
|
||||
{
|
||||
pthread_detach(pthread_self());
|
||||
#endif
|
||||
#ifdef _WIN32_
|
||||
void dist_function(void* param)
|
||||
{
|
||||
#endif
|
||||
|
||||
uint8_t rxdata[500];
|
||||
int circ = 0;
|
||||
|
||||
printf("Distributor running\n");
|
||||
while (keeprunning)
|
||||
{
|
||||
if (ann_running == 0)
|
||||
{
|
||||
// give all data sources the same priority
|
||||
if (++circ >= 5) circ = 0;
|
||||
|
||||
int len = 0;
|
||||
switch (circ)
|
||||
{
|
||||
case 0:
|
||||
len = read_fifo(PSK_GUI_TX, rxdata, sizeof(rxdata));
|
||||
break;
|
||||
case 1:
|
||||
len = read_fifo(EXT_TX, rxdata, sizeof(rxdata));
|
||||
break;
|
||||
case 2:
|
||||
len = read_fifo(EXT_SPECNB, rxdata, sizeof(rxdata));
|
||||
break;
|
||||
case 3:
|
||||
len = read_fifo(EXT_SPECWB, rxdata, sizeof(rxdata));
|
||||
break;
|
||||
case 4: sleep_ms(10); //prevent process from eating 100% CPU time
|
||||
break;
|
||||
}
|
||||
|
||||
if (len > 0) _sendToModulator(rxdata, len);
|
||||
}
|
||||
}
|
||||
printf("Distributor exits\n");
|
||||
|
||||
#ifdef _LINUX_
|
||||
pthread_exit(NULL); // self terminate this thread
|
||||
return NULL;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* data.. to be sent to _sendToModulator
|
||||
* put it into the PSK_GUI_TX fifo
|
||||
*/
|
||||
void sendPSKdata(uint8_t* data, int len, int fifoID)
|
||||
{
|
||||
write_fifo(fifoID, data, len);
|
||||
//printf("into fifo:%d has now:%d of 100 elements (each 300 bytes long)\n", fifoID, fifo_usedspace(fifoID));
|
||||
|
||||
// wait until sent
|
||||
while (keeprunning)
|
||||
{
|
||||
int us = fifo_usedspace(fifoID);
|
||||
if (us <= 2) break;
|
||||
sleep_ms(10);
|
||||
}
|
||||
}
|
||||
Executable
+509
@@ -0,0 +1,509 @@
|
||||
/*
|
||||
* High Speed modem to transfer data in a 2,7kHz SSB channel
|
||||
* =========================================================
|
||||
* Author: DJ0ABR
|
||||
*
|
||||
* (c) DJ0ABR
|
||||
* www.dj0abr.de
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* extdata.c ... handle external data coming via udp 40135
|
||||
*
|
||||
* Data format (Packet Length: 219 Byte, fits into one HSmodem payload)
|
||||
* ====================================================================
|
||||
* Byte 0 ... Data Type
|
||||
* Byte 1 ... Length
|
||||
* Byte 2-218 .. data (217 Bytes)
|
||||
*
|
||||
* Data Type:
|
||||
* types 0-31 ... reserved for HSmodem's internal use
|
||||
* type 32-255 .. available for public use. Registration recommended to avoid identical use by different apps
|
||||
* already defined by HSmodem:
|
||||
* type 0 ... payload contains DX-cluster messages as ASCII text
|
||||
* type 1 ... NB spectrum data
|
||||
*
|
||||
* Length:
|
||||
* length of the data field, maximum: 217
|
||||
*
|
||||
*/
|
||||
|
||||
#include "hsmodem.h"
|
||||
|
||||
void makeSpecData(uint8_t* pdata, int len);
|
||||
void handleNBSpecData(uint8_t *pdata, int len);
|
||||
void makeWBSpecData(uint8_t* pdata, int len);
|
||||
void handleWBSpecData(uint8_t* pdata, int len);
|
||||
|
||||
uint32_t extDataID = 0x7743fa9f;
|
||||
|
||||
// message received on UDP port 40135
|
||||
void ext_rxdata(uint8_t* pdata, int len, struct sockaddr_in* rxsock)
|
||||
{
|
||||
if (extData_active == 0) return;
|
||||
|
||||
uint32_t id = pdata[0];
|
||||
id <<= 8;
|
||||
id += pdata[1];
|
||||
id <<= 8;
|
||||
id += pdata[2];
|
||||
id <<= 8;
|
||||
id += pdata[3];
|
||||
|
||||
if (id != extDataID)
|
||||
{
|
||||
printf("incoming data on 40135, wrong ID: %d\n", id);
|
||||
return;
|
||||
}
|
||||
|
||||
if (pdata[4] == 0)
|
||||
{
|
||||
// DX cluster message
|
||||
printf("DX cluster message received: <%s>\n", pdata + 5);
|
||||
// pdata MUST have size: PAYLOADLEN
|
||||
if (len != PAYLOADLEN)
|
||||
{
|
||||
printf("ext_rxdata wrong size:%d, need:%d, ignoring\n", len, PAYLOADLEN);
|
||||
return;
|
||||
}
|
||||
|
||||
// 8 ... ExternalData
|
||||
// 3 ... SingleFrame
|
||||
// 1 ... repeat frame if TX currently down
|
||||
modem_sendPSKData(pdata + 4, 8, 3, 1, EXT_TX);
|
||||
}
|
||||
|
||||
else if (pdata[4] == 1)
|
||||
{
|
||||
// NB spectrum data
|
||||
makeSpecData(pdata + 4 + 1, len - 1 - 4);
|
||||
}
|
||||
|
||||
else if (pdata[4] == 2)
|
||||
{
|
||||
// CW Skimmer data
|
||||
|
||||
// generate a full payload, padded with zeros
|
||||
uint8_t payload[PAYLOADLEN];
|
||||
memset(payload, 0, PAYLOADLEN);
|
||||
if (len > PAYLOADLEN) len = PAYLOADLEN; // just for security, will usually never happen
|
||||
memcpy(payload, pdata + 4, len-4);
|
||||
|
||||
printf("external CW Skimmer message ID: %d msglen:%d message<%s>\n", pdata[4], len, payload);
|
||||
|
||||
// 8 ... ExternalData
|
||||
// 3 ... SingleFrame
|
||||
// 1 ... repeat frame if TX currently down
|
||||
modem_sendPSKData(payload, 8, 3, 1, EXT_TX);
|
||||
}
|
||||
|
||||
else if (pdata[4] == 3)
|
||||
{
|
||||
// WB spectrum data
|
||||
makeWBSpecData(pdata + 4 + 1, len - 1 - 4);
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
printf("external message: %d msglen: %d unknown\n", pdata[0], len);
|
||||
}
|
||||
}
|
||||
|
||||
// message received by modem
|
||||
// length of pdata is PAYLOADLEN+10 (see frame_packer.c: getPayload())
|
||||
void ext_modemRX(uint8_t* pdata)
|
||||
{
|
||||
static uint8_t lastpl[PAYLOADLEN];
|
||||
static uint8_t lastcwpl[PAYLOADLEN];
|
||||
|
||||
uint8_t* payload = pdata + 10;
|
||||
// the first byte is the external-type specifier
|
||||
if (payload[0] == 0)
|
||||
{
|
||||
if (memcmp(payload, lastpl, PAYLOADLEN))
|
||||
{
|
||||
// new frame received
|
||||
memcpy(lastpl, payload, PAYLOADLEN);
|
||||
|
||||
// DX-cluster message
|
||||
// send to websocket
|
||||
ws_send(payload, PAYLOADLEN);
|
||||
}
|
||||
}
|
||||
|
||||
if (payload[0] == 1)
|
||||
{
|
||||
handleNBSpecData(payload, PAYLOADLEN);
|
||||
}
|
||||
|
||||
if (payload[0] == 2)
|
||||
{
|
||||
if (memcmp(payload, lastcwpl, PAYLOADLEN))
|
||||
{
|
||||
// new frame received
|
||||
memcpy(lastcwpl, payload, PAYLOADLEN);
|
||||
|
||||
// CW skimmer message
|
||||
// send to websocket
|
||||
ws_send(payload, PAYLOADLEN);
|
||||
}
|
||||
}
|
||||
|
||||
if (payload[0] == 3)
|
||||
{
|
||||
handleWBSpecData(payload, PAYLOADLEN);
|
||||
}
|
||||
|
||||
// type=16 is also in use, see hsmodem (Bulletin)
|
||||
}
|
||||
|
||||
/*
|
||||
* pdata: array of 550 16-bit values
|
||||
* starting at 10489.475 with a resolution of 1 kHz
|
||||
* up to 10490.025 which is a range of 550kHz (550 16bit values)
|
||||
* left beacon 10489.500 is at index 25 (24..26)
|
||||
*/
|
||||
void makeSpecData(uint8_t *pdata, int len)
|
||||
{
|
||||
// check if TX fifo has data already
|
||||
int us = fifo_usedspace(EXT_SPECNB);
|
||||
if (us > 1) return; // ignore data
|
||||
|
||||
const int mlen = 550;
|
||||
if (len > mlen) len = mlen;
|
||||
|
||||
// convert into 16 bit values
|
||||
uint16_t sval[mlen];
|
||||
for (int i = 0; i < mlen; i++)
|
||||
{
|
||||
sval[i] = pdata[2 * i];
|
||||
sval[i] <<= 8;
|
||||
sval[i] += pdata[2 * i + 1];
|
||||
}
|
||||
|
||||
// measure value of left beacon
|
||||
int vmax = 0;
|
||||
for (int i = 24; i <= 26; i++)
|
||||
if (sval[i] > vmax) vmax = sval[i];
|
||||
if (vmax < 1) vmax = 1; // avoid divide by zero error
|
||||
|
||||
//printf("Beaconlevel: %d\n",vmax);
|
||||
|
||||
// normalize to beaconlevel, which is 100%
|
||||
// and 100% is 6 bit maximum, which is 63
|
||||
// also reduce length by 2, resulting in 275 values
|
||||
const int vlen = mlen / 2;
|
||||
uint8_t snormval[vlen];
|
||||
int idx = 0;
|
||||
for (int i = 0; i < mlen; i+=2)
|
||||
{
|
||||
if (idx >= vlen)
|
||||
{
|
||||
printf("vlen too small\n");
|
||||
break; // just for security, will never happen
|
||||
}
|
||||
snormval[idx] = (uint8_t)((63 * sval[i]) / vmax);
|
||||
uint8_t v = (uint8_t)((63 * sval[i+1]) / vmax);
|
||||
if (v > snormval[idx]) snormval[idx] = v;
|
||||
idx++;
|
||||
}
|
||||
|
||||
// here we have 275 values with a resolution of 2kHz
|
||||
// each value is 6 bit long
|
||||
// so we have 275 * 6 = 1650 bit, which is 207 byte,
|
||||
// and fits into the extData payload of 217 byte
|
||||
|
||||
//showbytestring("TX:",snormval,30,30);
|
||||
|
||||
// store in average buffer
|
||||
static uint16_t avgbuf[vlen];
|
||||
static int avganz = 0;
|
||||
for (int i = 0; i < idx; i++)
|
||||
//avgbuf[i] += snormval[i];
|
||||
if(avgbuf[i] < snormval[i]) avgbuf[i] = snormval[i];
|
||||
avganz++;
|
||||
|
||||
/*
|
||||
// check if TX fifo has data already
|
||||
int us = fifo_usedspace(EXT_SPECNB);
|
||||
if (us > 1) return;
|
||||
|
||||
|
||||
// check if audio playback fifo is filled already
|
||||
us = io_fifo_usedspace(io_pbidx);
|
||||
if (us > 48000) return; // max 1s latency
|
||||
*/
|
||||
|
||||
// build average
|
||||
//for (int i = 0; i < idx; i++)
|
||||
// avgbuf[i] /= avganz;
|
||||
avganz = 0;
|
||||
|
||||
// snormval has 6-bit values, each in one byte
|
||||
// convert it to a bitstream
|
||||
uint8_t bitstream[PAYLOADLEN]; // the result will be shorter
|
||||
int sbyte = 0, sbit = 0;
|
||||
int dbyte = 1, dbit = 0; // dbyte=1 because bitstream[0] is the message ID
|
||||
memset(bitstream, 0, sizeof(bitstream));
|
||||
while (1)
|
||||
{
|
||||
// read actual bit
|
||||
uint8_t bit = avgbuf[sbyte] & (1 << sbit);
|
||||
if (bit) bit = 1;
|
||||
// write into bitstream
|
||||
bitstream[dbyte] |= (bit << dbit);
|
||||
// move source to next position
|
||||
if (++sbit >= 6)
|
||||
{
|
||||
sbit = 0;
|
||||
sbyte++;
|
||||
if (sbyte == idx) break; // finished
|
||||
}
|
||||
// move destination to next position
|
||||
if (++dbit >= 8)
|
||||
{
|
||||
dbit = 0;
|
||||
dbyte++;
|
||||
}
|
||||
if (dbyte >= PAYLOADLEN)
|
||||
{
|
||||
printf("dbyte wrong:%d max is %d\n", dbyte, PAYLOADLEN);
|
||||
break;
|
||||
}
|
||||
}
|
||||
memset(avgbuf, 0, vlen * sizeof(uint16_t));
|
||||
// data in: bitstream, length of data: dbyte
|
||||
|
||||
// send to modem
|
||||
// 8 ... ExternalData
|
||||
// 3 ... SingleFrame
|
||||
// 1 ... repeat frame if TX currently down
|
||||
|
||||
bitstream[0] = 1; // message ID for spectrum data
|
||||
modem_sendPSKData(bitstream, 8, 3, 1, EXT_SPECNB);
|
||||
}
|
||||
|
||||
void handleNBSpecData(uint8_t *pdata, int len)
|
||||
{
|
||||
// extract into original data
|
||||
uint8_t odata[1000];
|
||||
int sby = 1, sbi = 0;
|
||||
int dby = 1, dbi = 0;
|
||||
memset(odata, 0, sizeof(odata));
|
||||
while (1)
|
||||
{
|
||||
// read actual bit
|
||||
uint8_t bit = pdata[sby] & (1 << sbi);
|
||||
if (bit) bit = 1;
|
||||
// write into orig data
|
||||
odata[dby] |= (bit << dbi);
|
||||
// move source to next position
|
||||
if (++sbi >= 8)
|
||||
{
|
||||
sbi = 0;
|
||||
sby++;
|
||||
if (sby >= len) break; // finished
|
||||
}
|
||||
// move destination to next position
|
||||
if (++dbi >= 6)
|
||||
{
|
||||
dbi = 0;
|
||||
dby++;
|
||||
}
|
||||
}
|
||||
|
||||
//showbytestring("RX:", odata, 30, 30);
|
||||
|
||||
// send to websocket
|
||||
odata[0] = 1;
|
||||
ws_send(odata, dby);
|
||||
}
|
||||
|
||||
/*
|
||||
* Spectrum data format as received by the browser:
|
||||
* ------------------------------------------------
|
||||
* Byte 0 ... fixed to 0
|
||||
* Byte 1 ... length MSB
|
||||
* Byte 2 ... length LSB
|
||||
* Byte 3 ... =1 identifies the message as NB spectrum
|
||||
* Byte 4-278 ... spectrum data (275 values)
|
||||
*
|
||||
* spectrum data:
|
||||
* --------------
|
||||
* 10489.525 - 10490.025 = 550kHz Resolution 2 kHz: 275 values
|
||||
* each value has 6 bit. 0x3f is the maximum
|
||||
*/
|
||||
|
||||
// WB Transponder
|
||||
|
||||
/*
|
||||
* pdata: array of 266 16-bit values
|
||||
* starting at 10491.500 with a resolution of 30 kHz
|
||||
* up to 10499.500 which is a range of 8000kHz (266 16bit values)
|
||||
* left beacon 10491.500 is at index 0 (0..16)
|
||||
*/
|
||||
void makeWBSpecData(uint8_t* pdata, int len)
|
||||
{
|
||||
// check if TX fifo has data already
|
||||
int us = fifo_usedspace(EXT_SPECWB);
|
||||
if (us > 1) return; // ignore data
|
||||
|
||||
const int mlen = 266;
|
||||
if (len > mlen) len = mlen;
|
||||
|
||||
// convert into 16 bit values
|
||||
uint16_t sval[mlen];
|
||||
for (int i = 0; i < mlen; i++)
|
||||
{
|
||||
sval[i] = pdata[2 * i];
|
||||
sval[i] <<= 8;
|
||||
sval[i] += pdata[2 * i + 1];
|
||||
}
|
||||
|
||||
// measure value of left beacon
|
||||
int vmax = 0;
|
||||
for (int i = 0; i <= 16; i++)
|
||||
if (sval[i] > vmax) vmax = sval[i];
|
||||
if (vmax < 1) vmax = 1; // avoid divide by zero error
|
||||
|
||||
//printf("Beaconlevel: %d\n",vmax);
|
||||
//showbytestring16("gultiti:", sval, 40);
|
||||
|
||||
// substract 0-level then
|
||||
// normalize to beaconlevel, which is 100%
|
||||
// and 100% is 6 bit maximum, which is 63
|
||||
uint16_t WBnullLevel = 350;
|
||||
uint8_t snormval[mlen];
|
||||
int idx = 0;
|
||||
vmax -= WBnullLevel;
|
||||
if (vmax < 0) vmax = 0;
|
||||
for (int i = 0; i < mlen; i++)
|
||||
{
|
||||
int nv = sval[i] - WBnullLevel;
|
||||
if (nv < 0) nv = 0;
|
||||
uint8_t va = (uint8_t)((63 * nv) / vmax);
|
||||
if (va > 63) va = 63;
|
||||
snormval[idx] = va;
|
||||
idx++;
|
||||
}
|
||||
|
||||
// here we have 266 values with a resolution of 30kHz
|
||||
// each value is 6 bit long
|
||||
// so we have 266 * 6 = 1596 bit, which is 199 byte,
|
||||
// and fits into the extData payload of 217 byte
|
||||
|
||||
//showbytestring("TX:",snormval,30,30);
|
||||
|
||||
// store in average buffer
|
||||
static uint16_t avgbuf[mlen];
|
||||
static int avganz = 0;
|
||||
for (int i = 0; i < idx; i++)
|
||||
//avgbuf[i] += snormval[i];
|
||||
if (avgbuf[i] < snormval[i]) avgbuf[i] = snormval[i];
|
||||
avganz++;
|
||||
/*
|
||||
// check if TX fifo has data already
|
||||
int us = fifo_usedspace(EXT_SPECWB);
|
||||
if (us > 1) return;
|
||||
|
||||
// check if audio playback fifo is filled already
|
||||
us = io_fifo_usedspace(io_pbidx);
|
||||
if (us > 48000) return; // max 1s latency
|
||||
*/
|
||||
// build average
|
||||
//for (int i = 0; i < idx; i++)
|
||||
// avgbuf[i] /= avganz;
|
||||
avganz = 0;
|
||||
|
||||
// snormval has 6-bit values, each in one byte
|
||||
// convert it to a bitstream
|
||||
uint8_t bitstream[PAYLOADLEN]; // the result will be shorter
|
||||
int sbyte = 0, sbit = 0;
|
||||
int dbyte = 1, dbit = 0; // dbyte=1 because bitstream[0] is the message ID
|
||||
memset(bitstream, 0, sizeof(bitstream));
|
||||
while (1)
|
||||
{
|
||||
// read actual bit
|
||||
uint8_t bit = avgbuf[sbyte] & (1 << sbit);
|
||||
if (bit) bit = 1;
|
||||
// write into bitstream
|
||||
bitstream[dbyte] |= (bit << dbit);
|
||||
// move source to next position
|
||||
if (++sbit >= 6)
|
||||
{
|
||||
sbit = 0;
|
||||
sbyte++;
|
||||
if (sbyte == idx) break; // finished
|
||||
}
|
||||
// move destination to next position
|
||||
if (++dbit >= 8)
|
||||
{
|
||||
dbit = 0;
|
||||
dbyte++;
|
||||
}
|
||||
if (dbyte >= PAYLOADLEN)
|
||||
{
|
||||
printf("dbyte wrong:%d max is %d\n", dbyte, PAYLOADLEN);
|
||||
break;
|
||||
}
|
||||
}
|
||||
memset(avgbuf, 0, mlen * sizeof(uint16_t));
|
||||
// data in: bitstream, length of data: dbyte
|
||||
|
||||
// send to modem
|
||||
// 8 ... ExternalData
|
||||
// 3 ... SingleFrame
|
||||
// 1 ... repeat frame if TX currently down
|
||||
|
||||
bitstream[0] = 3; // message ID for WB spectrum data
|
||||
modem_sendPSKData(bitstream, 8, 3, 1, EXT_SPECWB);
|
||||
}
|
||||
|
||||
// WB data received via HF
|
||||
void handleWBSpecData(uint8_t* pdata, int len)
|
||||
{
|
||||
// extract into original data
|
||||
uint8_t odata[1000];
|
||||
int sby = 1, sbi = 0;
|
||||
int dby = 1, dbi = 0;
|
||||
memset(odata, 0, sizeof(odata));
|
||||
while (1)
|
||||
{
|
||||
// read actual bit
|
||||
uint8_t bit = pdata[sby] & (1 << sbi);
|
||||
if (bit) bit = 1;
|
||||
// write into orig data
|
||||
odata[dby] |= (bit << dbi);
|
||||
// move source to next position
|
||||
if (++sbi >= 8)
|
||||
{
|
||||
sbi = 0;
|
||||
sby++;
|
||||
if (sby >= len) break; // finished
|
||||
}
|
||||
// move destination to next position
|
||||
if (++dbi >= 6)
|
||||
{
|
||||
dbi = 0;
|
||||
dby++;
|
||||
}
|
||||
}
|
||||
|
||||
//showbytestring("RX:", odata, 30, 30);
|
||||
|
||||
// send to websocket
|
||||
odata[0] = 3;
|
||||
ws_send(odata, dby);
|
||||
}
|
||||
+1
-3
@@ -288,15 +288,13 @@ uint16_t* mean(uint16_t* f, int smoothX, int smoothY)
|
||||
|
||||
void _init_fft()
|
||||
{
|
||||
printf("init FFT\n");
|
||||
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);
|
||||
plan = fftw_plan_dft_r2c_1d(FFT_AUDIOSAMPLERATE, din, cpout, FFTW_MEASURE);
|
||||
|
||||
// create arbitrary pre decimator
|
||||
// decimate 44.1k or 48k down to 8000Hz
|
||||
|
||||
+132
-330
@@ -1,10 +1,16 @@
|
||||
/*
|
||||
* High Speed modem to transfer data in a 2,7kHz SSB channel
|
||||
* =========================================================
|
||||
* Audio Library for Linux and Windows
|
||||
* ===================================
|
||||
* Author: DJ0ABR
|
||||
*
|
||||
* (c) DJ0ABR
|
||||
* www.dj0abr.de
|
||||
* Author: Kurt Moraw, Ham radio: DJ0ABR, github: dj0abr
|
||||
* License: GPL-3
|
||||
*
|
||||
* compilation:
|
||||
* Windows ... Visual Studio
|
||||
* Linux ... make
|
||||
*
|
||||
* Documentation see: libkmaudio.h
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -20,372 +26,168 @@
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* fifo.c ... thread safe buffer for audio I/O
|
||||
*
|
||||
* fifo.cpp ... thread safe FIFOs
|
||||
*
|
||||
*/
|
||||
|
||||
#include "hsmodem.h"
|
||||
|
||||
#define NUM_OF_FIFOS 20
|
||||
|
||||
|
||||
/*
|
||||
#ifdef _WIN32_
|
||||
CRITICAL_SECTION io_cap_crit_sec;
|
||||
CRITICAL_SECTION io_pb_crit_sec;
|
||||
#define IO_CAP_LOCK EnterCriticalSection(&io_cap_crit_sec)
|
||||
#define IO_PB_LOCK EnterCriticalSection(&io_pb_crit_sec)
|
||||
void IO_CAP_UNLOCK()
|
||||
#ifdef WIN32
|
||||
CRITICAL_SECTION fifo_crit_sec[NUM_OF_FIFOS];
|
||||
#define LOCKFIFO(pn) EnterCriticalSection(&(fifo_crit_sec[pn]))
|
||||
void UNLOCKFIFO(int pn)
|
||||
{
|
||||
if (&io_cap_crit_sec != NULL)
|
||||
LeaveCriticalSection(&io_cap_crit_sec);
|
||||
}
|
||||
void IO_PB_UNLOCK()
|
||||
{
|
||||
if (&io_pb_crit_sec != NULL)
|
||||
LeaveCriticalSection(&io_pb_crit_sec);
|
||||
if (&(fifo_crit_sec[pn]) != NULL)
|
||||
LeaveCriticalSection(&(fifo_crit_sec[pn]));
|
||||
}
|
||||
#else
|
||||
pthread_mutex_t fifo_crit_sec[NUM_OF_FIFOS];
|
||||
#define LOCKFIFO(pn) pthread_mutex_lock(&(fifo_crit_sec[pn]))
|
||||
#define UNLOCKFIFO(pn) pthread_mutex_unlock(&(fifo_crit_sec[pn]))
|
||||
#endif
|
||||
|
||||
#ifdef _LINUX_
|
||||
pthread_mutex_t io_cap_crit_sec;
|
||||
pthread_mutex_t io_pb_crit_sec;
|
||||
#define IO_CAP_LOCK pthread_mutex_lock(&io_cap_crit_sec)
|
||||
void IO_CAP_UNLOCK() { pthread_mutex_unlock(&io_cap_crit_sec); }
|
||||
#define IO_PB_LOCK pthread_mutex_lock(&io_pb_crit_sec)
|
||||
void IO_PB_UNLOCK() { pthread_mutex_unlock(&io_pb_crit_sec); }
|
||||
#endif
|
||||
#define FIFOBUFLEN 100 // number of fifo buffers
|
||||
#define FIFOELEMENTLEN 300 // length of one fifo element
|
||||
|
||||
#define AUDIO_PLAYBACK_BUFLEN (48000 * 15) // space for 10 seconds of samples
|
||||
#define AUDIO_CAPTURE_BUFLEN (10000) //48000)// * 10) // space for 10 seconds of samples
|
||||
int wridx[NUM_OF_FIFOS];
|
||||
int rdidx[NUM_OF_FIFOS];
|
||||
int8_t buffer[NUM_OF_FIFOS][FIFOBUFLEN][FIFOELEMENTLEN];
|
||||
|
||||
int io_cap_wridx = 0;
|
||||
int io_cap_rdidx = 0;
|
||||
float io_cap_buffer[AUDIO_CAPTURE_BUFLEN];
|
||||
|
||||
int io_pb_wridx = 0;
|
||||
int io_pb_rdidx = 0;
|
||||
float io_pb_buffer[AUDIO_PLAYBACK_BUFLEN];
|
||||
|
||||
void io_init_pipes()
|
||||
void init_fifos()
|
||||
{
|
||||
#ifdef _WIN32_
|
||||
if (&io_cap_crit_sec != NULL) DeleteCriticalSection(&io_cap_crit_sec);
|
||||
InitializeCriticalSection(&io_cap_crit_sec);
|
||||
|
||||
if (&io_pb_crit_sec != NULL) DeleteCriticalSection(&io_pb_crit_sec);
|
||||
InitializeCriticalSection(&io_pb_crit_sec);
|
||||
|
||||
io_clear_audio_fifos();
|
||||
#endif
|
||||
|
||||
io_voice_init_pipes();
|
||||
rtty_init_pipes();
|
||||
}
|
||||
|
||||
// write one sample into the fifo
|
||||
// overwrite old data if the fifo is full
|
||||
void io_cap_write_fifo(float sample)
|
||||
{
|
||||
if (((io_cap_wridx + 1) % AUDIO_CAPTURE_BUFLEN) == io_cap_rdidx)
|
||||
{
|
||||
//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();
|
||||
}
|
||||
|
||||
int io_cap_read_fifo(float* data)
|
||||
{
|
||||
IO_CAP_LOCK;
|
||||
|
||||
if (io_cap_rdidx == io_cap_wridx)
|
||||
{
|
||||
// Fifo empty, no data available
|
||||
IO_CAP_UNLOCK();
|
||||
return 0;
|
||||
}
|
||||
|
||||
*data = io_cap_buffer[io_cap_rdidx];
|
||||
if (++io_cap_rdidx >= AUDIO_CAPTURE_BUFLEN) io_cap_rdidx = 0;
|
||||
IO_CAP_UNLOCK();
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
void io_cap_write_fifo_clear()
|
||||
{
|
||||
io_cap_wridx = io_cap_rdidx = 0;
|
||||
}
|
||||
|
||||
int io_cap_fifo_freespace()
|
||||
{
|
||||
int freebuf = 0;
|
||||
|
||||
IO_CAP_LOCK;
|
||||
|
||||
int elemInFifo = (io_cap_wridx + AUDIO_CAPTURE_BUFLEN - io_cap_rdidx) % AUDIO_CAPTURE_BUFLEN;
|
||||
freebuf = AUDIO_CAPTURE_BUFLEN - elemInFifo;
|
||||
|
||||
IO_CAP_UNLOCK();
|
||||
|
||||
return freebuf;
|
||||
}
|
||||
|
||||
int io_cap_fifo_usedPercent()
|
||||
{
|
||||
int fs = io_cap_fifo_freespace();
|
||||
int used = AUDIO_CAPTURE_BUFLEN - fs;
|
||||
used = (used * 100) / AUDIO_CAPTURE_BUFLEN;
|
||||
return used;
|
||||
}
|
||||
|
||||
void io_pb_write_fifo(float sample)
|
||||
{
|
||||
IO_PB_LOCK;
|
||||
|
||||
// check if there is free space in fifo
|
||||
if (io_pb_fifo_freespace(1) == 0)
|
||||
{
|
||||
IO_PB_UNLOCK();
|
||||
printf("************* pb fifo full\n");
|
||||
return;
|
||||
}
|
||||
|
||||
io_pb_buffer[io_pb_wridx] = sample;
|
||||
if (++io_pb_wridx >= AUDIO_PLAYBACK_BUFLEN) io_pb_wridx = 0;
|
||||
IO_PB_UNLOCK();
|
||||
//printf("write: pbw:%d pbr:%d\n",io_pb_wridx,io_pb_rdidx);
|
||||
}
|
||||
|
||||
void io_pb_write_fifo_clear()
|
||||
{
|
||||
io_pb_wridx = io_pb_rdidx = 0;
|
||||
}
|
||||
|
||||
int io_pb_fifo_usedBlocks()
|
||||
{
|
||||
int fs = io_pb_fifo_freespace(0);
|
||||
int used = AUDIO_PLAYBACK_BUFLEN - fs;
|
||||
used /= (txinterpolfactor * UDPBLOCKLEN * 8 / bitsPerSymbol);
|
||||
return used;
|
||||
}
|
||||
|
||||
int io_pb_fifo_freespace(int nolock)
|
||||
{
|
||||
int freebuf = 0;
|
||||
|
||||
if (nolock == 0) IO_PB_LOCK;
|
||||
|
||||
int elemInFifo = (io_pb_wridx + AUDIO_PLAYBACK_BUFLEN - io_pb_rdidx) % AUDIO_PLAYBACK_BUFLEN;
|
||||
freebuf = AUDIO_PLAYBACK_BUFLEN - elemInFifo;
|
||||
|
||||
if (nolock == 0) IO_PB_UNLOCK();
|
||||
|
||||
//printf("fifolen:%d check: pbw:%d pbr:%d freebuf:%d\n",AUDIO_PLAYBACK_BUFLEN,io_pb_wridx,io_pb_rdidx,freebuf);
|
||||
|
||||
return freebuf;
|
||||
}
|
||||
|
||||
int io_pb_fifo_usedspace()
|
||||
{
|
||||
IO_PB_LOCK;
|
||||
int elemInFifo = (io_pb_wridx + AUDIO_PLAYBACK_BUFLEN - io_pb_rdidx) % AUDIO_PLAYBACK_BUFLEN;
|
||||
IO_PB_UNLOCK();
|
||||
|
||||
return elemInFifo;
|
||||
}
|
||||
|
||||
// read num elements
|
||||
// if num elems not avail, return all what fifo has stored
|
||||
int io_pb_read_fifo_num(float* data, int num)
|
||||
{
|
||||
IO_PB_LOCK;
|
||||
|
||||
int elemInFifo = (io_pb_wridx + AUDIO_PLAYBACK_BUFLEN - io_pb_rdidx) % AUDIO_PLAYBACK_BUFLEN;
|
||||
|
||||
if (elemInFifo == 0)
|
||||
{
|
||||
// Fifo empty, no data available
|
||||
//printf("only %d elements available\n", elemInFifo);
|
||||
IO_PB_UNLOCK();
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (num > elemInFifo)
|
||||
num = elemInFifo;
|
||||
|
||||
for (int i = 0; i < num; i++)
|
||||
{
|
||||
*data++ = io_pb_buffer[io_pb_rdidx];
|
||||
if (++io_pb_rdidx >= AUDIO_PLAYBACK_BUFLEN) io_pb_rdidx = 0;
|
||||
}
|
||||
IO_PB_UNLOCK();
|
||||
|
||||
return num;
|
||||
}
|
||||
|
||||
void io_clear_audio_fifos()
|
||||
{
|
||||
io_pb_write_fifo_clear();
|
||||
io_cap_write_fifo_clear();
|
||||
}
|
||||
*/
|
||||
// ================== RTTY FIFO ===================
|
||||
|
||||
void clear_rtty_fifos();
|
||||
|
||||
#ifdef _WIN32_
|
||||
CRITICAL_SECTION rtty_tx_crit_sec;
|
||||
CRITICAL_SECTION rtty_rx_crit_sec;
|
||||
#define RTTY_TX_LOCK EnterCriticalSection(&rtty_tx_crit_sec)
|
||||
#define RTTY_RX_LOCK EnterCriticalSection(&rtty_rx_crit_sec)
|
||||
void RTTY_TX_UNLOCK()
|
||||
{
|
||||
if (&rtty_tx_crit_sec != NULL)
|
||||
LeaveCriticalSection(&rtty_tx_crit_sec);
|
||||
}
|
||||
void RTTY_RX_UNLOCK()
|
||||
{
|
||||
if (&rtty_rx_crit_sec != NULL)
|
||||
LeaveCriticalSection(&rtty_rx_crit_sec);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef _LINUX_
|
||||
pthread_mutex_t rtty_tx_crit_sec;
|
||||
pthread_mutex_t rtty_rx_crit_sec;
|
||||
#define RTTY_TX_LOCK pthread_mutex_lock(&rtty_tx_crit_sec)
|
||||
void RTTY_TX_UNLOCK() { pthread_mutex_unlock(&rtty_tx_crit_sec); }
|
||||
#define RTTY_RX_LOCK pthread_mutex_lock(&rtty_rx_crit_sec)
|
||||
void RTTY_RX_UNLOCK() { pthread_mutex_unlock(&rtty_rx_crit_sec); }
|
||||
#endif
|
||||
|
||||
void rtty_init_pipes()
|
||||
{
|
||||
#ifdef _WIN32_
|
||||
// init pipes only once
|
||||
static int f = 1;
|
||||
|
||||
if (f)
|
||||
{
|
||||
f = 0;
|
||||
if (&rtty_tx_crit_sec != NULL) DeleteCriticalSection(&rtty_tx_crit_sec);
|
||||
InitializeCriticalSection(&rtty_tx_crit_sec);
|
||||
|
||||
if (&rtty_rx_crit_sec != NULL) DeleteCriticalSection(&rtty_rx_crit_sec);
|
||||
InitializeCriticalSection(&rtty_rx_crit_sec);
|
||||
}
|
||||
for (int i = 0; i < NUM_OF_FIFOS; i++)
|
||||
{
|
||||
#ifdef WIN32
|
||||
if (&(fifo_crit_sec[i]) != NULL) DeleteCriticalSection(&(fifo_crit_sec[i]));
|
||||
InitializeCriticalSection(&(fifo_crit_sec[i]));
|
||||
#else
|
||||
if (&(fifo_crit_sec[i]) != NULL) pthread_mutex_destroy(&(fifo_crit_sec[i]));
|
||||
pthread_mutex_init(&(fifo_crit_sec[i]), NULL);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
clear_rtty_fifos();
|
||||
for (int i = 0; i < NUM_OF_FIFOS; i++)
|
||||
fifo_clear(i);
|
||||
}
|
||||
|
||||
#define RTTY_FIFOLEN 200
|
||||
|
||||
int rtty_tx_wridx = 0;
|
||||
int rtty_tx_rdidx = 0;
|
||||
char rtty_tx_buffer[RTTY_FIFOLEN];
|
||||
|
||||
int rtty_rx_wridx = 0;
|
||||
int rtty_rx_rdidx = 0;
|
||||
char rtty_rx_buffer[RTTY_FIFOLEN];
|
||||
|
||||
// TX char from GUI to RTTY TX thread
|
||||
|
||||
void clear_rtty_fifos()
|
||||
// write into the fifo
|
||||
// ignore data if the fifo is full
|
||||
void write_fifo(int pipenum, uint8_t *pdata, int len)
|
||||
{
|
||||
rtty_tx_wridx = rtty_tx_rdidx = 0;
|
||||
rtty_rx_wridx = rtty_rx_rdidx = 0;
|
||||
}
|
||||
if (pipenum < 0 || pipenum >= NUM_OF_FIFOS) return;
|
||||
|
||||
int rtty_tx_fifo_freespace()
|
||||
{
|
||||
int elemInFifo = (rtty_tx_wridx + RTTY_FIFOLEN - rtty_tx_rdidx) % RTTY_FIFOLEN;
|
||||
return RTTY_FIFOLEN - elemInFifo;
|
||||
}
|
||||
|
||||
void clear_rtty_txfifo()
|
||||
{
|
||||
RTTY_TX_LOCK;
|
||||
rtty_tx_wridx = rtty_tx_rdidx = 0;
|
||||
RTTY_TX_UNLOCK();
|
||||
}
|
||||
|
||||
void rtty_tx_write_fifo(char c)
|
||||
{
|
||||
RTTY_TX_LOCK;
|
||||
|
||||
// check if there is free space in fifo
|
||||
if (rtty_tx_fifo_freespace() == 0)
|
||||
LOCKFIFO(pipenum);
|
||||
if (((wridx[pipenum] + 1) % FIFOBUFLEN) == rdidx[pipenum])
|
||||
{
|
||||
RTTY_TX_UNLOCK();
|
||||
//printf("cannot WRITE fifo %d full\n",pipenum);
|
||||
UNLOCKFIFO(pipenum);
|
||||
return;
|
||||
}
|
||||
|
||||
rtty_tx_buffer[rtty_tx_wridx] = c;
|
||||
if (++rtty_tx_wridx >= RTTY_FIFOLEN) rtty_tx_wridx = 0;
|
||||
RTTY_TX_UNLOCK();
|
||||
// as the first 2 bytes store the length, MSB first
|
||||
buffer[pipenum][wridx[pipenum]][0] = len >> 8;
|
||||
buffer[pipenum][wridx[pipenum]][1] = len & 0xff;
|
||||
|
||||
// followed by the data
|
||||
memcpy(buffer[pipenum][wridx[pipenum]] + 2, pdata, len);
|
||||
if (++wridx[pipenum] >= FIFOBUFLEN) wridx[pipenum] = 0;
|
||||
|
||||
UNLOCKFIFO(pipenum);
|
||||
}
|
||||
|
||||
int rtty_tx_read_fifo(char *pc)
|
||||
// read from the fifo
|
||||
// return: number of bytes read
|
||||
int read_fifo(int pipenum, uint8_t* pdata, int maxlen)
|
||||
{
|
||||
RTTY_TX_LOCK;
|
||||
|
||||
if (rtty_tx_rdidx == rtty_tx_wridx)
|
||||
if (pipenum < 0 || pipenum >= NUM_OF_FIFOS)
|
||||
{
|
||||
// Fifo empty, no data available
|
||||
RTTY_TX_UNLOCK();
|
||||
printf("read_fifo: wrong pipenum:%d (%d ..%d)\n", pipenum, 0, NUM_OF_FIFOS-1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
*pc = rtty_tx_buffer[rtty_tx_rdidx];
|
||||
if (++rtty_tx_rdidx >= RTTY_FIFOLEN) rtty_tx_rdidx = 0;
|
||||
RTTY_TX_UNLOCK();
|
||||
LOCKFIFO(pipenum);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int rtty_rx_fifo_freespace()
|
||||
{
|
||||
int elemInFifo = (rtty_rx_wridx + RTTY_FIFOLEN - rtty_rx_rdidx) % RTTY_FIFOLEN;
|
||||
return RTTY_FIFOLEN - elemInFifo;
|
||||
}
|
||||
|
||||
void rtty_rx_write_fifo(char c)
|
||||
{
|
||||
RTTY_RX_LOCK;
|
||||
|
||||
// check if there is free space in fifo
|
||||
if (rtty_rx_fifo_freespace() == 0)
|
||||
{
|
||||
RTTY_RX_UNLOCK();
|
||||
return;
|
||||
}
|
||||
|
||||
rtty_rx_buffer[rtty_rx_wridx] = c;
|
||||
if (++rtty_rx_wridx >= RTTY_FIFOLEN) rtty_rx_wridx = 0;
|
||||
RTTY_RX_UNLOCK();
|
||||
}
|
||||
|
||||
int rtty_rx_read_fifo(char* pc)
|
||||
{
|
||||
RTTY_RX_LOCK;
|
||||
|
||||
if (rtty_rx_rdidx == rtty_rx_wridx)
|
||||
if (rdidx[pipenum] == wridx[pipenum])
|
||||
{
|
||||
// Fifo empty, no data available
|
||||
RTTY_RX_UNLOCK();
|
||||
//printf("read: no data\n");
|
||||
UNLOCKFIFO(pipenum);
|
||||
return 0;
|
||||
}
|
||||
|
||||
*pc = rtty_rx_buffer[rtty_rx_rdidx];
|
||||
if (++rtty_rx_rdidx >= RTTY_FIFOLEN) rtty_rx_rdidx = 0;
|
||||
RTTY_RX_UNLOCK();
|
||||
// read length
|
||||
int len = buffer[pipenum][rdidx[pipenum]][0];
|
||||
len <<= 8;
|
||||
len += buffer[pipenum][rdidx[pipenum]][1];
|
||||
|
||||
if (len > maxlen)
|
||||
{
|
||||
printf("read_fifo: %d, pdata too small. Need:%d has:%d\n", pipenum, len, maxlen);
|
||||
return 0; // pdata too small
|
||||
}
|
||||
|
||||
// read data
|
||||
memcpy(pdata, buffer[pipenum][rdidx[pipenum]] + 2, len);
|
||||
if (++rdidx[pipenum] >= FIFOBUFLEN) rdidx[pipenum] = 0;
|
||||
UNLOCKFIFO(pipenum);
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
void fifo_clear(int pipenum)
|
||||
{
|
||||
if (pipenum < 0 || pipenum >= NUM_OF_FIFOS) return;
|
||||
|
||||
wridx[pipenum] = rdidx[pipenum] = 0;
|
||||
}
|
||||
|
||||
int fifo_freespace(int pipenum)
|
||||
{
|
||||
if (pipenum < 0 || pipenum >= NUM_OF_FIFOS) return 0;
|
||||
|
||||
int freebuf = 0;
|
||||
|
||||
LOCKFIFO(pipenum);
|
||||
|
||||
int elemInFifo = (wridx[pipenum] + FIFOBUFLEN - rdidx[pipenum]) % FIFOBUFLEN;
|
||||
freebuf = FIFOBUFLEN - elemInFifo;
|
||||
|
||||
UNLOCKFIFO(pipenum);
|
||||
return freebuf;
|
||||
}
|
||||
|
||||
int fifo_dataavail(int pipenum)
|
||||
{
|
||||
LOCKFIFO(pipenum);
|
||||
|
||||
if (rdidx[pipenum] == wridx[pipenum])
|
||||
{
|
||||
// Fifo empty, no data available
|
||||
UNLOCKFIFO(pipenum);
|
||||
return 0;
|
||||
}
|
||||
UNLOCKFIFO(pipenum);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int fifo_usedspace(int pipenum)
|
||||
{
|
||||
int us = FIFOBUFLEN - fifo_freespace(pipenum);
|
||||
//printf("fifo:%d used space:%d\n", pipenum, us);
|
||||
return us;
|
||||
}
|
||||
|
||||
int fifo_usedpercent(int pipenum)
|
||||
{
|
||||
int used = FIFOBUFLEN - fifo_freespace(pipenum);
|
||||
int percent = (used * 100) / FIFOBUFLEN;
|
||||
return percent;
|
||||
}
|
||||
|
||||
Executable
+17
@@ -0,0 +1,17 @@
|
||||
#pragma once
|
||||
|
||||
enum _FIFOUSAGE_ {
|
||||
FIFO_RTTYTX = 0,
|
||||
PSK_GUI_TX,
|
||||
EXT_TX,
|
||||
EXT_SPECNB,
|
||||
EXT_SPECWB,
|
||||
};
|
||||
|
||||
void init_fifos();
|
||||
void write_fifo(int pipenum, uint8_t* pdata, int len);
|
||||
int read_fifo(int pipenum, uint8_t* data, int maxlen);
|
||||
void fifo_clear(int pipenum);
|
||||
int fifo_freespace(int pipenum);
|
||||
int fifo_usedspace(int pipenum);
|
||||
int fifo_usedpercent(int pipenum);
|
||||
@@ -108,11 +108,15 @@ uint8_t *Pack(uint8_t *payload, int type, int status, int *plen, int repeat)
|
||||
// polulate the raw frame
|
||||
|
||||
// make the frame counter
|
||||
if(repeat == 0 || type == 1) // 1=BER test
|
||||
framecounter++;
|
||||
// for type 8 (external app data) do not use frame counter
|
||||
if (type != 8)
|
||||
{
|
||||
if (repeat == 0 || type == 1) // 1=BER test
|
||||
framecounter++;
|
||||
|
||||
if (status == 0)
|
||||
framecounter = 0; // start of file
|
||||
if (status == 0)
|
||||
framecounter = 0; // start of file
|
||||
}
|
||||
|
||||
// insert frame counter and status bits
|
||||
frame.counter_LSB = framecounter & 0xff;
|
||||
@@ -367,7 +371,11 @@ uint8_t *getPayload(uint8_t *rxb)
|
||||
framenumrx <<= 8;
|
||||
framenumrx += frame.counter_LSB; // frame counter LSB
|
||||
|
||||
if (lastframenum != framenumrx) rx_status |= 4;
|
||||
if ((lastframenum != framenumrx) && (lastframenum != ((framenumrx+1)%1024)))
|
||||
{
|
||||
|
||||
rx_status |= 4;
|
||||
}
|
||||
lastframenum = framenumrx;
|
||||
if (++lastframenum >= 1024) lastframenum = 0; // 1024 = 2^10 (10 bit frame number)
|
||||
|
||||
|
||||
+120
-78
@@ -46,12 +46,13 @@ int keeprunning = 1;
|
||||
// UDP I/O
|
||||
int BC_sock_AppToModem = -1;
|
||||
int DATA_sock_AppToModem = -1;
|
||||
int DATA_sock_FFT_from_GR = -1;
|
||||
int DATA_sock_I_Q_from_GR = -1;
|
||||
int DATA_sock_ExtToModem = -1;
|
||||
|
||||
int UdpBCport_AppToModem = 40131;
|
||||
int UdpDataPort_AppToModem = 40132;
|
||||
int UdpDataPort_ModemToApp = 40133;
|
||||
int UdpBCport_AppToModem = 40131; // broadcast messages from GUI
|
||||
int UdpDataPort_AppToModem = 40132; // data messages from GUI
|
||||
int UdpDataPort_ModemToApp = 40133; // all messages to GUI
|
||||
int TcpDataPort_WebSocket = 40134; // web socket data exchange to local browser
|
||||
int UdpDataPort_ExtWebdata = 40135; // get data from ext. application to sent via modem
|
||||
|
||||
// op mode depending values
|
||||
// default mode if not set by the app
|
||||
@@ -93,8 +94,8 @@ int io_pbidx = -1;
|
||||
int voice_capidx = -1;
|
||||
int voice_pbidx = -1;
|
||||
|
||||
int safemode = 0;
|
||||
int sendIntro = 0;
|
||||
int extData_active = 0;
|
||||
|
||||
char mycallsign[21];
|
||||
char myqthloc[11];
|
||||
@@ -198,11 +199,14 @@ int main(int argc, char* argv[])
|
||||
#endif
|
||||
printf("user home path:<%s>\n", homepath);
|
||||
|
||||
init_tune();
|
||||
kmaudio_init();
|
||||
kmaudio_getDeviceList();
|
||||
init_packer();
|
||||
initFEC();
|
||||
init_fifos(); // init fifos for PSK data and RTTY characters
|
||||
init_distributor(); // init distribution process for PSK data
|
||||
init_tune(); // init tuning tones (mixed to signal)
|
||||
kmaudio_init(); // init soundcard driver
|
||||
kmaudio_getDeviceList();// get sound devices
|
||||
init_packer(); // init PSK packer/unpacker
|
||||
initFEC(); // init FEC calculator
|
||||
ws_init(); // init Websocket
|
||||
|
||||
// start udp RX to listen for broadcast search message from Application
|
||||
UdpRxInit(&BC_sock_AppToModem, UdpBCport_AppToModem, &bc_rxdata, &keeprunning);
|
||||
@@ -210,12 +214,16 @@ int main(int argc, char* argv[])
|
||||
// start udp RX for data from application
|
||||
UdpRxInit(&DATA_sock_AppToModem, UdpDataPort_AppToModem, &appdata_rxdata, &keeprunning);
|
||||
|
||||
// start udp RX to listen for data from external program
|
||||
// these data will be sent via QO100 (i.e.: to the receiver's websocket)
|
||||
UdpRxInit(&DATA_sock_ExtToModem, UdpDataPort_ExtWebdata, &ext_rxdata, &keeprunning);
|
||||
|
||||
printf("QO100modem initialised and running\n");
|
||||
|
||||
while (keeprunning)
|
||||
{
|
||||
int wait = 1;
|
||||
|
||||
|
||||
if (restart_modems == 1)
|
||||
{
|
||||
printf("restart modem requested\n");
|
||||
@@ -352,9 +360,10 @@ SPEEDRATE sr[NUMSPEEDMODES] = {
|
||||
|
||||
void startModem()
|
||||
{
|
||||
printf("startModem\n");
|
||||
printf("startModem. Speedmode:%d\n",set_speedmode);
|
||||
close_dsp();
|
||||
close_rtty();
|
||||
fifo_clear(PSK_GUI_TX);
|
||||
speedmode = set_speedmode;
|
||||
if (speedmode < 0 || speedmode >= NUMSPEEDMODES)
|
||||
speedmode = 4;
|
||||
@@ -367,7 +376,6 @@ void startModem()
|
||||
rxPreInterpolfactor = sr[speedmode].rx;
|
||||
linespeed = sr[speedmode].linespeed;
|
||||
opusbitrate = sr[speedmode].codecrate;
|
||||
|
||||
// int TX audio and modulator
|
||||
io_capidx = kmaudio_startCapture(captureDeviceName, caprate);
|
||||
if (io_capidx == -1)
|
||||
@@ -375,7 +383,7 @@ void startModem()
|
||||
printf("CAP: cannot open device: %s\n", captureDeviceName);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
io_pbidx = kmaudio_startPlayback(playbackDeviceName, caprate);
|
||||
if (io_pbidx == -1)
|
||||
{
|
||||
@@ -384,6 +392,7 @@ void startModem()
|
||||
}
|
||||
|
||||
_init_fft();
|
||||
|
||||
if (speedmode < 10)
|
||||
{
|
||||
init_dsp();
|
||||
@@ -393,7 +402,6 @@ void startModem()
|
||||
rtty_txoff = 1;
|
||||
init_rtty();
|
||||
}
|
||||
|
||||
init_tune();
|
||||
}
|
||||
|
||||
@@ -435,7 +443,7 @@ void initVoice()
|
||||
}
|
||||
}
|
||||
|
||||
// called from UDP callback ! DO NOT call any system functions
|
||||
// called from UDP callback
|
||||
void setSpeedmode(int spm)
|
||||
{
|
||||
printf("set speedmode:%d\n", spm);
|
||||
@@ -475,7 +483,7 @@ uint8_t *getDevList(int* plen)
|
||||
txdata[2] = (io_pbidx != -1 && devlist[io_pbidx].working) ? '1' : '0';
|
||||
txdata[3] = (voice_capidx != -1 && devlist[voice_capidx].working) ? '1' : '0';
|
||||
txdata[4] = (voice_pbidx != -1 && devlist[voice_pbidx].working) ? '1' : '0';
|
||||
|
||||
|
||||
return txdata;
|
||||
}
|
||||
|
||||
@@ -501,12 +509,14 @@ void bc_rxdata(uint8_t* pdata, int len, struct sockaddr_in* rxsock)
|
||||
* 6 ... safe mode number
|
||||
* 7 ... send Intro
|
||||
* 8 ... rtty autosync
|
||||
* 9 ... unused
|
||||
* 10 .. 109 ... PB device name
|
||||
* 110 .. 209 ... CAP device name
|
||||
* 210 .. 229 ... Callsign
|
||||
* 230 .. 239 ... qthloc
|
||||
* 240 .. 259 ... Name
|
||||
* 9 ... hsmodem speed mode
|
||||
* 10 .. external data IF on/off
|
||||
* 11-19 ... unused
|
||||
* 20 .. 119 ... PB device name
|
||||
* 120 .. 219 ... CAP device name
|
||||
* 220 .. 239 ... Callsign
|
||||
* 230 .. 249 ... qthloc
|
||||
* 250 .. 269 ... Name
|
||||
*/
|
||||
|
||||
char rxip[20];
|
||||
@@ -555,23 +565,32 @@ void bc_rxdata(uint8_t* pdata, int len, struct sockaddr_in* rxsock)
|
||||
return;
|
||||
}
|
||||
|
||||
memcpy(mycallsign, pdata + 210, sizeof(mycallsign));
|
||||
memcpy(mycallsign, pdata + 220, sizeof(mycallsign));
|
||||
mycallsign[sizeof(mycallsign) - 1] = 0;
|
||||
|
||||
memcpy(myqthloc, pdata + 230, sizeof(myqthloc));
|
||||
memcpy(myqthloc, pdata + 240, sizeof(myqthloc));
|
||||
myqthloc[sizeof(myqthloc) - 1] = 0;
|
||||
|
||||
memcpy(myname, pdata + 240, sizeof(myname));
|
||||
memcpy(myname, pdata + 250, sizeof(myname));
|
||||
myname[sizeof(myname) - 1] = 0;
|
||||
|
||||
if(pdata[9] != 255 && set_speedmode != pdata[9])
|
||||
setSpeedmode(pdata[9]);
|
||||
|
||||
//printf("<%s> <%s> <%s>\n", mycallsign, myqthloc, myname);
|
||||
|
||||
//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];
|
||||
io_setAudioDevices(pdata[1], pdata[2], pdata[3], pdata[4], pdata[5], (char*)(pdata + 20), (char*)(pdata + 120));
|
||||
sendIntro = pdata[7];
|
||||
rtty_autosync = pdata[8];
|
||||
|
||||
if (extData_active == 0 && pdata[10] == 1)
|
||||
printf("ext.Data activated\n");
|
||||
else if (extData_active == 1 && pdata[10] == 0)
|
||||
printf("ext.Data deactivated\n");
|
||||
|
||||
extData_active = pdata[10];
|
||||
|
||||
lastms = actms;
|
||||
}
|
||||
}
|
||||
@@ -583,12 +602,17 @@ void appdata_rxdata(uint8_t* pdata, int len, struct sockaddr_in* rxsock)
|
||||
uint8_t minfo = pdata[1];
|
||||
|
||||
//printf("from GUI: %d %d\n", pdata[0], pdata[1]);
|
||||
|
||||
|
||||
// type values: see oscardata config.cs: frame types
|
||||
|
||||
if (type == 16)
|
||||
{
|
||||
// Byte 1 contains the speed mode index
|
||||
setSpeedmode(pdata[1]);
|
||||
// a bulletin file from the GUI
|
||||
// has to be sent to webbrowsers via websocket
|
||||
//printf("Bulletin contents:\n<%s>\n", pdata + 1);
|
||||
// the first byte (16) is used as the external type specifier
|
||||
|
||||
ws_send(pdata, len);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -741,7 +765,7 @@ void appdata_rxdata(uint8_t* pdata, int len, struct sockaddr_in* rxsock)
|
||||
if (type == 30)
|
||||
{
|
||||
// rtty key pressed
|
||||
rtty_tx_write_fifo(minfo);
|
||||
write_fifo(FIFO_RTTYTX,&minfo,1);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -753,11 +777,13 @@ void appdata_rxdata(uint8_t* pdata, int len, struct sockaddr_in* rxsock)
|
||||
len += pdata[2];
|
||||
len++; // the first toTX command
|
||||
//printf("hsmodem.cpp rtty_tx_write_fifo: ");
|
||||
for (int i = 0; i < len; i++)
|
||||
write_fifo(FIFO_RTTYTX, pdata+3,len);
|
||||
|
||||
/*for (int i = 0; i < len; i++)
|
||||
{
|
||||
//printf("%c", pdata[3 + i]);
|
||||
rtty_tx_write_fifo(pdata[3 + i]);
|
||||
}
|
||||
write_fifo(FIFO_RTTYTX, pdata[3 + i]);
|
||||
}*/
|
||||
//printf("\n");
|
||||
return;
|
||||
}
|
||||
@@ -773,13 +799,14 @@ void appdata_rxdata(uint8_t* pdata, int len, struct sockaddr_in* rxsock)
|
||||
{
|
||||
// stop TX immediately
|
||||
rtty_txoff = 1;
|
||||
clear_rtty_txfifo();
|
||||
fifo_clear(FIFO_RTTYTX);
|
||||
}
|
||||
}
|
||||
if (type >= 29 && type <= 32) return;
|
||||
|
||||
if (speedmode == 10) return;
|
||||
|
||||
|
||||
// here we are with payload data to be sent via the modulator
|
||||
|
||||
if (len != (PAYLOADLEN + 2))
|
||||
@@ -790,22 +817,12 @@ void appdata_rxdata(uint8_t* pdata, int len, struct sockaddr_in* rxsock)
|
||||
|
||||
//if (getSending() == 1) return; // already sending (Array sending)
|
||||
|
||||
// send a payload
|
||||
if (minfo == 0 || minfo == 3)
|
||||
{
|
||||
// this is the first frame of a larger file
|
||||
sendAnnouncement();
|
||||
// send first frame multiple times, like a preamble, to give the
|
||||
// receiver some time for synchronisation
|
||||
// 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 6s: 6* ((caprate/txinterpolfactor) * bitsPerSymbol / 8) /258 + 1 frames
|
||||
toGR_sendData(pdata + 2, type, minfo,0);
|
||||
int numframespreamble = 6 * ((caprate / txinterpolfactor) * bitsPerSymbol / 8) / 258 + 1;
|
||||
//if (type == 1)// BER Test
|
||||
// numframespreamble = 1;
|
||||
for (int i = 0; i < numframespreamble; i++)
|
||||
toGR_sendData(pdata + 2, type, minfo,1);
|
||||
toGR_sendData(pdata + 2, type, minfo, 5); // repeat the first frame a couple of times
|
||||
sendStationInfo();
|
||||
}
|
||||
else if ((len - 2) < PAYLOADLEN)
|
||||
@@ -814,45 +831,63 @@ void appdata_rxdata(uint8_t* pdata, int len, struct sockaddr_in* rxsock)
|
||||
uint8_t payload[PAYLOADLEN];
|
||||
memset(payload, 0, PAYLOADLEN);
|
||||
memcpy(payload, pdata + 2, len - 2);
|
||||
toGR_sendData(payload, type, minfo,0);
|
||||
if (safemode > 0)
|
||||
{
|
||||
for (int sm = 0; sm < safemode; sm++)
|
||||
toGR_sendData(payload, type, minfo, 1);
|
||||
}
|
||||
if (minfo == 2)
|
||||
{
|
||||
// repeat last frame
|
||||
for (int rl = 0; rl < (10 - safemode); rl++)
|
||||
toGR_sendData(payload, type, minfo, 1);
|
||||
}
|
||||
|
||||
if (minfo == 2) // if its the last frame, repeate a couple of times
|
||||
toGR_sendData(payload, type, minfo, 5);
|
||||
else
|
||||
toGR_sendData(payload, type, minfo, 0); // send only once
|
||||
}
|
||||
else
|
||||
{
|
||||
toGR_sendData(pdata + 2, type, minfo,0);
|
||||
|
||||
if (safemode > 0)
|
||||
{
|
||||
for(int sm=0; sm < safemode; sm++)
|
||||
toGR_sendData(pdata + 2, type, minfo, 1);
|
||||
}
|
||||
if (minfo == 2)
|
||||
{
|
||||
// repeat last frame
|
||||
for(int rl = 0; rl < (10-safemode); rl ++)
|
||||
toGR_sendData(pdata + 2, type, minfo, 1);
|
||||
}
|
||||
// normal sending: continous or last frame
|
||||
if (minfo == 2) // if its the last frame, repeate a couple of times
|
||||
toGR_sendData(pdata + 2, type, minfo, 5);
|
||||
else
|
||||
toGR_sendData(pdata + 2, type, minfo, 0);
|
||||
}
|
||||
}
|
||||
|
||||
// pack and send PSK data
|
||||
void toGR_sendData(uint8_t* data, int type, int status, int repeat)
|
||||
{
|
||||
modem_sendPSKData(data, type, status, repeat, PSK_GUI_TX);
|
||||
}
|
||||
|
||||
// pack and send PSK data
|
||||
// handle repetitions and check if TX was down
|
||||
// repeat: 0=do not repeat, 1=repeat if currently not sending, >1 = number of repetitions
|
||||
void modem_sendPSKData(uint8_t* data, int type, int status, int repeat, int fifoID)
|
||||
{
|
||||
// send the first frame normal (with a new frame counter value)
|
||||
int len = 0;
|
||||
uint8_t* txdata = Pack(data, type, status, &len, repeat);
|
||||
uint8_t* txdata = Pack(data, type, status, &len, 0);
|
||||
if (txdata != NULL)
|
||||
{
|
||||
sendPSKdata(txdata, len, fifoID);
|
||||
}
|
||||
if (repeat == 0) return;
|
||||
|
||||
//showbytestring((char *)"TX: ", txdata, len);
|
||||
// now check if repetitions are required
|
||||
if (bitsPerSymbol == 0 || txinterpolfactor == 0) return; // just for security, no useful function
|
||||
int repetitions = 6 * ((caprate / txinterpolfactor) * bitsPerSymbol / 8) / 258 + 1;
|
||||
|
||||
if (txdata != NULL) sendToModulator(txdata, len);
|
||||
if (isPlaying(io_pbidx) == 0) // if not sending, repeat frame
|
||||
{
|
||||
if (repeat == 1)
|
||||
repeat = repetitions;
|
||||
else if (repeat > 1)
|
||||
{
|
||||
// if TX was down, do at least "repetitions" repetitions
|
||||
if (repeat < repetitions) repeat = repetitions;
|
||||
}
|
||||
}
|
||||
|
||||
// and the rest repeated if requested
|
||||
txdata = Pack(data, type, status, &len, 1);
|
||||
for (int i = 0; i < repeat; i++)
|
||||
{
|
||||
if (txdata != NULL) sendPSKdata(txdata, len, fifoID);
|
||||
}
|
||||
}
|
||||
|
||||
void sendStationInfo()
|
||||
@@ -869,7 +904,7 @@ void sendStationInfo()
|
||||
|
||||
for (int i = 0; i < 2; i++)
|
||||
{
|
||||
if (txdata != NULL) sendToModulator(txdata, len);
|
||||
if (txdata != NULL) sendPSKdata(txdata, len, PSK_GUI_TX);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -885,6 +920,13 @@ void GRdata_rxdata(uint8_t* pdata, int len, struct sockaddr_in* rxsock)
|
||||
{
|
||||
// complete frame received
|
||||
//printf("type:%d\n", pl[0]);
|
||||
|
||||
if (pl[0] == 8)
|
||||
{
|
||||
// external data received
|
||||
ext_modemRX(pl);
|
||||
}
|
||||
|
||||
// send payload to app
|
||||
uint8_t txpl[PAYLOADLEN + 10 + 1];
|
||||
memcpy(txpl + 1, pl, PAYLOADLEN + 10);
|
||||
|
||||
+12
-44
@@ -66,7 +66,9 @@
|
||||
#include "codec2.h"
|
||||
#include "libkmaudio/soundio.h"
|
||||
#include "baudot.h"
|
||||
#include "fifo.h"
|
||||
#include "libkmaudio/libkmaudio.h"
|
||||
#include "websocket/websocketserver.h"
|
||||
|
||||
#define jpg_tempfilename "rxdata.jpg"
|
||||
|
||||
@@ -127,37 +129,6 @@ void measure_speed_bps(int len);
|
||||
void initFEC();
|
||||
void GetFEC(uint8_t* txblock, int len, uint8_t* destArray);
|
||||
int cfec_Reconstruct(uint8_t* darr, uint8_t* destination);
|
||||
/*
|
||||
void io_pb_write_fifo_clear();
|
||||
int io_init_sound(char* pbname, char* capname);
|
||||
int io_pb_fifo_freespace(int nolock);
|
||||
void io_init_pipes();
|
||||
void io_clear_audio_fifos();
|
||||
void io_close_audio();
|
||||
int io_cap_read_fifo(float* data);
|
||||
void io_readAudioDevices();
|
||||
uint8_t* io_getAudioDevicelist(int* len);
|
||||
void io_pb_write_fifo(float sample);
|
||||
int io_pb_fifo_usedspace();
|
||||
int io_cap_fifo_usedPercent();
|
||||
int io_pb_read_fifo_num(float* data, int num);
|
||||
void io_clear_audio_fifos();
|
||||
int io_pb_fifo_usedBlocks();
|
||||
void io_voice_init_pipes();
|
||||
int io_mic_read_fifo(float* data);
|
||||
void io_ls_write_fifo(float sample);
|
||||
char* getDevID(char* devname, int io);
|
||||
int io_init_voice(char* lsname, char* micname);
|
||||
int min_int(int a, int b);
|
||||
void io_close_voice();
|
||||
int io_ls_read_fifo_num(float* data, int num);
|
||||
void io_mic_write_fifo(float sample);
|
||||
void write_sample_s16ne(char* ptr, double sample);
|
||||
int io_ls_fifo_usedspace();
|
||||
void write_sample_float32ne(char* ptr, double sample);
|
||||
void io_clear_voice_fifos();
|
||||
|
||||
*/
|
||||
|
||||
void io_setPBvolume(int v);
|
||||
void io_setCAPvolume(int v);
|
||||
@@ -175,13 +146,15 @@ void modulator(uint8_t sym_in);
|
||||
|
||||
void init_dsp();
|
||||
int demodulator();
|
||||
void sendToModulator(uint8_t* d, int len);
|
||||
void _sendToModulator(uint8_t* d, int len);
|
||||
void resetModem();
|
||||
void close_dsp();
|
||||
void _init_fft();
|
||||
void _exit_fft();
|
||||
void showbytestringf(char* title, float* data, int totallen, int anz);
|
||||
uint16_t* make_waterfall(float fre, int* retlen);
|
||||
void sendPSKdata(uint8_t* data, int len, int fifoID);
|
||||
void modem_sendPSKData(uint8_t* data, int type, int status, int repeat, int fifoID);
|
||||
|
||||
void toCodecDecoder(uint8_t* pdata, int len);
|
||||
|
||||
@@ -212,26 +185,22 @@ void playIntro();
|
||||
float do_tuning(int send);
|
||||
void init_tune();
|
||||
float singleFrequency();
|
||||
int rtty_rx();
|
||||
void modifyRXfreq(float diff_Hz, int absolute);
|
||||
void showbytestring16(char* title, uint16_t* data, int anz);
|
||||
void rtty_sendChar(int c);
|
||||
void init_rtty();
|
||||
int do_rtty();
|
||||
void make_FFTdata(float f);
|
||||
void close_rtty();
|
||||
void close_a();
|
||||
void rtty_modifyRXfreq(int);
|
||||
void showbitstring(char* title, uint8_t* data, int totallen, int anz);
|
||||
void rtty_tx_write_fifo(char c);
|
||||
int rtty_tx_read_fifo(char* pc);
|
||||
void rtty_rx_write_fifo(char c);
|
||||
int rtty_rx_read_fifo(char* pc);
|
||||
void clear_rtty_txfifo();
|
||||
void fmtest();
|
||||
void rtty_init_pipes();
|
||||
void initVoice();
|
||||
void sendStationInfo();
|
||||
void ext_rxdata(uint8_t* pdata, int len, struct sockaddr_in* rxsock);
|
||||
void init_distributor();
|
||||
void ext_modemRX(uint8_t* pdata);
|
||||
int fifo_dataavail(int pipenum);
|
||||
void showbytestring32(char* title, uint32_t* data, int anz);
|
||||
void start_timer(int mSec, void(*timer_func_handler)(void));
|
||||
|
||||
|
||||
extern int speedmode;
|
||||
@@ -262,7 +231,6 @@ extern int trigger_resetmodem;
|
||||
extern int rxlevel_deteced;
|
||||
extern int rx_in_sync;
|
||||
extern int restart_modems;
|
||||
extern int safemode;
|
||||
extern char homepath[];
|
||||
extern int sendIntro;
|
||||
extern int tuning;
|
||||
@@ -280,7 +248,7 @@ extern float pbvol;
|
||||
extern float capvol;
|
||||
extern float lsvol;
|
||||
extern float micvol;
|
||||
|
||||
extern int extData_active;
|
||||
|
||||
#ifdef _LINUX_
|
||||
int isRunning(char* prgname);
|
||||
|
||||
@@ -228,6 +228,7 @@
|
||||
<ClInclude Include="fec.h" />
|
||||
<ClInclude Include="fftw3.h" />
|
||||
<ClInclude Include="fftw_lib\fftw3.h" />
|
||||
<ClInclude Include="fifo.h" />
|
||||
<ClInclude Include="frameformat.h" />
|
||||
<ClInclude Include="hsmodem.h" />
|
||||
<ClInclude Include="libkmaudio\endian.h" />
|
||||
@@ -239,17 +240,23 @@
|
||||
<ClInclude Include="opus_types.h" />
|
||||
<ClInclude Include="symboltracker.h" />
|
||||
<ClInclude Include="udp.h" />
|
||||
<ClInclude Include="websocket\base64.h" />
|
||||
<ClInclude Include="websocket\sha1.h" />
|
||||
<ClInclude Include="websocket\websocketserver.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="announcement.cpp" />
|
||||
<ClCompile Include="codec2.cpp" />
|
||||
<ClCompile Include="constellation.cpp" />
|
||||
<ClCompile Include="crc16.cpp" />
|
||||
<ClCompile Include="distrubution.cpp" />
|
||||
<ClCompile Include="extdata.cpp" />
|
||||
<ClCompile Include="fec.cpp" />
|
||||
<ClCompile Include="fft.cpp" />
|
||||
<ClCompile Include="fifo.cpp" />
|
||||
<ClCompile Include="frame_packer.cpp" />
|
||||
<ClCompile Include="hsmodem.cpp" />
|
||||
<ClCompile Include="kmtimer.cpp" />
|
||||
<ClCompile Include="libkmaudio\libkmaudio_capture.cpp" />
|
||||
<ClCompile Include="libkmaudio\libkmaudio_capture_linux.cpp" />
|
||||
<ClCompile Include="libkmaudio\libkmaudio_fifo.cpp" />
|
||||
@@ -271,6 +278,12 @@
|
||||
<ClCompile Include="udp.cpp" />
|
||||
<ClCompile Include="voiceprocessor.cpp" />
|
||||
<ClCompile Include="volume.cpp" />
|
||||
<ClCompile Include="websocket\base64.cpp" />
|
||||
<ClCompile Include="websocket\handshake.cpp" />
|
||||
<ClCompile Include="websocket\sha1.cpp" />
|
||||
<ClCompile Include="websocket\websocketserver.cpp" />
|
||||
<ClCompile Include="websocket\ws.cpp" />
|
||||
<ClCompile Include="websocket\ws_callbacks.cpp" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
|
||||
@@ -105,6 +105,33 @@
|
||||
<ClCompile Include="volume.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="websocket\base64.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="websocket\handshake.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="websocket\sha1.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="websocket\websocketserver.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="websocket\ws.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="websocket\ws_callbacks.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="extdata.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="distrubution.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="kmtimer.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="hsmodem.h">
|
||||
@@ -155,5 +182,17 @@
|
||||
<ClInclude Include="libkmaudio\endian.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="websocket\base64.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="websocket\sha1.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="websocket\websocketserver.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="fifo.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
Executable
+101
@@ -0,0 +1,101 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>QO-100 Data</title>
|
||||
<style>
|
||||
</style>
|
||||
|
||||
<script type="text/javascript">
|
||||
|
||||
window.onload = onstart;
|
||||
var sockintv;
|
||||
var sockOpen = 0;
|
||||
var websocket;
|
||||
|
||||
function onstart()
|
||||
{
|
||||
sockintv = setInterval(checkSocket, 1000);
|
||||
}
|
||||
|
||||
var intervalset = 0;
|
||||
|
||||
// checks if the socket is connected,
|
||||
// if not, try to connect
|
||||
function checkSocket()
|
||||
{
|
||||
if(sockOpen == 0)
|
||||
{
|
||||
if(websocket != null)
|
||||
websocketclose();
|
||||
openWebSocket();
|
||||
}
|
||||
|
||||
websocket.send("alive\0");
|
||||
|
||||
// set interval to 5s, this ensures that it can reconnect
|
||||
// if the server was down. This does not work with faster intervals
|
||||
if(intervalset == 0)
|
||||
{
|
||||
intervalset = 1;
|
||||
clearInterval(sockintv);
|
||||
sockintv = setInterval(checkSocket, 5000);
|
||||
}
|
||||
}
|
||||
|
||||
function websocketclose()
|
||||
{
|
||||
if(websocket != null)
|
||||
{
|
||||
console.log("close websocket");
|
||||
websocket.close();
|
||||
websocket = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
console.log("close websocket, already closed");
|
||||
}
|
||||
}
|
||||
|
||||
var msg = "";
|
||||
|
||||
function openWebSocket()
|
||||
{
|
||||
window.WebSocket = window.WebSocket || window.MozWebSocket;
|
||||
|
||||
sockurl = "ws://" + "localhost" + ":40134";
|
||||
websocket = new WebSocket(sockurl);
|
||||
websocket.binaryType = "arraybuffer";
|
||||
|
||||
websocket.onopen = function () {
|
||||
sockOpen = 1;
|
||||
console.log("WebSocket " + sockurl + " now OPEN");
|
||||
};
|
||||
|
||||
websocket.onerror = function () {
|
||||
console.log("Error ... reconnecting ...");
|
||||
websocketclose();
|
||||
sockOpen = 0;
|
||||
};
|
||||
|
||||
websocket.onclose = function () {
|
||||
console.log("Disconnected ... connecting ...");
|
||||
websocketclose();
|
||||
sockOpen = 0;
|
||||
};
|
||||
|
||||
websocket.onmessage = function (message)
|
||||
{
|
||||
var arr = new Uint8Array(message.data);
|
||||
console.log("message received: length:" + arr.length + " data" + arr);
|
||||
// TODO: do whatever you want with this message
|
||||
};
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
HSmodem Websocket Example
|
||||
</body>
|
||||
</html>
|
||||
|
||||
Executable
+125
@@ -0,0 +1,125 @@
|
||||
/*
|
||||
* High Speed modem to transfer data in a 2,7kHz SSB channel
|
||||
* =========================================================
|
||||
* Author: DJ0ABR
|
||||
*
|
||||
* (c) DJ0ABR
|
||||
* www.dj0abr.de
|
||||
|
||||
websocket server: based on the work by: Davidson Francis <davidsondfgl@gmail.com>
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
*
|
||||
* Repeatable Timer for various needs
|
||||
* ==================================
|
||||
*
|
||||
*/
|
||||
|
||||
#include "hsmodem.h"
|
||||
|
||||
/*
|
||||
usage:
|
||||
|
||||
void timerhandler_function(void)
|
||||
{
|
||||
// called every time_in_ms
|
||||
}
|
||||
|
||||
start_timer(time_in_ms, &timerhandler_function);
|
||||
|
||||
*/
|
||||
|
||||
void(*timer_func_handler_pntr)(void);
|
||||
|
||||
#ifdef _LINUX_
|
||||
|
||||
struct itimerval timervalue;
|
||||
struct sigaction new_handler, old_handler;
|
||||
void timer_sig_handler(int);
|
||||
|
||||
void start_timer(int mSec, void(*timer_func_handler)(void))
|
||||
{
|
||||
timer_func_handler_pntr = timer_func_handler;
|
||||
|
||||
timervalue.it_interval.tv_sec = mSec / 1000;
|
||||
timervalue.it_interval.tv_usec = (mSec % 1000) * 1000;
|
||||
timervalue.it_value.tv_sec = mSec / 1000;
|
||||
timervalue.it_value.tv_usec = (mSec % 1000) * 1000;
|
||||
if (setitimer(ITIMER_REAL, &timervalue, NULL))
|
||||
{
|
||||
printf("start_timer() error\n");
|
||||
return;
|
||||
}
|
||||
|
||||
new_handler.sa_handler = &timer_sig_handler;
|
||||
new_handler.sa_flags = SA_NOMASK;
|
||||
if (sigaction(SIGALRM, &new_handler, &old_handler))
|
||||
{
|
||||
printf("sigaction() error\n");
|
||||
return;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
void timer_sig_handler(int arg)
|
||||
{
|
||||
timer_func_handler_pntr();
|
||||
}
|
||||
|
||||
|
||||
void stop_timer(void)
|
||||
{
|
||||
timervalue.it_interval.tv_sec = 0;
|
||||
timervalue.it_interval.tv_usec = 0;
|
||||
timervalue.it_value.tv_sec = 0;
|
||||
timervalue.it_value.tv_usec = 0;
|
||||
setitimer(ITIMER_REAL, &timervalue, NULL);
|
||||
|
||||
sigaction(SIGALRM, &old_handler, NULL);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef WIN32
|
||||
|
||||
int ms;
|
||||
unsigned int timethreadID = 0;
|
||||
unsigned int __stdcall TimerThread(void* p);
|
||||
|
||||
void start_timer(int mSec, void(*timer_func_handler)(void))
|
||||
{
|
||||
timer_func_handler_pntr = timer_func_handler;
|
||||
ms = mSec;
|
||||
|
||||
_beginthreadex(NULL, 0, TimerThread, NULL, 0, &timethreadID);
|
||||
}
|
||||
|
||||
unsigned int __stdcall TimerThread(void* p)
|
||||
{
|
||||
HANDLE event_handle = CreateEvent(NULL, FALSE, FALSE, "mytimerhandle");
|
||||
while (keeprunning)
|
||||
{
|
||||
switch (WaitForSingleObject(event_handle, ms))
|
||||
{
|
||||
case WAIT_TIMEOUT:
|
||||
timer_func_handler_pntr();
|
||||
break;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -200,12 +200,13 @@ int io_fifo_freespace(int pipenum);
|
||||
// returns number of used elements (audio 16 bit short values)
|
||||
int io_fifo_usedspace(int pipenum);
|
||||
|
||||
// like before, but returns a number between 0 and 100 %
|
||||
int io_fifo_usedpercent(int pipenum);
|
||||
|
||||
// clear the fifo
|
||||
void io_fifo_clear(int pipenum);
|
||||
|
||||
// check if a playbackdevice is currently playing
|
||||
int isPlaying(int id);
|
||||
|
||||
|
||||
// -------- functions for internal use only --------
|
||||
|
||||
@@ -222,6 +223,7 @@ typedef struct _DEVLIST_ {
|
||||
int requested_samprate = 0; // sample rate requested by caller
|
||||
int real_samprate = 0; // real sample rate of the device
|
||||
int working = 0; // 0=not running, 1=initialized and working
|
||||
int audio_playing = 0; // audio is currently playing, or not
|
||||
#ifdef WIN32 // Windows using portaudio
|
||||
int devnum = -1; // port audio device number
|
||||
PaStreamParameters inputParameters;
|
||||
@@ -262,7 +264,7 @@ void close_playback_stream(int idx);
|
||||
|
||||
extern DEVLIST devlist[MAXDEVICES];
|
||||
extern int devanz;
|
||||
extern int keeprunning;
|
||||
extern int keepcallbacksrunning;
|
||||
|
||||
#ifndef WIN32 // Linux
|
||||
int kmaudio_init_linux();
|
||||
|
||||
@@ -150,7 +150,7 @@ int recordCallback( const void* inputBuffer,
|
||||
(void)timeInfo;
|
||||
(void)statusFlags;
|
||||
|
||||
if(keeprunning == 1)
|
||||
if(keepcallbacksrunning == 1)
|
||||
return paContinue;
|
||||
|
||||
return paComplete;
|
||||
|
||||
@@ -66,7 +66,7 @@ void read_callback(struct SoundIoInStream* instream, int frame_count_min, int fr
|
||||
struct SoundIoChannelArea* areas;
|
||||
// samples are in areas.ptr
|
||||
int frames_left = frame_count_max; // take all
|
||||
while (keeprunning)
|
||||
while (keepcallbacksrunning)
|
||||
{
|
||||
int frame_count = frames_left;
|
||||
if ((err = soundio_instream_begin_read(instream, &areas, &frame_count)))
|
||||
|
||||
@@ -233,7 +233,7 @@ int io_fifo_elems_avail(int pipenum)
|
||||
elems = (io_wridx[pipenum] + AUDIO_FIFOFLEN - io_rdidx[pipenum]) % AUDIO_FIFOFLEN;
|
||||
UNLOCK(pipenum);
|
||||
|
||||
elems -= 10;
|
||||
elems -= 10; // give some reserve
|
||||
return elems;
|
||||
}
|
||||
|
||||
@@ -241,12 +241,3 @@ int io_fifo_usedspace(int pipenum)
|
||||
{
|
||||
return AUDIO_FIFOFLEN - io_fifo_freespace(pipenum);
|
||||
}
|
||||
|
||||
int io_fifo_usedpercent(int pipenum)
|
||||
{
|
||||
int used = AUDIO_FIFOFLEN - io_fifo_freespace(pipenum);
|
||||
int percent = (used * 100) / AUDIO_FIFOFLEN;
|
||||
//printf("idx:%d used:%d size:%d percent:%d\n", pipenum, used, AUDIO_FIFOFLEN, percent);
|
||||
return percent;
|
||||
}
|
||||
|
||||
|
||||
@@ -34,7 +34,7 @@
|
||||
|
||||
void kmaudio_close();
|
||||
|
||||
//int keeprunning = 1; // to stop callbacks at program end
|
||||
int keepcallbacksrunning = 1; // to stop callbacks at program end
|
||||
|
||||
int kmaudio_init()
|
||||
{
|
||||
@@ -43,7 +43,7 @@ int kmaudio_init()
|
||||
|
||||
printf("libkmaudio_init\n");
|
||||
|
||||
keeprunning = 1;
|
||||
keepcallbacksrunning = 1;
|
||||
init_pipes(); // init fifo
|
||||
init_maxarray(); // init array for maxlevel measurement
|
||||
|
||||
@@ -86,7 +86,7 @@ void kmaudio_close()
|
||||
#else
|
||||
kmaudio_close_linux();
|
||||
#endif
|
||||
keeprunning = 0;
|
||||
keepcallbacksrunning = 0;
|
||||
}
|
||||
/*
|
||||
// diagonstic routines for development
|
||||
|
||||
@@ -123,6 +123,11 @@ void getMax(int id, float fv)
|
||||
farridx[id] = 0;
|
||||
}
|
||||
|
||||
int isPlaying(int id)
|
||||
{
|
||||
return devlist[id].audio_playing;
|
||||
}
|
||||
|
||||
/*
|
||||
* returns the max level (within 1 second) of this stream in % (0..100)
|
||||
* if the level >= 100 the signal will get clipped and distorted
|
||||
|
||||
@@ -142,6 +142,7 @@ int playCallback( const void* inputBuffer,
|
||||
|
||||
//measure_speed_bps(framesPerBuffer);
|
||||
|
||||
/* das hier ging
|
||||
int16_t f[FRAMES_PER_BUFFER];
|
||||
memset(f, 0, sizeof(int16_t) * FRAMES_PER_BUFFER);
|
||||
unsigned int num = io_read_fifo_num_short(devidx, f, framesPerBuffer);
|
||||
@@ -150,6 +151,23 @@ int playCallback( const void* inputBuffer,
|
||||
//printf("got %d from fifo, requested %d\n", num, framesPerBuffer);
|
||||
}
|
||||
int av = io_fifo_elems_avail(devidx);
|
||||
das nächste ist neu
|
||||
*/
|
||||
|
||||
int16_t f[FRAMES_PER_BUFFER];
|
||||
// if fifo does not have enough data, just send 0.. (silence)
|
||||
// this gives the fifo a chance to fill up a bit
|
||||
memset(f, 0, sizeof(int16_t) * FRAMES_PER_BUFFER);
|
||||
if ((unsigned long)io_fifo_elems_avail(devidx) >= framesPerBuffer)
|
||||
{
|
||||
io_read_fifo_num_short(devidx, f, framesPerBuffer);
|
||||
devlist[devidx].audio_playing = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
// nothing to send
|
||||
devlist[devidx].audio_playing = 0;
|
||||
}
|
||||
|
||||
for (unsigned int i = 0; i < framesPerBuffer; i++)
|
||||
{
|
||||
@@ -166,7 +184,7 @@ int playCallback( const void* inputBuffer,
|
||||
(void)timeInfo;
|
||||
(void)statusFlags;
|
||||
|
||||
if (keeprunning == 1)
|
||||
if (keepcallbacksrunning == 1)
|
||||
return paContinue;
|
||||
|
||||
return paComplete;
|
||||
|
||||
@@ -85,12 +85,20 @@ static void write_callback(struct SoundIoOutStream* outstream, int frame_count_m
|
||||
}
|
||||
|
||||
float f[10000];
|
||||
// if fifo does not have enough data, just send 0.. (silence)
|
||||
// this gives the fifo a chance to fill up a bit
|
||||
memset(f, 0, sizeof(float) * frame_count);
|
||||
if (io_fifo_elems_avail(idx) >= frame_count)
|
||||
{
|
||||
// if fifo does not have enough data, don't take any
|
||||
// this gives the fifo a chance to fill up a bit
|
||||
io_read_fifo_num(idx, f, frame_count);
|
||||
//if (devlist[idx].audio_playing == 0) printf("NOW PLAYING\n");
|
||||
devlist[idx].audio_playing = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
// nothing to send
|
||||
//if (devlist[idx].audio_playing == 1) printf("STOPS playing\n");
|
||||
devlist[idx].audio_playing = 0;
|
||||
}
|
||||
|
||||
//measure_speed_bps(frame_count);
|
||||
@@ -132,8 +140,6 @@ static void write_callback(struct SoundIoOutStream* outstream, int frame_count_m
|
||||
|
||||
void underflow_callback(struct SoundIoOutStream* outstream)
|
||||
{
|
||||
static int count = 0;
|
||||
printf("underflow %d\n", count++);
|
||||
}
|
||||
|
||||
void close_playback_stream(int idx)
|
||||
@@ -148,7 +154,7 @@ void close_playback_stream(int idx)
|
||||
int kmaudio_startPlayback(char* devname, int samprate)
|
||||
{
|
||||
printf("Start request for PB stream:%s\n", devname);
|
||||
|
||||
|
||||
if (devname == NULL || strlen(devname) < 3) // no devices defined yet
|
||||
{
|
||||
printf("no PB devices specified\n");
|
||||
@@ -157,10 +163,11 @@ int kmaudio_startPlayback(char* devname, int samprate)
|
||||
|
||||
int idx = 0; // index into devlist
|
||||
char* pbdevid = getDevID(devname, 1, &idx);
|
||||
printf("idx:%d\n", idx);
|
||||
if (pbdevid == NULL) return -1;
|
||||
|
||||
close_playback_stream(idx);
|
||||
|
||||
|
||||
printf("Starting PB stream:%s [%d]\n", devname, idx);
|
||||
|
||||
io_fifo_clear(idx);
|
||||
@@ -199,7 +206,7 @@ int kmaudio_startPlayback(char* devname, int samprate)
|
||||
printf("soundio_outstream_create: out of memory\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
devlist[idx].requested_samprate = samprate;
|
||||
if (getRealSamprate(idx) == -1)
|
||||
{
|
||||
@@ -209,7 +216,7 @@ int kmaudio_startPlayback(char* devname, int samprate)
|
||||
|
||||
if (devlist[idx].requested_samprate != devlist[idx].real_samprate)
|
||||
resampler_create(idx);
|
||||
|
||||
|
||||
devlist[idx].outstream->format = SoundIoFormatFloat32NE;
|
||||
devlist[idx].outstream->sample_rate = devlist[idx].real_samprate;
|
||||
devlist[idx].outstream->software_latency = 0.1f;
|
||||
@@ -227,12 +234,13 @@ int kmaudio_startPlayback(char* devname, int samprate)
|
||||
printf("unable to start output device: %s", soundio_strerror(err));
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
printf("selected PLAYBACK device:\nname:%s\nid :%s\n", devname, pbdevid);
|
||||
printf("physical playback rate:%d, requested capture rate:%d\n", devlist[idx].real_samprate, devlist[idx].requested_samprate);
|
||||
printf("format: %s\n\n", soundio_format_string(devlist[idx].outstream->format));
|
||||
|
||||
|
||||
devlist[idx].working = 1;
|
||||
|
||||
return idx;
|
||||
}
|
||||
|
||||
|
||||
@@ -124,9 +124,9 @@ typedef enum PaErrorCode
|
||||
paNoError = 0,
|
||||
|
||||
paNotInitialized = -10000,
|
||||
paUnanticipatedHostError,
|
||||
paInvalidChannelCount,
|
||||
paInvalidSampleRate,
|
||||
paUnanticipatedHostError, // -9999
|
||||
paInvalidChannelCount, // -9998
|
||||
paInvalidSampleRate, // -9997
|
||||
paInvalidDevice,
|
||||
paInvalidFlag,
|
||||
paSampleFormatNotSupported,
|
||||
|
||||
+13
-1
@@ -143,7 +143,7 @@ void close_modulator()
|
||||
|
||||
// d ... symbols to send
|
||||
// len ... number of symbols in d
|
||||
void sendToModulator(uint8_t *d, int len)
|
||||
void _sendToModulator(uint8_t *d, int len)
|
||||
{
|
||||
if(upnco == NULL) return;
|
||||
|
||||
@@ -304,15 +304,25 @@ void make_FFTdata(float f)
|
||||
if (speedmode < 10)
|
||||
{
|
||||
// Bytes in Fifo
|
||||
/*
|
||||
int bus = io_fifo_usedspace(io_pbidx);
|
||||
// Payloads in fifo
|
||||
if (bitsPerSymbol == 0 || txinterpolfactor == 0) return; // just for security, no useful function
|
||||
us = bus / (txinterpolfactor * UDPBLOCKLEN * 8 / bitsPerSymbol);
|
||||
*/
|
||||
|
||||
// checke PSK_GUI_TX
|
||||
us = fifo_usedspace(PSK_GUI_TX);
|
||||
|
||||
|
||||
//printf("bytes:%d blocks:%d\n", bus, us);
|
||||
}
|
||||
if (speedmode == 10)
|
||||
{
|
||||
// RTTY
|
||||
us = io_fifo_usedspace(io_pbidx);
|
||||
us = us * 20 / 48000;
|
||||
//printf("bytes:%d\n", us);
|
||||
}
|
||||
|
||||
if (us > 255 || ann_running == 1) us = 255;
|
||||
@@ -332,6 +342,8 @@ void make_FFTdata(float f)
|
||||
txpl[bidx++] = rtty_frequency >> 8; // rtty qrg by autosync
|
||||
txpl[bidx++] = rtty_frequency & 0xff;
|
||||
|
||||
txpl[bidx++] = rtty_txoff ? 0 : 1; // TX on/off
|
||||
|
||||
for (int i = 0; i < fftlen; i++)
|
||||
{
|
||||
txpl[bidx++] = fft[i] >> 8;
|
||||
|
||||
+11
-2
@@ -109,7 +109,7 @@ void showbitstring(char* title, uint8_t* data, int totallen, int anz)
|
||||
|
||||
void showbytestring(char *title, uint8_t *data, int totallen, int anz)
|
||||
{
|
||||
printf("%s. len %d: ",title, totallen);
|
||||
printf("%s. len % 4d: ",title, totallen);
|
||||
for(int i=0; i<anz; i++)
|
||||
printf("%02X ",data[i]);
|
||||
printf("\n");
|
||||
@@ -123,6 +123,14 @@ void showbytestring16(char *title, uint16_t *data, int anz)
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
void showbytestring32(char* title, uint32_t* data, int anz)
|
||||
{
|
||||
printf("%s. len %d: ", title, anz);
|
||||
for (int i = 0; i < anz; i++)
|
||||
printf("%08X ", data[i]);
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
void showbytestringf(char* title, float* data, int totallen, int anz)
|
||||
{
|
||||
printf("%s. len %d: ", title, totallen);
|
||||
@@ -134,7 +142,8 @@ void showbytestringf(char* title, float* data, int totallen, int anz)
|
||||
#ifdef _LINUX_
|
||||
void sleep_ms(int ms)
|
||||
{
|
||||
usleep(ms * 1000);
|
||||
for(int i=0; i<1000; i++)
|
||||
usleep(ms);
|
||||
}
|
||||
#endif
|
||||
#ifdef _WIN32_
|
||||
|
||||
+75
-69
@@ -37,6 +37,7 @@ void baudot_encoder(char c, uint8_t bd[2], int* pnum);
|
||||
uint8_t getBaudot(char c, int letters);
|
||||
char baudot_decoder(char c);
|
||||
void sendRttyToGUI(uint8_t b);
|
||||
void send_baudot(char c);
|
||||
|
||||
#define rtty_CENTERFREQUENCY 1500
|
||||
|
||||
@@ -75,7 +76,6 @@ fskdem dem = NULL;
|
||||
|
||||
int rtty_autosync = 0;
|
||||
|
||||
|
||||
void rtty_modifyRXfreq(int f_Hz)
|
||||
{
|
||||
//printf("set:%d Hz\n", f_Hz);
|
||||
@@ -89,7 +89,7 @@ void sendRttyToGUI(uint8_t b)
|
||||
uint8_t txpl[7];
|
||||
txpl[0] = 6; // RTTY RX Byte follows
|
||||
txpl[1] = b; // RXed byte
|
||||
txpl[2] = rtty_txoff?0:1; // TX on/off
|
||||
txpl[2] = 0;
|
||||
txpl[3] = synced;
|
||||
txpl[4] = 0; // unused
|
||||
txpl[5] = 0;
|
||||
@@ -414,7 +414,7 @@ void init_rtty()
|
||||
//printf("wegen FM test, kein Init RTTY\n");
|
||||
//return;
|
||||
|
||||
rtty_init_pipes();
|
||||
fifo_clear(FIFO_RTTYTX);
|
||||
|
||||
close_rtty();
|
||||
|
||||
@@ -517,17 +517,24 @@ void rtty_tx_function(void* param)
|
||||
}
|
||||
}
|
||||
|
||||
char csend;
|
||||
if (rtty_tx_read_fifo(&csend))
|
||||
uint8_t pcsend[200];
|
||||
int rlen = read_fifo(FIFO_RTTYTX, pcsend, 200);
|
||||
|
||||
if(rlen > 0)
|
||||
{
|
||||
baudot_encoder(csend, bd, &anz);
|
||||
//printf("read fifo: %d -> %02X\n", csend, bd[0]);
|
||||
//printf("from fifo:%d <%s>\n", rlen, pcsend);
|
||||
for (int ilen = 0; ilen < rlen; ilen++)
|
||||
{
|
||||
baudot_encoder(pcsend[ilen], bd, &anz);
|
||||
for (int il = 0; il < anz; il++)
|
||||
{
|
||||
send_baudot(bd[il]);
|
||||
//printf("send: %d -> %02X\n", pcsend[ilen], bd[il]);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
bd[0] = 0x1f; // idle
|
||||
anz = 1;
|
||||
|
||||
if (rtty_txoff == 1)
|
||||
{
|
||||
sleep_ms(10);
|
||||
@@ -535,63 +542,7 @@ void rtty_tx_function(void* param)
|
||||
}
|
||||
|
||||
if (rtty_txoff > 1) rtty_txoff--;
|
||||
}
|
||||
|
||||
//if(bd[0] != 0x1f) printf("send chars: %02X\n",bd[0]);
|
||||
|
||||
for (int i = 0; i < anz; i++)
|
||||
{
|
||||
char c = bd[i];
|
||||
// c is the baudot code, fill into final byte cs
|
||||
uint8_t cs = 0;
|
||||
cs |= ((c & 1) ? 0x40 : 0);
|
||||
cs |= ((c & 2) ? 0x20 : 0);
|
||||
cs |= ((c & 4) ? 0x10 : 0);
|
||||
cs |= ((c & 8) ? 0x08 : 0);
|
||||
cs |= ((c & 16) ? 0x04 : 0);
|
||||
cs &= ~0x80; // Start bit to 1
|
||||
cs |= 3; // 2 stop bits
|
||||
|
||||
// send cs bit per bit
|
||||
for (int bitidx = 7; bitidx >= 0; bitidx--)
|
||||
{
|
||||
if (run_rtty_threads == 0) break;
|
||||
|
||||
//measure_speed_bps(1);
|
||||
|
||||
unsigned int sym_in = (cs & (1 << bitidx)) ? 1 : 0;
|
||||
|
||||
for (int twice = 0; twice < 4; twice++)
|
||||
{
|
||||
if (bitidx == 0 && twice == 2) break; //last bit only once
|
||||
|
||||
fskmod_modulate(modi, sym_in, &(buf_tx[0]));
|
||||
|
||||
// move sample to 1,5kHz carrier
|
||||
for (int j = 0; j < k; j++)
|
||||
{
|
||||
nco_crcf_step(rtty_upnco);
|
||||
liquid_float_complex outb;
|
||||
nco_crcf_mix_up(rtty_upnco, buf_tx[j], &outb);
|
||||
|
||||
float usbf = outb.real + outb.imag;
|
||||
|
||||
// adapt to audio sample rate
|
||||
int fs;
|
||||
while (keeprunning && run_rtty_threads)
|
||||
{
|
||||
fs = io_fifo_usedspace(io_pbidx);
|
||||
//printf("%d\n", fs);
|
||||
// attention: if this number is too low, the audio write callback will not process it
|
||||
if (fs < 24000) break;
|
||||
sleep_ms(10);
|
||||
}
|
||||
|
||||
usbf *= 0.2f;
|
||||
kmaudio_playsamples(io_pbidx, &usbf, 1, pbvol);
|
||||
}
|
||||
}
|
||||
}
|
||||
send_baudot(0); // idle
|
||||
}
|
||||
}
|
||||
#ifdef _LINUX_
|
||||
@@ -600,6 +551,61 @@ void rtty_tx_function(void* param)
|
||||
#endif
|
||||
}
|
||||
|
||||
// send one baudot byte
|
||||
void send_baudot(char c)
|
||||
{
|
||||
// c is the baudot code, fill into final byte cs
|
||||
uint8_t cs = 0;
|
||||
cs |= ((c & 1) ? 0x40 : 0);
|
||||
cs |= ((c & 2) ? 0x20 : 0);
|
||||
cs |= ((c & 4) ? 0x10 : 0);
|
||||
cs |= ((c & 8) ? 0x08 : 0);
|
||||
cs |= ((c & 16) ? 0x04 : 0);
|
||||
cs &= ~0x80; // Start bit to 1
|
||||
cs |= 3; // 2 stop bits
|
||||
|
||||
// send cs bit per bit
|
||||
for (int bitidx = 7; bitidx >= 0; bitidx--)
|
||||
{
|
||||
if (run_rtty_threads == 0) break;
|
||||
|
||||
//measure_speed_bps(1);
|
||||
|
||||
unsigned int sym_in = (cs & (1 << bitidx)) ? 1 : 0;
|
||||
|
||||
for (int twice = 0; twice < 4; twice++)
|
||||
{
|
||||
if (bitidx == 0 && twice == 2) break; //last bit only once
|
||||
|
||||
fskmod_modulate(modi, sym_in, &(buf_tx[0]));
|
||||
|
||||
// move sample to 1,5kHz carrier
|
||||
for (int j = 0; j < k; j++)
|
||||
{
|
||||
nco_crcf_step(rtty_upnco);
|
||||
liquid_float_complex outb;
|
||||
nco_crcf_mix_up(rtty_upnco, buf_tx[j], &outb);
|
||||
|
||||
float usbf = outb.real + outb.imag;
|
||||
|
||||
// adapt to audio sample rate
|
||||
int fs;
|
||||
while (keeprunning && run_rtty_threads)
|
||||
{
|
||||
fs = io_fifo_usedspace(io_pbidx);
|
||||
//printf("%d\n", fs);
|
||||
// attention: if this number is too low, the audio write callback will not process it
|
||||
if (fs < 24000) break;
|
||||
sleep_ms(10);
|
||||
}
|
||||
|
||||
usbf *= 0.015f; // make RTTY signal smaller then PSK
|
||||
kmaudio_playsamples(io_pbidx, &usbf, 1, pbvol);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// RTTY RX thread
|
||||
#ifdef _LINUX_
|
||||
void* rtty_rx_function(void* param)
|
||||
@@ -662,7 +668,7 @@ void rtty_rx_function(void* param)
|
||||
nco_crcf_mix_down(rtty_dnnco, rx1500, &dc_out);
|
||||
|
||||
// sharp filter
|
||||
firfilt_crcf_push(rtty_q, dc_out); // push input sample
|
||||
firfilt_crcf_push(rtty_q, dc_out); // push input sample
|
||||
firfilt_crcf_execute(rtty_q, &(buf_rx[bridx])); // compute output
|
||||
|
||||
bridx++;
|
||||
@@ -677,7 +683,7 @@ void rtty_rx_function(void* param)
|
||||
if (db != -1)
|
||||
{
|
||||
char lt = baudot_decoder((uint8_t)db);
|
||||
//printf("rxbyte:%02X deoced:%02X\n", db, lt);
|
||||
//printf("rxbyte:%02X decoded:%02X\n", db, lt);
|
||||
if (lt > 0)
|
||||
sendRttyToGUI(lt);
|
||||
}
|
||||
|
||||
+1
-1
@@ -114,7 +114,7 @@ void threadfunction(void* param) {
|
||||
RXCFG rxcfg;
|
||||
memcpy((uint8_t *)(&rxcfg), (uint8_t *)param, sizeof(RXCFG));
|
||||
int recvlen;
|
||||
const int maxUDPpacketsize = 1024;
|
||||
const int maxUDPpacketsize = 2048;
|
||||
char rxbuf[maxUDPpacketsize];
|
||||
struct sockaddr_in fromSock;
|
||||
fromlen = sizeof(struct sockaddr_in);
|
||||
|
||||
@@ -171,7 +171,8 @@ void sendCodecToModulator(uint8_t *pdata, int len)
|
||||
{
|
||||
// we have to check if the TX fifo has enough data. In case of an underrun the Q(8A)PSK signal will be distorted
|
||||
int us = io_fifo_usedspace(io_pbidx);
|
||||
if (us < 20000)
|
||||
if (us > 20000) break; // too many data in playback fifo, do nothing
|
||||
if (us < 5000)
|
||||
{
|
||||
//printf("tx filler\n");
|
||||
// not enough samples in the TX buffer
|
||||
|
||||
Executable
+155
@@ -0,0 +1,155 @@
|
||||
/*
|
||||
* Base64 encoding/decoding (RFC1341)
|
||||
* Copyright (c) 2005-2011, Jouni Malinen <j@w1.fi>
|
||||
*
|
||||
* This software may be distributed under the terms of the BSD license.
|
||||
* See README for more details.
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include "base64.h"
|
||||
|
||||
static const unsigned char base64_table[65] =
|
||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
||||
|
||||
/**
|
||||
* base64_encode - Base64 encode
|
||||
* @src: Data to be encoded
|
||||
* @len: Length of the data to be encoded
|
||||
* @out_len: Pointer to output length variable, or %NULL if not used
|
||||
* Returns: Allocated buffer of out_len bytes of encoded data,
|
||||
* or %NULL on failure
|
||||
*
|
||||
* Caller is responsible for freeing the returned buffer. Returned buffer is
|
||||
* nul terminated to make it easier to use as a C string. The nul terminator is
|
||||
* not included in out_len.
|
||||
*/
|
||||
unsigned char * base64_encode(const unsigned char *src, size_t len,
|
||||
size_t *out_len)
|
||||
{
|
||||
unsigned char *out, *pos;
|
||||
const unsigned char *end, *in;
|
||||
size_t olen;
|
||||
int line_len;
|
||||
|
||||
olen = len * 4 / 3 + 4; /* 3-byte blocks to 4-byte */
|
||||
olen += olen / 72; /* line feeds */
|
||||
olen++; /* nul termination */
|
||||
if (olen < len)
|
||||
return NULL; /* integer overflow */
|
||||
out = (unsigned char *)malloc(olen);
|
||||
if (out == NULL)
|
||||
return NULL;
|
||||
|
||||
end = src + len;
|
||||
in = src;
|
||||
pos = out;
|
||||
line_len = 0;
|
||||
while (end - in >= 3) {
|
||||
*pos++ = base64_table[in[0] >> 2];
|
||||
*pos++ = base64_table[((in[0] & 0x03) << 4) | (in[1] >> 4)];
|
||||
*pos++ = base64_table[((in[1] & 0x0f) << 2) | (in[2] >> 6)];
|
||||
*pos++ = base64_table[in[2] & 0x3f];
|
||||
in += 3;
|
||||
line_len += 4;
|
||||
if (line_len >= 72) {
|
||||
*pos++ = '\n';
|
||||
line_len = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (end - in) {
|
||||
*pos++ = base64_table[in[0] >> 2];
|
||||
if (end - in == 1) {
|
||||
*pos++ = base64_table[(in[0] & 0x03) << 4];
|
||||
*pos++ = '=';
|
||||
} else {
|
||||
*pos++ = base64_table[((in[0] & 0x03) << 4) |
|
||||
(in[1] >> 4)];
|
||||
*pos++ = base64_table[(in[1] & 0x0f) << 2];
|
||||
}
|
||||
*pos++ = '=';
|
||||
line_len += 4;
|
||||
}
|
||||
|
||||
if (line_len)
|
||||
*pos++ = '\n';
|
||||
|
||||
*pos = '\0';
|
||||
if (out_len)
|
||||
*out_len = pos - out;
|
||||
return out;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* base64_decode - Base64 decode
|
||||
* @src: Data to be decoded
|
||||
* @len: Length of the data to be decoded
|
||||
* @out_len: Pointer to output length variable
|
||||
* Returns: Allocated buffer of out_len bytes of decoded data,
|
||||
* or %NULL on failure
|
||||
*
|
||||
* Caller is responsible for freeing the returned buffer.
|
||||
*/
|
||||
unsigned char * base64_decode(const unsigned char *src, size_t len,
|
||||
size_t *out_len)
|
||||
{
|
||||
unsigned char dtable[256], *out, *pos, block[4], tmp;
|
||||
size_t i, count, olen;
|
||||
int pad = 0;
|
||||
|
||||
memset(dtable, 0x80, 256);
|
||||
for (i = 0; i < sizeof(base64_table) - 1; i++)
|
||||
dtable[base64_table[i]] = (unsigned char) i;
|
||||
dtable['='] = 0;
|
||||
|
||||
count = 0;
|
||||
for (i = 0; i < len; i++) {
|
||||
if (dtable[src[i]] != 0x80)
|
||||
count++;
|
||||
}
|
||||
|
||||
if (count == 0 || count % 4)
|
||||
return NULL;
|
||||
|
||||
olen = count / 4 * 3;
|
||||
pos = out = (unsigned char*)malloc(olen);
|
||||
if (out == NULL)
|
||||
return NULL;
|
||||
|
||||
count = 0;
|
||||
for (i = 0; i < len; i++) {
|
||||
tmp = dtable[src[i]];
|
||||
if (tmp == 0x80)
|
||||
continue;
|
||||
|
||||
if (src[i] == '=')
|
||||
pad++;
|
||||
block[count] = tmp;
|
||||
count++;
|
||||
if (count == 4) {
|
||||
*pos++ = (block[0] << 2) | (block[1] >> 4);
|
||||
*pos++ = (block[1] << 4) | (block[2] >> 2);
|
||||
*pos++ = (block[2] << 6) | block[3];
|
||||
count = 0;
|
||||
if (pad) {
|
||||
if (pad == 1)
|
||||
pos--;
|
||||
else if (pad == 2)
|
||||
pos -= 2;
|
||||
else {
|
||||
/* Invalid padding */
|
||||
free(out);
|
||||
return NULL;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
*out_len = pos - out;
|
||||
return out;
|
||||
}
|
||||
Executable
+19
@@ -0,0 +1,19 @@
|
||||
/*
|
||||
* Base64 encoding/decoding (RFC1341)
|
||||
* Copyright (c) 2005, Jouni Malinen <j@w1.fi>
|
||||
*
|
||||
* This software may be distributed under the terms of the BSD license.
|
||||
* See README for more details.
|
||||
*/
|
||||
|
||||
#ifndef BASE64_H
|
||||
#define BASE64_H
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
unsigned char * base64_encode(const unsigned char *src, size_t len,
|
||||
size_t *out_len);
|
||||
unsigned char * base64_decode(const unsigned char *src, size_t len,
|
||||
size_t *out_len);
|
||||
|
||||
#endif /* BASE64_H */
|
||||
Executable
+73
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
Copyright (C) 2016 Davidson Francis <davidsondfgl@gmail.com>
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
#include "../hsmodem.h"
|
||||
#include "sha1.h"
|
||||
#include "base64.h"
|
||||
|
||||
/**
|
||||
* Gets the field Sec-WebSocket-Accept on response, by
|
||||
* an previously informed key.
|
||||
* @param wsKey Sec-WebSocket-Key
|
||||
* @param dest source to be stored the value.
|
||||
*/
|
||||
int getHSaccept(char *wsKey, unsigned char **dest)
|
||||
{
|
||||
SHA1Context ctx;
|
||||
char *str = (char *)malloc( sizeof(char) * (WS_KEY_LEN + WS_MS_LEN + 1) );
|
||||
unsigned char hash[SHA1HashSize];
|
||||
|
||||
strcpy(str, wsKey);
|
||||
strcat(str, MAGIC_STRING);
|
||||
|
||||
SHA1Reset(&ctx);
|
||||
SHA1Input(&ctx, (const uint8_t *)str, WS_KEYMS_LEN);
|
||||
SHA1Result(&ctx, hash);
|
||||
|
||||
*dest = base64_encode(hash, SHA1HashSize, NULL);
|
||||
*(*dest + strlen((const char *)*dest) - 1) = '\0';
|
||||
free(str);
|
||||
return (0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the complete response to accomplish a succesfully
|
||||
* handshake.
|
||||
* @param hsrequest Client request.
|
||||
* @param hsresponse Server response.
|
||||
*/
|
||||
int getHSresponse(char *hsrequest, char **hsresponse)
|
||||
{
|
||||
char *s;
|
||||
unsigned char *accept;
|
||||
|
||||
for (s = strtok(hsrequest, "\r\n"); s != NULL; s = strtok(NULL, "\r\n") )
|
||||
if (strstr(s, WS_HS_REQ) != NULL)
|
||||
break;
|
||||
|
||||
s = strtok(s, " ");
|
||||
s = strtok(NULL, " ");
|
||||
|
||||
getHSaccept(s, &accept);
|
||||
|
||||
*hsresponse = (char*)malloc(sizeof(char) * WS_HS_ACCLEN);
|
||||
strcpy(*hsresponse, WS_HS_ACCEPT);
|
||||
strcat(*hsresponse, (const char *)accept);
|
||||
strcat(*hsresponse, "\r\n\r\n");
|
||||
|
||||
free(accept);
|
||||
return (0);
|
||||
}
|
||||
Executable
+389
@@ -0,0 +1,389 @@
|
||||
/*
|
||||
* sha1.c
|
||||
*
|
||||
* Description:
|
||||
* This file implements the Secure Hashing Algorithm 1 as
|
||||
* defined in FIPS PUB 180-1 published April 17, 1995.
|
||||
*
|
||||
* The SHA-1, produces a 160-bit message digest for a given
|
||||
* data stream. It should take about 2**n steps to find a
|
||||
* message with the same digest as a given message and
|
||||
* 2**(n/2) to find any two messages with the same digest,
|
||||
* when n is the digest size in bits. Therefore, this
|
||||
* algorithm can serve as a means of providing a
|
||||
* "fingerprint" for a message.
|
||||
*
|
||||
* Portability Issues:
|
||||
* SHA-1 is defined in terms of 32-bit "words". This code
|
||||
* uses <stdint.h> (included via "sha1.h" to define 32 and 8
|
||||
* bit unsigned integer types. If your C compiler does not
|
||||
* support 32 bit unsigned integers, this code is not
|
||||
* appropriate.
|
||||
*
|
||||
* Caveats:
|
||||
* SHA-1 is designed to work with messages less than 2^64 bits
|
||||
* long. Although SHA-1 allows a message digest to be generated
|
||||
* for messages of any number of bits less than 2^64, this
|
||||
* implementation only works with messages with a length that is
|
||||
* a multiple of the size of an 8-bit character.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "sha1.h"
|
||||
|
||||
/*
|
||||
* Define the SHA1 circular left shift macro
|
||||
*/
|
||||
#define SHA1CircularShift(bits,word) \
|
||||
(((word) << (bits)) | ((word) >> (32-(bits))))
|
||||
|
||||
/* Local Function Prototyptes */
|
||||
void SHA1PadMessage(SHA1Context *);
|
||||
void SHA1ProcessMessageBlock(SHA1Context *);
|
||||
|
||||
/*
|
||||
* SHA1Reset
|
||||
*
|
||||
* Description:
|
||||
* This function will initialize the SHA1Context in preparation
|
||||
* for computing a new SHA1 message digest.
|
||||
*
|
||||
* Parameters:
|
||||
* context: [in/out]
|
||||
* The context to reset.
|
||||
*
|
||||
* Returns:
|
||||
* sha Error Code.
|
||||
*
|
||||
*/
|
||||
int SHA1Reset(SHA1Context *context)
|
||||
{
|
||||
if (!context)
|
||||
{
|
||||
return shaNull;
|
||||
}
|
||||
|
||||
context->Length_Low = 0;
|
||||
context->Length_High = 0;
|
||||
context->Message_Block_Index = 0;
|
||||
|
||||
context->Intermediate_Hash[0] = 0x67452301;
|
||||
context->Intermediate_Hash[1] = 0xEFCDAB89;
|
||||
context->Intermediate_Hash[2] = 0x98BADCFE;
|
||||
context->Intermediate_Hash[3] = 0x10325476;
|
||||
context->Intermediate_Hash[4] = 0xC3D2E1F0;
|
||||
|
||||
context->Computed = 0;
|
||||
context->Corrupted = 0;
|
||||
|
||||
return shaSuccess;
|
||||
}
|
||||
|
||||
/*
|
||||
* SHA1Result
|
||||
*
|
||||
* Description:
|
||||
* This function will return the 160-bit message digest into the
|
||||
* Message_Digest array provided by the caller.
|
||||
* NOTE: The first octet of hash is stored in the 0th element,
|
||||
* the last octet of hash in the 19th element.
|
||||
*
|
||||
* Parameters:
|
||||
* context: [in/out]
|
||||
* The context to use to calculate the SHA-1 hash.
|
||||
* Message_Digest: [out]
|
||||
* Where the digest is returned.
|
||||
*
|
||||
* Returns:
|
||||
* sha Error Code.
|
||||
*
|
||||
*/
|
||||
int SHA1Result( SHA1Context *context,
|
||||
uint8_t Message_Digest[SHA1HashSize])
|
||||
{
|
||||
int i;
|
||||
|
||||
if (!context || !Message_Digest)
|
||||
{
|
||||
return shaNull;
|
||||
}
|
||||
|
||||
if (context->Corrupted)
|
||||
{
|
||||
return context->Corrupted;
|
||||
}
|
||||
|
||||
if (!context->Computed)
|
||||
{
|
||||
SHA1PadMessage(context);
|
||||
for(i=0; i<64; ++i)
|
||||
{
|
||||
/* message may be sensitive, clear it out */
|
||||
context->Message_Block[i] = 0;
|
||||
}
|
||||
context->Length_Low = 0; /* and clear length */
|
||||
context->Length_High = 0;
|
||||
context->Computed = 1;
|
||||
|
||||
}
|
||||
|
||||
for(i = 0; i < SHA1HashSize; ++i)
|
||||
{
|
||||
Message_Digest[i] = context->Intermediate_Hash[i>>2]
|
||||
>> 8 * ( 3 - ( i & 0x03 ) );
|
||||
}
|
||||
|
||||
return shaSuccess;
|
||||
}
|
||||
|
||||
/*
|
||||
* SHA1Input
|
||||
*
|
||||
* Description:
|
||||
* This function accepts an array of octets as the next portion
|
||||
* of the message.
|
||||
*
|
||||
* Parameters:
|
||||
* context: [in/out]
|
||||
* The SHA context to update
|
||||
* message_array: [in]
|
||||
* An array of characters representing the next portion of
|
||||
* the message.
|
||||
* length: [in]
|
||||
* The length of the message in message_array
|
||||
*
|
||||
* Returns:
|
||||
* sha Error Code.
|
||||
*
|
||||
*/
|
||||
int SHA1Input( SHA1Context *context,
|
||||
const uint8_t *message_array,
|
||||
unsigned length)
|
||||
{
|
||||
if (!length)
|
||||
{
|
||||
return shaSuccess;
|
||||
}
|
||||
|
||||
if (!context || !message_array)
|
||||
{
|
||||
return shaNull;
|
||||
}
|
||||
|
||||
if (context->Computed)
|
||||
{
|
||||
context->Corrupted = shaStateError;
|
||||
|
||||
return shaStateError;
|
||||
}
|
||||
|
||||
if (context->Corrupted)
|
||||
{
|
||||
return context->Corrupted;
|
||||
}
|
||||
while(length-- && !context->Corrupted)
|
||||
{
|
||||
context->Message_Block[context->Message_Block_Index++] =
|
||||
(*message_array & 0xFF);
|
||||
|
||||
context->Length_Low += 8;
|
||||
if (context->Length_Low == 0)
|
||||
{
|
||||
context->Length_High++;
|
||||
if (context->Length_High == 0)
|
||||
{
|
||||
/* Message is too long */
|
||||
context->Corrupted = 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (context->Message_Block_Index == 64)
|
||||
{
|
||||
SHA1ProcessMessageBlock(context);
|
||||
}
|
||||
|
||||
message_array++;
|
||||
}
|
||||
|
||||
return shaSuccess;
|
||||
}
|
||||
|
||||
/*
|
||||
* SHA1ProcessMessageBlock
|
||||
*
|
||||
* Description:
|
||||
* This function will process the next 512 bits of the message
|
||||
* stored in the Message_Block array.
|
||||
*
|
||||
* Parameters:
|
||||
* None.
|
||||
*
|
||||
* Returns:
|
||||
* Nothing.
|
||||
*
|
||||
* Comments:
|
||||
|
||||
* Many of the variable names in this code, especially the
|
||||
* single character names, were used because those were the
|
||||
* names used in the publication.
|
||||
*
|
||||
*
|
||||
*/
|
||||
void SHA1ProcessMessageBlock(SHA1Context *context)
|
||||
{
|
||||
const uint32_t K[] = { /* Constants defined in SHA-1 */
|
||||
0x5A827999,
|
||||
0x6ED9EBA1,
|
||||
0x8F1BBCDC,
|
||||
0xCA62C1D6
|
||||
};
|
||||
int t; /* Loop counter */
|
||||
uint32_t temp; /* Temporary word value */
|
||||
uint32_t W[80]; /* Word sequence */
|
||||
uint32_t A, B, C, D, E; /* Word buffers */
|
||||
|
||||
/*
|
||||
* Initialize the first 16 words in the array W
|
||||
*/
|
||||
for(t = 0; t < 16; t++)
|
||||
{
|
||||
W[t] = context->Message_Block[t * 4] << 24;
|
||||
W[t] |= context->Message_Block[t * 4 + 1] << 16;
|
||||
W[t] |= context->Message_Block[t * 4 + 2] << 8;
|
||||
W[t] |= context->Message_Block[t * 4 + 3];
|
||||
}
|
||||
|
||||
for(t = 16; t < 80; t++)
|
||||
{
|
||||
W[t] = SHA1CircularShift(1,W[t-3] ^ W[t-8] ^ W[t-14] ^ W[t-16]);
|
||||
}
|
||||
|
||||
A = context->Intermediate_Hash[0];
|
||||
B = context->Intermediate_Hash[1];
|
||||
C = context->Intermediate_Hash[2];
|
||||
D = context->Intermediate_Hash[3];
|
||||
E = context->Intermediate_Hash[4];
|
||||
|
||||
for(t = 0; t < 20; t++)
|
||||
{
|
||||
temp = SHA1CircularShift(5,A) +
|
||||
((B & C) | ((~B) & D)) + E + W[t] + K[0];
|
||||
E = D;
|
||||
D = C;
|
||||
C = SHA1CircularShift(30,B);
|
||||
|
||||
B = A;
|
||||
A = temp;
|
||||
}
|
||||
|
||||
for(t = 20; t < 40; t++)
|
||||
{
|
||||
temp = SHA1CircularShift(5,A) + (B ^ C ^ D) + E + W[t] + K[1];
|
||||
E = D;
|
||||
D = C;
|
||||
C = SHA1CircularShift(30,B);
|
||||
B = A;
|
||||
A = temp;
|
||||
}
|
||||
|
||||
for(t = 40; t < 60; t++)
|
||||
{
|
||||
temp = SHA1CircularShift(5,A) +
|
||||
((B & C) | (B & D) | (C & D)) + E + W[t] + K[2];
|
||||
E = D;
|
||||
D = C;
|
||||
C = SHA1CircularShift(30,B);
|
||||
B = A;
|
||||
A = temp;
|
||||
}
|
||||
|
||||
for(t = 60; t < 80; t++)
|
||||
{
|
||||
temp = SHA1CircularShift(5,A) + (B ^ C ^ D) + E + W[t] + K[3];
|
||||
E = D;
|
||||
D = C;
|
||||
C = SHA1CircularShift(30,B);
|
||||
B = A;
|
||||
A = temp;
|
||||
}
|
||||
|
||||
context->Intermediate_Hash[0] += A;
|
||||
context->Intermediate_Hash[1] += B;
|
||||
context->Intermediate_Hash[2] += C;
|
||||
context->Intermediate_Hash[3] += D;
|
||||
context->Intermediate_Hash[4] += E;
|
||||
|
||||
context->Message_Block_Index = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* SHA1PadMessage
|
||||
*
|
||||
|
||||
* Description:
|
||||
* According to the standard, the message must be padded to an even
|
||||
* 512 bits. The first padding bit must be a '1'. The last 64
|
||||
* bits represent the length of the original message. All bits in
|
||||
* between should be 0. This function will pad the message
|
||||
* according to those rules by filling the Message_Block array
|
||||
* accordingly. It will also call the ProcessMessageBlock function
|
||||
* provided appropriately. When it returns, it can be assumed that
|
||||
* the message digest has been computed.
|
||||
*
|
||||
* Parameters:
|
||||
* context: [in/out]
|
||||
* The context to pad
|
||||
* ProcessMessageBlock: [in]
|
||||
* The appropriate SHA*ProcessMessageBlock function
|
||||
* Returns:
|
||||
* Nothing.
|
||||
*
|
||||
*/
|
||||
|
||||
void SHA1PadMessage(SHA1Context *context)
|
||||
{
|
||||
/*
|
||||
* Check to see if the current message block is too small to hold
|
||||
* the initial padding bits and length. If so, we will pad the
|
||||
* block, process it, and then continue padding into a second
|
||||
* block.
|
||||
*/
|
||||
if (context->Message_Block_Index > 55)
|
||||
{
|
||||
context->Message_Block[context->Message_Block_Index++] = 0x80;
|
||||
while(context->Message_Block_Index < 64)
|
||||
{
|
||||
context->Message_Block[context->Message_Block_Index++] = 0;
|
||||
}
|
||||
|
||||
SHA1ProcessMessageBlock(context);
|
||||
|
||||
while(context->Message_Block_Index < 56)
|
||||
{
|
||||
context->Message_Block[context->Message_Block_Index++] = 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
context->Message_Block[context->Message_Block_Index++] = 0x80;
|
||||
while(context->Message_Block_Index < 56)
|
||||
{
|
||||
|
||||
context->Message_Block[context->Message_Block_Index++] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Store the message length as the last 8 octets
|
||||
*/
|
||||
context->Message_Block[56] = context->Length_High >> 24;
|
||||
context->Message_Block[57] = context->Length_High >> 16;
|
||||
context->Message_Block[58] = context->Length_High >> 8;
|
||||
context->Message_Block[59] = context->Length_High;
|
||||
context->Message_Block[60] = context->Length_Low >> 24;
|
||||
context->Message_Block[61] = context->Length_Low >> 16;
|
||||
context->Message_Block[62] = context->Length_Low >> 8;
|
||||
context->Message_Block[63] = context->Length_Low;
|
||||
|
||||
SHA1ProcessMessageBlock(context);
|
||||
}
|
||||
Executable
+73
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
* sha1.h
|
||||
*
|
||||
* Description:
|
||||
* This is the header file for code which implements the Secure
|
||||
* Hashing Algorithm 1 as defined in FIPS PUB 180-1 published
|
||||
* April 17, 1995.
|
||||
*
|
||||
* Many of the variable names in this code, especially the
|
||||
* single character names, were used because those were the names
|
||||
* used in the publication.
|
||||
*
|
||||
* Please read the file sha1.c for more information.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _SHA1_H_
|
||||
#define _SHA1_H_
|
||||
|
||||
#include <stdint.h>
|
||||
/*
|
||||
* If you do not have the ISO standard stdint.h header file, then you
|
||||
* must typdef the following:
|
||||
* name meaning
|
||||
* uint32_t unsigned 32 bit integer
|
||||
* uint8_t unsigned 8 bit integer (i.e., unsigned char)
|
||||
* int_least16_t integer of >= 16 bits
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _SHA_enum_
|
||||
#define _SHA_enum_
|
||||
enum
|
||||
{
|
||||
shaSuccess = 0,
|
||||
shaNull, /* Null pointer parameter */
|
||||
shaInputTooLong, /* input data too long */
|
||||
shaStateError /* called Input after Result */
|
||||
};
|
||||
#endif
|
||||
#define SHA1HashSize 20
|
||||
|
||||
/*
|
||||
* This structure will hold context information for the SHA-1
|
||||
* hashing operation
|
||||
*/
|
||||
typedef struct SHA1Context
|
||||
{
|
||||
uint32_t Intermediate_Hash[SHA1HashSize/4]; /* Message Digest */
|
||||
|
||||
uint32_t Length_Low; /* Message length in bits */
|
||||
uint32_t Length_High; /* Message length in bits */
|
||||
|
||||
/* Index into message block array */
|
||||
int_least16_t Message_Block_Index;
|
||||
uint8_t Message_Block[64]; /* 512-bit message blocks */
|
||||
|
||||
int Computed; /* Is the digest computed? */
|
||||
int Corrupted; /* Is the message digest corrupted? */
|
||||
} SHA1Context;
|
||||
|
||||
/*
|
||||
* Function Prototypes
|
||||
*/
|
||||
|
||||
int SHA1Reset( SHA1Context *);
|
||||
int SHA1Input( SHA1Context *,
|
||||
const uint8_t *,
|
||||
unsigned int);
|
||||
int SHA1Result( SHA1Context *,
|
||||
uint8_t Message_Digest[SHA1HashSize]);
|
||||
|
||||
#endif
|
||||
Executable
+339
@@ -0,0 +1,339 @@
|
||||
/*
|
||||
* High Speed modem to transfer data in a 2,7kHz SSB channel
|
||||
* =========================================================
|
||||
* Author: DJ0ABR
|
||||
*
|
||||
* (c) DJ0ABR
|
||||
* www.dj0abr.de
|
||||
*
|
||||
* websocket server: based on the work by: Davidson Francis <davidsondfgl@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* websocketserver.c ... sends data to a web browser
|
||||
*
|
||||
* if a web browser is logged into this WebSocketServer then
|
||||
* we send the data to this browser.
|
||||
* Its the job of the browser to make anything with these data.
|
||||
*
|
||||
* This WebSocketServer is a multi threaded implementation and
|
||||
* opens a new thread for each client
|
||||
*
|
||||
* ! THIS implementation of a WebSocketServer DOES NOT require a Webserver (like Apache)
|
||||
* because it handles all Websocket tasks by itself !
|
||||
*
|
||||
* usage:
|
||||
* ======
|
||||
*
|
||||
* ws_init() ... call once after program start to initialize the websocket server
|
||||
*
|
||||
* ws_send(unsigned char *pdata, int len) ...
|
||||
* send pdata (max. lenght MESSAGE_LENGTH) to all connected clients.
|
||||
* this function is thread safe. It stores the data in a buffer.
|
||||
* This buffer is sent to the clients in the websocket-thread in the background.
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
#include "../hsmodem.h"
|
||||
|
||||
void init_ws_locks();
|
||||
void ws_alive();
|
||||
|
||||
#ifdef _WIN32_
|
||||
void wsproc(void* param);
|
||||
#else
|
||||
void* wsproc(void* param);
|
||||
#endif
|
||||
|
||||
#define MAXIPLEN 16
|
||||
|
||||
WS_SOCK actsock[MAX_CLIENTS];
|
||||
char clilist[MAX_CLIENTS][MAXIPLEN];
|
||||
|
||||
/*void test_websocket()
|
||||
{
|
||||
char* msg = "ABCD1234";
|
||||
|
||||
static int t = 0;
|
||||
if (++t > 100)
|
||||
{
|
||||
t = 0;
|
||||
printf("send ws: %s\n", msg);
|
||||
ws_send((unsigned char *)msg, strlen(msg));
|
||||
}
|
||||
}*/
|
||||
|
||||
// initialise the WebSocketServer
|
||||
// run the WebSocketServer in a new thread
|
||||
void ws_init()
|
||||
{
|
||||
printf("starting websocket server\n");
|
||||
init_ws_locks();
|
||||
for(int i=0; i<MAX_CLIENTS; i++)
|
||||
{
|
||||
actsock[i].socket = -1;
|
||||
actsock[i].send = 0;
|
||||
}
|
||||
|
||||
start_timer(1000, ws_alive);
|
||||
|
||||
#ifdef WIN32
|
||||
_beginthread(wsproc, 0, NULL);
|
||||
#else
|
||||
pthread_t ws_pid;
|
||||
int ret = pthread_create(&ws_pid,NULL,wsproc, NULL);
|
||||
if(ret)
|
||||
printf("websocket: process NOT started !!!\n\r");
|
||||
#endif
|
||||
}
|
||||
|
||||
// Websocket thread
|
||||
#ifdef WIN32
|
||||
void wsproc(void* param)
|
||||
{
|
||||
#else
|
||||
void* wsproc(void* param)
|
||||
{
|
||||
pthread_detach(pthread_self());
|
||||
#endif
|
||||
|
||||
printf("websocket server running\n");
|
||||
|
||||
struct ws_events evs;
|
||||
evs.onopen = &onopen;
|
||||
evs.onclose = &onclose;
|
||||
evs.onmessage = &onmessage;
|
||||
evs.onwork = &onwork;
|
||||
ws_socket(&evs, TcpDataPort_WebSocket);
|
||||
|
||||
#ifndef WIN32
|
||||
pthread_exit(NULL); // self terminate this thread
|
||||
return NULL;
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef WIN32
|
||||
CRITICAL_SECTION ws_crit_sec;
|
||||
#define WS_LOCK EnterCriticalSection(&ws_crit_sec)
|
||||
void WS_UNLOCK()
|
||||
{
|
||||
if (&ws_crit_sec != NULL)
|
||||
LeaveCriticalSection(&ws_crit_sec);
|
||||
}
|
||||
#else
|
||||
pthread_mutex_t ws_crit_sec;
|
||||
#define WS_LOCK pthread_mutex_lock(&ws_crit_sec)
|
||||
void WS_UNLOCK() { pthread_mutex_unlock(&ws_crit_sec); }
|
||||
#endif
|
||||
|
||||
void init_ws_locks()
|
||||
{
|
||||
// init pipes only once
|
||||
static int f = 1;
|
||||
if (f)
|
||||
{
|
||||
f = 0;
|
||||
#ifdef WIN32
|
||||
InitializeCriticalSection(&ws_crit_sec);
|
||||
#else
|
||||
pthread_mutex_init(&ws_crit_sec, NULL);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* save the data in an array and set a flag.
|
||||
* the transmission to the web browser is done by the threads
|
||||
* handling all logged-in web browsers.
|
||||
*
|
||||
* this function is thread safe by a LOCK
|
||||
*/
|
||||
|
||||
// insert the new message into the buffer of each active client
|
||||
void ws_send(unsigned char *pdata, int len)
|
||||
{
|
||||
WS_LOCK;
|
||||
for(int i=0; i<MAX_CLIENTS; i++)
|
||||
{
|
||||
if(actsock[i].socket != -1)
|
||||
{
|
||||
// insert data
|
||||
if(actsock[i].send == 0)
|
||||
{
|
||||
if ((unsigned int)len < sizeof(actsock[i].msg))
|
||||
memcpy(actsock[i].msg, pdata, len);
|
||||
else
|
||||
memcpy(actsock[i].msg, "ws check data size", 18);
|
||||
actsock[i].msglen = len;
|
||||
actsock[i].send = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
WS_UNLOCK();
|
||||
}
|
||||
|
||||
unsigned char *ws_build_txframe(int i, int *plength)
|
||||
{
|
||||
WS_LOCK;
|
||||
|
||||
// calculate total length
|
||||
// add 3 for each element for the first byte which is the element type followed by the length
|
||||
int geslen = 0;
|
||||
if(actsock[i].send == 1) geslen += (actsock[i].msglen + 3);
|
||||
|
||||
if(geslen < 10)
|
||||
{
|
||||
WS_UNLOCK();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// assign TX buffer
|
||||
unsigned char *txdata = (unsigned char*)malloc(geslen);
|
||||
if(txdata == NULL)
|
||||
{
|
||||
WS_UNLOCK();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// copy data into TX buffer and set the type byte
|
||||
int idx = 0;
|
||||
if(actsock[i].send == 1)
|
||||
{
|
||||
txdata[idx++] = 0; // type information
|
||||
txdata[idx++] = actsock[i].msglen >> 8;
|
||||
txdata[idx++] = actsock[i].msglen & 0xff;
|
||||
memcpy(txdata+idx,actsock[i].msg,actsock[i].msglen);
|
||||
idx += actsock[i].msglen;
|
||||
}
|
||||
|
||||
*plength = idx;
|
||||
|
||||
WS_UNLOCK();
|
||||
return txdata;
|
||||
}
|
||||
|
||||
// insert a socket into the socket-list
|
||||
void insert_socket(int fd, char *cli)
|
||||
{
|
||||
WS_LOCK;
|
||||
|
||||
for(int i=0; i<MAX_CLIENTS; i++)
|
||||
{
|
||||
if(actsock[i].socket == -1)
|
||||
{
|
||||
actsock[i].socket = fd;
|
||||
actsock[i].send = 0;
|
||||
actsock[i].alive = 10;
|
||||
strncpy(clilist[i],cli,MAXIPLEN);
|
||||
clilist[i][MAXIPLEN-1] = 0;
|
||||
printf("accepted client %d %d\n",i,fd);
|
||||
|
||||
WS_UNLOCK();
|
||||
return;
|
||||
}
|
||||
}
|
||||
WS_UNLOCK();
|
||||
printf("all sockets in use !!!\n");
|
||||
}
|
||||
|
||||
// remove a socket from the socket-list
|
||||
void remove_socket(int fd)
|
||||
{
|
||||
WS_LOCK;
|
||||
for(int i=0; i<MAX_CLIENTS; i++)
|
||||
{
|
||||
if(actsock[i].socket == fd)
|
||||
{
|
||||
printf("remove client %d %d\n", i, fd);
|
||||
/*
|
||||
!!! DO NOT close it here, this is the wrong thread !!!
|
||||
close(fd);
|
||||
*/
|
||||
actsock[i].socket = -1;
|
||||
clilist[i][0] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
WS_UNLOCK();
|
||||
}
|
||||
|
||||
int get_socket_idx(int fd)
|
||||
{
|
||||
WS_LOCK;
|
||||
for(int i=0; i<MAX_CLIENTS; i++)
|
||||
{
|
||||
if(actsock[i].socket == fd)
|
||||
{
|
||||
WS_UNLOCK();
|
||||
return i;
|
||||
}
|
||||
}
|
||||
WS_UNLOCK();
|
||||
return -1;
|
||||
}
|
||||
|
||||
// get IP corresponding to a socket
|
||||
char *getSocketIP(int fd)
|
||||
{
|
||||
WS_LOCK;
|
||||
for(int i=0; i<MAX_CLIENTS; i++)
|
||||
{
|
||||
if(actsock[i].socket == fd)
|
||||
{
|
||||
WS_UNLOCK();
|
||||
return clilist[i];
|
||||
}
|
||||
}
|
||||
WS_UNLOCK();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// called by timer every 1s
|
||||
void ws_alive()
|
||||
{
|
||||
for (int i = 0; i < MAX_CLIENTS; i++)
|
||||
{
|
||||
if (actsock[i].socket != -1 && actsock[i].alive > 0)
|
||||
{
|
||||
//printf("%d %d\n", i, actsock[i].alive);
|
||||
actsock[i].alive--;
|
||||
if (actsock[i].alive == 0)
|
||||
{
|
||||
// remove inactive client
|
||||
remove_socket(actsock[i].socket);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// remove a socket from the socket-list
|
||||
int get_alive(int fd)
|
||||
{
|
||||
int a = 0;
|
||||
WS_LOCK;
|
||||
for (int i = 0; i < MAX_CLIENTS; i++)
|
||||
{
|
||||
if (actsock[i].socket == fd)
|
||||
{
|
||||
a = actsock[i].alive;
|
||||
WS_UNLOCK();
|
||||
return a;
|
||||
}
|
||||
}
|
||||
WS_UNLOCK();
|
||||
return 0;
|
||||
}
|
||||
Executable
+87
@@ -0,0 +1,87 @@
|
||||
|
||||
#define MESSAGE_LENGTH 30000
|
||||
#define MAX_CLIENTS 20 // if changed: change also fifo.h !!!!!!!!!
|
||||
|
||||
#define WS_KEY_LEN 24
|
||||
#define WS_MS_LEN 36
|
||||
#define WS_KEYMS_LEN (WS_KEY_LEN + WS_MS_LEN)
|
||||
#define MAGIC_STRING "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
|
||||
|
||||
#define WS_HS_REQ "Sec-WebSocket-Key"
|
||||
|
||||
#define WS_HS_ACCLEN 130
|
||||
#define WS_HS_ACCEPT \
|
||||
"HTTP/1.1 101 Switching Protocols\r\n" \
|
||||
"Upgrade: websocket\r\n" \
|
||||
"Connection: Upgrade\r\n" \
|
||||
"Sec-WebSocket-Accept: " \
|
||||
|
||||
/* Frame definitions. */
|
||||
#define WS_FIN 128
|
||||
|
||||
/* Frame types. */
|
||||
#define WS_FR_OP_TXT 1
|
||||
#define WS_FR_OP_BINARY 2
|
||||
#define WS_FR_OP_CLSE 8
|
||||
|
||||
#define WS_FR_OP_UNSUPPORTED 0xF
|
||||
|
||||
extern int TcpDataPort_WebSocket;
|
||||
|
||||
// list of sockets, -1=inactive
|
||||
typedef struct {
|
||||
int socket; // socket id
|
||||
unsigned char msg[MESSAGE_LENGTH]; // message to send to the browser
|
||||
int msglen;
|
||||
int send; // 0=nothing to send, 1=send now
|
||||
struct sockaddr_in fromSock;
|
||||
int alive = 0;
|
||||
} WS_SOCK;
|
||||
|
||||
// Events
|
||||
struct ws_events
|
||||
{
|
||||
/* void onopen(int fd); */
|
||||
void (*onopen)(int);
|
||||
|
||||
/* void onclose(int fd); */
|
||||
void (*onclose)(int);
|
||||
|
||||
/* void onmessage(int fd, unsigned char *message); */
|
||||
void (*onmessage)(int, unsigned char *);
|
||||
|
||||
/* int onwork(int fd); do something short, worker function, called by the thread's main loop */
|
||||
int (*onwork)(int fd, unsigned char *cnt0, unsigned char *cnt1);
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
uint32_t command;
|
||||
uint32_t para;
|
||||
uint32_t client;
|
||||
char spara[100];
|
||||
} USERMSG;
|
||||
|
||||
int getHSaccept(char *wsKey, unsigned char **dest);
|
||||
int getHSresponse(char *hsrequest, char **hsresponse);
|
||||
|
||||
char* ws_getaddress(int fd);
|
||||
int ws_sendframe_binary(int fd, unsigned char *msg, uint64_t length);
|
||||
int ws_socket(struct ws_events *evs, int port);
|
||||
void ws_send(unsigned char* pdata, int len);
|
||||
void ws_init();
|
||||
int get_useranz();
|
||||
void onopen(int fd);
|
||||
void onclose(int fd);
|
||||
void onmessage(int fd, unsigned char *message);
|
||||
int onwork(int fd, unsigned char *cnt0, unsigned char *cnt1);
|
||||
void insert_socket(int fd, char *cli);
|
||||
void remove_socket(int fd);
|
||||
char *getSocketIP(int fd);
|
||||
unsigned char *ws_build_txframe(int i, int *plength);
|
||||
int get_socket_idx(int fd);
|
||||
int isLocal(int idx);
|
||||
void test_websocket();
|
||||
int get_alive(int fd);
|
||||
|
||||
extern WS_SOCK actsock[MAX_CLIENTS];
|
||||
extern char myIP[20];
|
||||
Executable
+434
@@ -0,0 +1,434 @@
|
||||
/*
|
||||
* High Speed modem to transfer data in a 2,7kHz SSB channel
|
||||
* =========================================================
|
||||
* Author: DJ0ABR
|
||||
*
|
||||
* (c) DJ0ABR
|
||||
* www.dj0abr.de
|
||||
|
||||
websocket server: based on the work by: Davidson Francis <davidsondfgl@gmail.com>
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
#include "../hsmodem.h"
|
||||
|
||||
/* Registered events. */
|
||||
struct ws_events events;
|
||||
|
||||
/**
|
||||
* Gets the IP address relative to a
|
||||
* file descriptor opened by the server.
|
||||
* @param fd File descriptor target.
|
||||
* @return Pointer the ip address.
|
||||
*/
|
||||
char* ws_getaddress(int fd)
|
||||
{
|
||||
struct sockaddr_in addr;
|
||||
#ifdef WIN32
|
||||
int addr_size;
|
||||
#else
|
||||
socklen_t addr_size;
|
||||
#endif
|
||||
char *client;
|
||||
|
||||
addr_size = sizeof(struct sockaddr_in);
|
||||
if ( getpeername(fd, (struct sockaddr *)&addr, &addr_size) < 0 )
|
||||
return NULL;
|
||||
|
||||
client = (char *)malloc(sizeof(char) * 20);
|
||||
if(client == NULL)
|
||||
return NULL;
|
||||
|
||||
strcpy(client, inet_ntoa(addr.sin_addr));
|
||||
return (client);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates and send a WebSocket frame
|
||||
* with some binary message.
|
||||
* @param fd Target to be send.
|
||||
* @param msg Message to be send.
|
||||
*/
|
||||
int ws_sendframe_binary(int fd, unsigned char *msg, uint64_t length)
|
||||
{
|
||||
unsigned char *response; /* Response data. */
|
||||
unsigned char frame[10]; /* Frame. */
|
||||
uint8_t idx_first_rData; /* Index data. */
|
||||
int idx_response; /* Index response. */
|
||||
int output; /* Bytes sent. */
|
||||
|
||||
/* Binary data. */
|
||||
frame[0] = (WS_FIN | WS_FR_OP_BINARY);
|
||||
|
||||
/* Split the size between octects. */
|
||||
if (length <= 125)
|
||||
{
|
||||
frame[1] = length & 0x7F;
|
||||
idx_first_rData = 2;
|
||||
}
|
||||
|
||||
/* Size between 126 and 65535 bytes. */
|
||||
else if (length >= 126 && length <= 65535)
|
||||
{
|
||||
frame[1] = 126;
|
||||
frame[2] = (length >> 8) & 255;
|
||||
frame[3] = length & 255;
|
||||
idx_first_rData = 4;
|
||||
}
|
||||
|
||||
/* More than 65535 bytes. */
|
||||
else
|
||||
{
|
||||
frame[1] = 127;
|
||||
frame[2] = (unsigned char) ((length >> 56) & 255);
|
||||
frame[3] = (unsigned char) ((length >> 48) & 255);
|
||||
frame[4] = (unsigned char) ((length >> 40) & 255);
|
||||
frame[5] = (unsigned char) ((length >> 32) & 255);
|
||||
frame[6] = (unsigned char) ((length >> 24) & 255);
|
||||
frame[7] = (unsigned char) ((length >> 16) & 255);
|
||||
frame[8] = (unsigned char) ((length >> 8) & 255);
|
||||
frame[9] = (unsigned char) (length & 255);
|
||||
idx_first_rData = 10;
|
||||
}
|
||||
|
||||
/* Add frame bytes. */
|
||||
idx_response = 0;
|
||||
response = (unsigned char *)malloc((size_t)( sizeof(unsigned char) * (idx_first_rData + length + 1)));
|
||||
if(response != NULL)
|
||||
{
|
||||
for (int i = 0; i < idx_first_rData; i++)
|
||||
{
|
||||
response[i] = frame[i];
|
||||
idx_response++;
|
||||
}
|
||||
|
||||
/* Add data bytes. */
|
||||
for (uint64_t i = 0; i < length; i++)
|
||||
{
|
||||
response[idx_response] = msg[i];
|
||||
idx_response++;
|
||||
}
|
||||
output = send(fd, (char *)response, idx_response,0);
|
||||
free(response);
|
||||
return (output);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Receives a text frame, parse and decodes it.
|
||||
* @param frame WebSocket frame to be parsed.
|
||||
* @param length Frame length.
|
||||
* @param type Frame type.
|
||||
*/
|
||||
static unsigned char* ws_receiveframe(unsigned char *frame, size_t length, int *type)
|
||||
{
|
||||
unsigned char *msg; /* Decoded message. */
|
||||
uint8_t mask; /* Payload is masked? */
|
||||
uint8_t flength; /* Raw length. */
|
||||
uint8_t idx_first_mask; /* Index masking key. */
|
||||
uint8_t idx_first_data; /* Index data. */
|
||||
size_t data_length; /* Data length. */
|
||||
uint8_t masks[4]; /* Masking key. */
|
||||
int i,j; /* Loop indexes. */
|
||||
|
||||
msg = NULL;
|
||||
|
||||
/* Checks the frame type and parse the frame. */
|
||||
if (frame[0] == (WS_FIN | WS_FR_OP_TXT) )
|
||||
{
|
||||
*type = WS_FR_OP_TXT;
|
||||
idx_first_mask = 2;
|
||||
mask = frame[1];
|
||||
flength = mask & 0x7F;
|
||||
|
||||
if (flength == 126)
|
||||
idx_first_mask = 4;
|
||||
else if (flength == 127)
|
||||
idx_first_mask = 10;
|
||||
|
||||
idx_first_data = idx_first_mask + 4;
|
||||
data_length = length - idx_first_data;
|
||||
|
||||
masks[0] = frame[idx_first_mask+0];
|
||||
masks[1] = frame[idx_first_mask+1];
|
||||
masks[2] = frame[idx_first_mask+2];
|
||||
masks[3] = frame[idx_first_mask+3];
|
||||
|
||||
msg = (unsigned char *)malloc(sizeof(unsigned char) * (data_length+1) );
|
||||
if(msg == NULL)
|
||||
return NULL;
|
||||
|
||||
for (i = idx_first_data, j = 0; i < (int)length; i++, j++)
|
||||
msg[j] = frame[i] ^ masks[j % 4];
|
||||
|
||||
msg[j] = '\0';
|
||||
}
|
||||
|
||||
/* Close frame. */
|
||||
else if (frame[0] == (WS_FIN | WS_FR_OP_CLSE) )
|
||||
*type = WS_FR_OP_CLSE;
|
||||
|
||||
/* Not supported frame yet. */
|
||||
else
|
||||
*type = frame[0] & 0x0F;
|
||||
|
||||
return msg;
|
||||
}
|
||||
|
||||
// nonblocking read with a 10ms timeout
|
||||
int readsocket(int sock, unsigned char* buf, int maxlen)
|
||||
{
|
||||
int n;
|
||||
// make the read unblocking
|
||||
// but check with select if something is in the receive buffer
|
||||
fd_set input;
|
||||
FD_ZERO(&input);
|
||||
FD_SET(sock, &input);
|
||||
struct timeval timeout;
|
||||
timeout.tv_sec = 0;
|
||||
timeout.tv_usec = 10000;
|
||||
n = select(sock + 1, &input, NULL, NULL, &timeout); // select will socket+1, blöd, aber ist so
|
||||
if (n <= 0)
|
||||
{
|
||||
return n; // 0=no data, <0=error
|
||||
}
|
||||
|
||||
if (!FD_ISSET(sock, &input))
|
||||
{
|
||||
return -1; // error
|
||||
}
|
||||
|
||||
sleep_ms(10); // wait a bit to give a message the chance to be rxed completely
|
||||
n = recv(sock, (char*)buf, maxlen, 0);
|
||||
return n;
|
||||
}
|
||||
|
||||
/**
|
||||
* Establishes to connection with the client and trigger
|
||||
* events when occurs one.
|
||||
* @param vsock Client file descriptor.
|
||||
* @note This will be run on a different thread.
|
||||
*/
|
||||
|
||||
#ifdef WIN32
|
||||
void ws_establishconnection(void* vsock)
|
||||
{
|
||||
#else
|
||||
void* ws_establishconnection(void* vsock)
|
||||
{
|
||||
pthread_detach(pthread_self());
|
||||
#endif
|
||||
|
||||
int sock;
|
||||
size_t n; /* Number of bytes sent/received. */
|
||||
unsigned char frm[MESSAGE_LENGTH]; /* Frame. */
|
||||
unsigned char *msg; /* Message. */
|
||||
char *response; /* Response frame. */
|
||||
int handshaked; /* Handshake state. */
|
||||
int type; /* Frame type. */
|
||||
unsigned char cnt0=0,cnt1=0;
|
||||
|
||||
handshaked = 0;
|
||||
sock = (int)(intptr_t)vsock;
|
||||
|
||||
while (keeprunning)
|
||||
{
|
||||
n = readsocket(sock, frm, sizeof(unsigned char) * MESSAGE_LENGTH);
|
||||
/* Receives message until get some error. */
|
||||
if (n >= 0)
|
||||
{
|
||||
if (n > 0)
|
||||
{
|
||||
// data received
|
||||
/* If not handshaked yet. */
|
||||
if (!handshaked)
|
||||
{
|
||||
getHSresponse((char*)frm, &response);
|
||||
handshaked = 1;
|
||||
/*printf("Handshaked, response: \n"
|
||||
"------------------------------------\n"
|
||||
"%s"
|
||||
"------------------------------------\n"
|
||||
,response);*/
|
||||
n = send(sock, response, strlen(response),0);
|
||||
events.onopen(sock);
|
||||
free(response);
|
||||
}
|
||||
|
||||
/* Decode/check type of frame. */
|
||||
msg = ws_receiveframe(frm, n, &type);
|
||||
|
||||
if (msg == NULL)
|
||||
continue;
|
||||
|
||||
/* Trigger events. */
|
||||
if (type == WS_FR_OP_TXT)
|
||||
events.onmessage(sock, msg);
|
||||
else if (type == WS_FR_OP_CLSE)
|
||||
{
|
||||
if (msg != NULL) free(msg);
|
||||
events.onclose(sock);
|
||||
#ifdef WIN32
|
||||
return;
|
||||
#else
|
||||
return vsock;
|
||||
#endif
|
||||
}
|
||||
else
|
||||
printf("type :%d\n", type);
|
||||
|
||||
if (msg != NULL) free(msg);
|
||||
}
|
||||
if (n == 0)
|
||||
{
|
||||
if (get_alive(sock) == 0)
|
||||
{
|
||||
events.onclose(sock);
|
||||
#ifdef WIN32
|
||||
return;
|
||||
#else
|
||||
return vsock;
|
||||
#endif
|
||||
}
|
||||
|
||||
// no data received, normal processing loop
|
||||
int ret = events.onwork(sock, &cnt0, &cnt1);
|
||||
if (ret == -1)
|
||||
{
|
||||
// other side closed the connection (write error)
|
||||
events.onclose(sock);
|
||||
#ifdef WIN32
|
||||
return;
|
||||
#else
|
||||
return vsock;
|
||||
#endif
|
||||
}
|
||||
sleep_ms(10); // do not eat up the CPU time
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#ifdef WIN32
|
||||
_close(sock);
|
||||
return;
|
||||
#else
|
||||
close(sock);
|
||||
return vsock;
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* Main loop for the server, runs in a thread
|
||||
* @param evs Events structure.
|
||||
* @param port Server port.
|
||||
*/
|
||||
int ws_socket(struct ws_events *evs, int port)
|
||||
{
|
||||
int sock; /* Current socket. */
|
||||
int new_sock; /* New opened connection. */
|
||||
struct sockaddr_in server; /* Server. */
|
||||
struct sockaddr_in client; /* Client. */
|
||||
int len; /* Length of sockaddr. */
|
||||
|
||||
if (evs == NULL || port <= 0 || port > 65535)
|
||||
{
|
||||
printf("An error has ocurred, please review your events or the desired port!\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Copy events. */
|
||||
memcpy(&events, evs, sizeof(struct ws_events));
|
||||
|
||||
#ifdef _WIN32_
|
||||
WSADATA wsaData = { 0 };
|
||||
int ires = WSAStartup(MAKEWORD(2, 2), &wsaData);
|
||||
if (ires != 0)
|
||||
printf("WSAStartup failed: %d\n", ires);
|
||||
#endif
|
||||
|
||||
/* Create socket. */
|
||||
sock = socket(AF_INET, SOCK_STREAM, 0);
|
||||
if (sock < 0) {
|
||||
printf("Could not create socket\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Reuse previous address. */
|
||||
const char val = 1;
|
||||
if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(int)) < 0)
|
||||
{
|
||||
perror("setsockopt(SO_REUSEADDR) failed");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Prepare the sockaddr_in structure. */
|
||||
server.sin_family = AF_INET;
|
||||
server.sin_addr.s_addr = INADDR_ANY;
|
||||
printf("Websocket Server: listen to port:%d\n",port);
|
||||
server.sin_port = htons(port);
|
||||
|
||||
/* Bind. */
|
||||
if( bind(sock, (struct sockaddr *)&server, sizeof(server)) < 0 )
|
||||
{
|
||||
perror("Bind failed");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Listen. */
|
||||
listen(sock, MAX_CLIENTS);
|
||||
|
||||
/* Wait for incoming connections. */
|
||||
printf("Waiting for incoming connections...\n");
|
||||
|
||||
len = sizeof(struct sockaddr_in);
|
||||
|
||||
/* Accept connections. */
|
||||
while (keeprunning)
|
||||
{
|
||||
//if (extData_active)
|
||||
{
|
||||
/* Accept. */
|
||||
#ifdef WIN32
|
||||
new_sock = accept(sock, (struct sockaddr*)&client, &len);
|
||||
printf("new socket: %d\n", new_sock);
|
||||
#else
|
||||
new_sock = accept(sock, (struct sockaddr*)&client, (socklen_t*)&len);
|
||||
#endif
|
||||
if (new_sock < 0)
|
||||
{
|
||||
perror("Error on accepting conections..");
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
#ifdef WIN32
|
||||
printf("start Thread\n");
|
||||
_beginthread(ws_establishconnection, 0, (void*)(intptr_t)new_sock);
|
||||
#else
|
||||
pthread_t client_thread;
|
||||
if (pthread_create(&client_thread, NULL, ws_establishconnection, (void*)(intptr_t)new_sock) < 0)
|
||||
perror("Could not create the client thread!");
|
||||
|
||||
pthread_detach(client_thread); // automatically release all ressources as soon as the thread is done
|
||||
#endif
|
||||
}
|
||||
/*else
|
||||
{
|
||||
sleep_ms(100);
|
||||
}*/
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
Executable
+101
@@ -0,0 +1,101 @@
|
||||
/*
|
||||
* High Speed modem to transfer data in a 2,7kHz SSB channel
|
||||
* =========================================================
|
||||
* Author: DJ0ABR
|
||||
*
|
||||
* (c) DJ0ABR
|
||||
* www.dj0abr.de
|
||||
|
||||
websocket server: based on the work by: Davidson Francis <davidsondfgl@gmail.com>
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
#include "../hsmodem.h"
|
||||
|
||||
extern int useCAT;
|
||||
int connections = 0;
|
||||
|
||||
// a new browser connected
|
||||
void onopen(int fd)
|
||||
{
|
||||
char *cli;
|
||||
cli = ws_getaddress(fd);
|
||||
if(cli != NULL)
|
||||
{
|
||||
insert_socket(fd,cli);
|
||||
printf("Connection opened, client: %d | addr: %s\n", fd, cli);
|
||||
connections++;
|
||||
printf("%d users logged in\n",connections);
|
||||
free(cli);
|
||||
}
|
||||
}
|
||||
|
||||
// a browser disconnected
|
||||
void onclose(int fd)
|
||||
{
|
||||
remove_socket(fd);
|
||||
#ifdef WIN32
|
||||
_close(fd);
|
||||
#else
|
||||
close(fd);
|
||||
#endif
|
||||
printf("Connection closed, client: %d\n", fd);
|
||||
connections--;
|
||||
printf("%d users logged in\n",connections);
|
||||
}
|
||||
|
||||
// if avaiable, send data to a browser
|
||||
int onwork(int fd, unsigned char *cnt0, unsigned char *cnt1)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
for(int i=0; i<MAX_CLIENTS; i++)
|
||||
{
|
||||
if(actsock[i].socket == fd)
|
||||
{
|
||||
// send all available data in one frame
|
||||
// (sending multiple frames resulted in data loss)
|
||||
int len;
|
||||
unsigned char *p = ws_build_txframe(i,&len);
|
||||
if(p != NULL)
|
||||
{
|
||||
ret = ws_sendframe_binary(fd, p, len);
|
||||
free(p);
|
||||
actsock[i].send = 0;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
// received a Websocket Message from a browser
|
||||
void onmessage(int fd, unsigned char *msg)
|
||||
{
|
||||
USERMSG tx_usermsg;
|
||||
|
||||
char *cli = ws_getaddress(fd);
|
||||
if(cli != NULL)
|
||||
{
|
||||
tx_usermsg.client = get_socket_idx(fd);
|
||||
tx_usermsg.command = 0;
|
||||
|
||||
//printf("Browser messages: %s, from: %s/%d\n", msg, cli, fd);
|
||||
actsock[tx_usermsg.client].alive = 20;
|
||||
|
||||
free(cli);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user