diff --git a/WinRelease/hsmodem.exe b/WinRelease/hsmodem.exe
index 430c8af..c44d35a 100755
Binary files a/WinRelease/hsmodem.exe and b/WinRelease/hsmodem.exe differ
diff --git a/WinRelease/oscardata.exe b/WinRelease/oscardata.exe
index ac7c65a..1f1953b 100755
Binary files a/WinRelease/oscardata.exe and b/WinRelease/oscardata.exe differ
diff --git a/hsmodem/Makefile b/hsmodem/Makefile
index fdac833..e7faec7 100755
--- a/hsmodem/Makefile
+++ b/hsmodem/Makefile
@@ -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
diff --git a/hsmodem/announcement.cpp b/hsmodem/announcement.cpp
index 8afdf9b..86352b7 100755
--- a/hsmodem/announcement.cpp
+++ b/hsmodem/announcement.cpp
@@ -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)
diff --git a/hsmodem/distrubution.cpp b/hsmodem/distrubution.cpp
new file mode 100755
index 0000000..953d332
--- /dev/null
+++ b/hsmodem/distrubution.cpp
@@ -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);
+ }
+}
diff --git a/hsmodem/extdata.cpp b/hsmodem/extdata.cpp
new file mode 100755
index 0000000..65e4cac
--- /dev/null
+++ b/hsmodem/extdata.cpp
@@ -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);
+}
diff --git a/hsmodem/fft.cpp b/hsmodem/fft.cpp
index 034aaae..aa82f12 100755
--- a/hsmodem/fft.cpp
+++ b/hsmodem/fft.cpp
@@ -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
diff --git a/hsmodem/fifo.cpp b/hsmodem/fifo.cpp
index 563539a..634098e 100755
--- a/hsmodem/fifo.cpp
+++ b/hsmodem/fifo.cpp
@@ -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;
+}
diff --git a/hsmodem/fifo.h b/hsmodem/fifo.h
new file mode 100755
index 0000000..809f8c6
--- /dev/null
+++ b/hsmodem/fifo.h
@@ -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);
diff --git a/hsmodem/frame_packer.cpp b/hsmodem/frame_packer.cpp
index f353d33..e521210 100755
--- a/hsmodem/frame_packer.cpp
+++ b/hsmodem/frame_packer.cpp
@@ -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)
diff --git a/hsmodem/hsmodem.cpp b/hsmodem/hsmodem.cpp
index b865296..6d501ff 100755
--- a/hsmodem/hsmodem.cpp
+++ b/hsmodem/hsmodem.cpp
@@ -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);
diff --git a/hsmodem/hsmodem.h b/hsmodem/hsmodem.h
index 2cbd553..2868e1c 100755
--- a/hsmodem/hsmodem.h
+++ b/hsmodem/hsmodem.h
@@ -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);
diff --git a/hsmodem/hsmodem.vcxproj b/hsmodem/hsmodem.vcxproj
index fee7d53..1289f49 100755
--- a/hsmodem/hsmodem.vcxproj
+++ b/hsmodem/hsmodem.vcxproj
@@ -228,6 +228,7 @@
+
@@ -239,17 +240,23 @@
+
+
+
+
+
+
@@ -271,6 +278,12 @@
+
+
+
+
+
+
diff --git a/hsmodem/hsmodem.vcxproj.filters b/hsmodem/hsmodem.vcxproj.filters
index 9a00d2a..dd1095b 100755
--- a/hsmodem/hsmodem.vcxproj.filters
+++ b/hsmodem/hsmodem.vcxproj.filters
@@ -105,6 +105,33 @@
Source Files
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
@@ -155,5 +182,17 @@
Header Files
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
\ No newline at end of file
diff --git a/hsmodem/html/websample.html b/hsmodem/html/websample.html
new file mode 100755
index 0000000..ddae0d8
--- /dev/null
+++ b/hsmodem/html/websample.html
@@ -0,0 +1,101 @@
+
+
+
+
+QO-100 Data
+
+
+
+
+
+
+HSmodem Websocket Example
+
+
+
diff --git a/hsmodem/kmtimer.cpp b/hsmodem/kmtimer.cpp
new file mode 100755
index 0000000..abed129
--- /dev/null
+++ b/hsmodem/kmtimer.cpp
@@ -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
+
+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
+*
+* 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
diff --git a/hsmodem/libkmaudio/libkmaudio.h b/hsmodem/libkmaudio/libkmaudio.h
index 333f6c8..48e7559 100755
--- a/hsmodem/libkmaudio/libkmaudio.h
+++ b/hsmodem/libkmaudio/libkmaudio.h
@@ -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();
diff --git a/hsmodem/libkmaudio/libkmaudio_capture.cpp b/hsmodem/libkmaudio/libkmaudio_capture.cpp
index be26da7..eeccd23 100755
--- a/hsmodem/libkmaudio/libkmaudio_capture.cpp
+++ b/hsmodem/libkmaudio/libkmaudio_capture.cpp
@@ -150,7 +150,7 @@ int recordCallback( const void* inputBuffer,
(void)timeInfo;
(void)statusFlags;
- if(keeprunning == 1)
+ if(keepcallbacksrunning == 1)
return paContinue;
return paComplete;
diff --git a/hsmodem/libkmaudio/libkmaudio_capture_linux.cpp b/hsmodem/libkmaudio/libkmaudio_capture_linux.cpp
index b586ddd..829c494 100755
--- a/hsmodem/libkmaudio/libkmaudio_capture_linux.cpp
+++ b/hsmodem/libkmaudio/libkmaudio_capture_linux.cpp
@@ -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)))
diff --git a/hsmodem/libkmaudio/libkmaudio_fifo.cpp b/hsmodem/libkmaudio/libkmaudio_fifo.cpp
index aeaba6f..b38fe3c 100755
--- a/hsmodem/libkmaudio/libkmaudio_fifo.cpp
+++ b/hsmodem/libkmaudio/libkmaudio_fifo.cpp
@@ -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;
-}
-
diff --git a/hsmodem/libkmaudio/libkmaudio_init.cpp b/hsmodem/libkmaudio/libkmaudio_init.cpp
index 0e3ec8d..23e3281 100755
--- a/hsmodem/libkmaudio/libkmaudio_init.cpp
+++ b/hsmodem/libkmaudio/libkmaudio_init.cpp
@@ -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
diff --git a/hsmodem/libkmaudio/libkmaudio_interface.cpp b/hsmodem/libkmaudio/libkmaudio_interface.cpp
index 44116a3..31d4f00 100755
--- a/hsmodem/libkmaudio/libkmaudio_interface.cpp
+++ b/hsmodem/libkmaudio/libkmaudio_interface.cpp
@@ -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
diff --git a/hsmodem/libkmaudio/libkmaudio_playback.cpp b/hsmodem/libkmaudio/libkmaudio_playback.cpp
index 3d24d5f..3a40390 100755
--- a/hsmodem/libkmaudio/libkmaudio_playback.cpp
+++ b/hsmodem/libkmaudio/libkmaudio_playback.cpp
@@ -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;
diff --git a/hsmodem/libkmaudio/libkmaudio_playback_linux.cpp b/hsmodem/libkmaudio/libkmaudio_playback_linux.cpp
index e5af6ae..294ded2 100755
--- a/hsmodem/libkmaudio/libkmaudio_playback_linux.cpp
+++ b/hsmodem/libkmaudio/libkmaudio_playback_linux.cpp
@@ -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;
}
diff --git a/hsmodem/libkmaudio/portaudio.h b/hsmodem/libkmaudio/portaudio.h
index 8a94aaf..e68858b 100755
--- a/hsmodem/libkmaudio/portaudio.h
+++ b/hsmodem/libkmaudio/portaudio.h
@@ -124,9 +124,9 @@ typedef enum PaErrorCode
paNoError = 0,
paNotInitialized = -10000,
- paUnanticipatedHostError,
- paInvalidChannelCount,
- paInvalidSampleRate,
+ paUnanticipatedHostError, // -9999
+ paInvalidChannelCount, // -9998
+ paInvalidSampleRate, // -9997
paInvalidDevice,
paInvalidFlag,
paSampleFormatNotSupported,
diff --git a/hsmodem/liquid_if.cpp b/hsmodem/liquid_if.cpp
index 064254c..4f75376 100755
--- a/hsmodem/liquid_if.cpp
+++ b/hsmodem/liquid_if.cpp
@@ -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;
diff --git a/hsmodem/main_helper.cpp b/hsmodem/main_helper.cpp
index 666bebc..1cb8429 100755
--- a/hsmodem/main_helper.cpp
+++ b/hsmodem/main_helper.cpp
@@ -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 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);
}
diff --git a/hsmodem/udp.cpp b/hsmodem/udp.cpp
index a3a987b..301b001 100755
--- a/hsmodem/udp.cpp
+++ b/hsmodem/udp.cpp
@@ -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);
diff --git a/hsmodem/voiceprocessor.cpp b/hsmodem/voiceprocessor.cpp
index 5f6d844..98ecc6b 100755
--- a/hsmodem/voiceprocessor.cpp
+++ b/hsmodem/voiceprocessor.cpp
@@ -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
diff --git a/hsmodem/websocket/base64.cpp b/hsmodem/websocket/base64.cpp
new file mode 100755
index 0000000..0acda24
--- /dev/null
+++ b/hsmodem/websocket/base64.cpp
@@ -0,0 +1,155 @@
+/*
+ * Base64 encoding/decoding (RFC1341)
+ * Copyright (c) 2005-2011, Jouni Malinen
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include
+#include
+#include
+#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;
+}
diff --git a/hsmodem/websocket/base64.h b/hsmodem/websocket/base64.h
new file mode 100755
index 0000000..5ea7f13
--- /dev/null
+++ b/hsmodem/websocket/base64.h
@@ -0,0 +1,19 @@
+/*
+ * Base64 encoding/decoding (RFC1341)
+ * Copyright (c) 2005, Jouni Malinen
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef BASE64_H
+#define BASE64_H
+
+#include
+
+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 */
diff --git a/hsmodem/websocket/handshake.cpp b/hsmodem/websocket/handshake.cpp
new file mode 100755
index 0000000..d9587e5
--- /dev/null
+++ b/hsmodem/websocket/handshake.cpp
@@ -0,0 +1,73 @@
+/*
+Copyright (C) 2016 Davidson Francis
+
+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
+*/
+#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);
+}
diff --git a/hsmodem/websocket/sha1.cpp b/hsmodem/websocket/sha1.cpp
new file mode 100755
index 0000000..f0c95dc
--- /dev/null
+++ b/hsmodem/websocket/sha1.cpp
@@ -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 (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);
+}
diff --git a/hsmodem/websocket/sha1.h b/hsmodem/websocket/sha1.h
new file mode 100755
index 0000000..eb51ed5
--- /dev/null
+++ b/hsmodem/websocket/sha1.h
@@ -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
+/*
+ * 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
diff --git a/hsmodem/websocket/websocketserver.cpp b/hsmodem/websocket/websocketserver.cpp
new file mode 100755
index 0000000..618769e
--- /dev/null
+++ b/hsmodem/websocket/websocketserver.cpp
@@ -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
+*
+* 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> 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 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;
+}
diff --git a/hsmodem/websocket/websocketserver.h b/hsmodem/websocket/websocketserver.h
new file mode 100755
index 0000000..d1433f7
--- /dev/null
+++ b/hsmodem/websocket/websocketserver.h
@@ -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];
diff --git a/hsmodem/websocket/ws.cpp b/hsmodem/websocket/ws.cpp
new file mode 100755
index 0000000..41621dd
--- /dev/null
+++ b/hsmodem/websocket/ws.cpp
@@ -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
+
+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
+*/
+#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;
+}
diff --git a/hsmodem/websocket/ws_callbacks.cpp b/hsmodem/websocket/ws_callbacks.cpp
new file mode 100755
index 0000000..21fce53
--- /dev/null
+++ b/hsmodem/websocket/ws_callbacks.cpp
@@ -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
+
+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
+*/
+
+#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 3) return;
Byte[] txarr = new byte[statics.PayloadLen];
@@ -153,7 +154,9 @@ namespace oscardata
if (txlen <= statics.PayloadLen)
{
// we just need to send one frame
- txudp(txdata, txtype, statics.SingleFrame);
+ for (int i = 0; i < txdata.Length; i++)
+ txarr[i] = txdata[i];
+ txudp(txarr, txtype, statics.SingleFrame);
setSending(false); // transmission complete
}
else
diff --git a/oscardata/oscardata/Form1.Designer.cs b/oscardata/oscardata/Form1.Designer.cs
index 0886a46..8244003 100755
--- a/oscardata/oscardata/Form1.Designer.cs
+++ b/oscardata/oscardata/Form1.Designer.cs
@@ -39,9 +39,7 @@
this.RXstatus = new System.Windows.Forms.ToolStripStatusLabel();
this.toolStrip_spacer = new System.Windows.Forms.ToolStripStatusLabel();
this.ts_userinfo = new System.Windows.Forms.ToolStripStatusLabel();
- this.panel_constel = new System.Windows.Forms.Panel();
this.timer_qpsk = new System.Windows.Forms.Timer(this.components);
- this.panel_txspectrum = new System.Windows.Forms.Panel();
this.tabPage_ber = new System.Windows.Forms.TabPage();
this.button6 = new System.Windows.Forms.Button();
this.bt_allf = new System.Windows.Forms.Button();
@@ -51,13 +49,13 @@
this.imageList1 = new System.Windows.Forms.ImageList(this.components);
this.button_startBERtest = new System.Windows.Forms.Button();
this.tabPage_image = new System.Windows.Forms.TabPage();
- this.groupBox1 = new System.Windows.Forms.Panel();
+ this.groupBox1 = new oscardata.DoubleBufferedPanel();
+ this.cb_picres = new System.Windows.Forms.ComboBox();
this.cb_loop = new System.Windows.Forms.CheckBox();
this.bt_rximages = new System.Windows.Forms.Button();
this.button_loadimage = new System.Windows.Forms.Button();
this.comboBox_quality = new System.Windows.Forms.ComboBox();
this.label2 = new System.Windows.Forms.Label();
- this.checkBox_big = new System.Windows.Forms.CheckBox();
this.button_cancelimg = new System.Windows.Forms.Button();
this.button_sendimage = new System.Windows.Forms.Button();
this.label_rximage = new System.Windows.Forms.Label();
@@ -66,6 +64,11 @@
this.pictureBox_tximage = new System.Windows.Forms.PictureBox();
this.tabControl1 = new System.Windows.Forms.TabControl();
this.tabPage_file = new System.Windows.Forms.TabPage();
+ this.groupBox8 = new System.Windows.Forms.GroupBox();
+ this.cb_file_pause = new System.Windows.Forms.ComboBox();
+ this.label13 = new System.Windows.Forms.Label();
+ this.cb_file_loop = new System.Windows.Forms.CheckBox();
+ this.bt_open_html = new System.Windows.Forms.Button();
this.pictureBox1 = new System.Windows.Forms.PictureBox();
this.button2 = new System.Windows.Forms.Button();
this.bt_openrxfile = new System.Windows.Forms.Button();
@@ -105,7 +108,7 @@
this.label3 = new System.Windows.Forms.Label();
this.tb_rtty_TX = new System.Windows.Forms.TextBox();
this.tb_rtty_RX = new System.Windows.Forms.TextBox();
- this.panel1 = new System.Windows.Forms.Panel();
+ this.panel1 = new oscardata.DoubleBufferedPanel();
this.textBox6 = new System.Windows.Forms.TextBox();
this.cb_rx_autosync = new System.Windows.Forms.CheckBox();
this.rb_rtty_real = new System.Windows.Forms.RadioButton();
@@ -143,9 +146,9 @@
this.label_cfgpath = new System.Windows.Forms.Label();
this.label_cfgpath_tit = new System.Windows.Forms.Label();
this.groupBox4 = new System.Windows.Forms.GroupBox();
- this.label13 = new System.Windows.Forms.Label();
+ this.cb_extIF = new System.Windows.Forms.CheckBox();
+ this.textBox7 = new System.Windows.Forms.TextBox();
this.label12 = new System.Windows.Forms.Label();
- this.cb_safemode = new System.Windows.Forms.ComboBox();
this.cb_language = new System.Windows.Forms.ComboBox();
this.cb_autostart = new System.Windows.Forms.CheckBox();
this.bt_shutdown = new System.Windows.Forms.Button();
@@ -191,18 +194,20 @@
this.cb_stampcall = new System.Windows.Forms.CheckBox();
this.tabPage_about = new System.Windows.Forms.TabPage();
this.richTextBox1 = new System.Windows.Forms.RichTextBox();
- this.cb_speed = new System.Windows.Forms.ComboBox();
- this.label_speed = new System.Windows.Forms.Label();
this.timer_searchmodem = new System.Windows.Forms.Timer(this.components);
- this.label_fifo = new System.Windows.Forms.Label();
- this.label_capfifo = new System.Windows.Forms.Label();
- this.lb_rxsignal = new System.Windows.Forms.Label();
- this.lb_rxsync = new System.Windows.Forms.Label();
- this.pn1 = new System.Windows.Forms.Panel();
+ this.pn1 = new oscardata.DoubleBufferedPanel();
this.progressBar_fifo = new oscardata.KmProgressBar();
+ this.label_capfifo = new System.Windows.Forms.Label();
+ this.label_speed = new System.Windows.Forms.Label();
this.pb_rxsignal = new System.Windows.Forms.PictureBox();
+ this.lb_rxsync = new System.Windows.Forms.Label();
this.progressBar_capfifo = new oscardata.KmProgressBar();
+ this.cb_speed = new System.Windows.Forms.ComboBox();
+ this.lb_rxsignal = new System.Windows.Forms.Label();
this.pb_rxsync = new System.Windows.Forms.PictureBox();
+ this.label_fifo = new System.Windows.Forms.Label();
+ this.panel_txspectrum = new oscardata.DoubleBufferedPanel();
+ this.panel_constel = new oscardata.DoubleBufferedPanel();
this.statusStrip1.SuspendLayout();
this.tabPage_ber.SuspendLayout();
this.tabPage_image.SuspendLayout();
@@ -211,6 +216,7 @@
((System.ComponentModel.ISupportInitialize)(this.pictureBox_tximage)).BeginInit();
this.tabControl1.SuspendLayout();
this.tabPage_file.SuspendLayout();
+ this.groupBox8.SuspendLayout();
((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).BeginInit();
this.tabPage_audio.SuspendLayout();
this.groupBox7.SuspendLayout();
@@ -273,62 +279,46 @@
//
// toolStripStatusLabel
//
+ this.toolStripStatusLabel.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
this.toolStripStatusLabel.Name = "toolStripStatusLabel";
- this.toolStripStatusLabel.Size = new System.Drawing.Size(39, 17);
+ this.toolStripStatusLabel.Size = new System.Drawing.Size(37, 17);
this.toolStripStatusLabel.Text = "Status";
//
// ts_ip
//
+ this.ts_ip.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
this.ts_ip.ForeColor = System.Drawing.Color.Red;
this.ts_ip.Name = "ts_ip";
- this.ts_ip.Size = new System.Drawing.Size(12, 17);
+ this.ts_ip.Size = new System.Drawing.Size(13, 17);
this.ts_ip.Text = "?";
//
// RXstatus
//
+ this.RXstatus.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
this.RXstatus.Name = "RXstatus";
- this.RXstatus.Size = new System.Drawing.Size(58, 17);
+ this.RXstatus.Size = new System.Drawing.Size(55, 17);
this.RXstatus.Text = "RX-Status";
//
// toolStrip_spacer
//
this.toolStrip_spacer.Name = "toolStrip_spacer";
- this.toolStrip_spacer.Size = new System.Drawing.Size(1156, 17);
+ this.toolStrip_spacer.Size = new System.Drawing.Size(1160, 17);
this.toolStrip_spacer.Spring = true;
//
// ts_userinfo
//
- this.ts_userinfo.Font = new System.Drawing.Font("Segoe UI", 9F, System.Drawing.FontStyle.Bold);
+ this.ts_userinfo.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F);
this.ts_userinfo.ForeColor = System.Drawing.Color.Blue;
this.ts_userinfo.Name = "ts_userinfo";
this.ts_userinfo.Size = new System.Drawing.Size(16, 17);
this.ts_userinfo.Text = "...";
//
- // panel_constel
- //
- this.panel_constel.BackColor = System.Drawing.Color.AliceBlue;
- this.panel_constel.Location = new System.Drawing.Point(11, 590);
- this.panel_constel.Name = "panel_constel";
- this.panel_constel.Size = new System.Drawing.Size(75, 75);
- this.panel_constel.TabIndex = 5;
- this.panel_constel.Paint += new System.Windows.Forms.PaintEventHandler(this.panel_constel_Paint);
- //
// timer_qpsk
//
this.timer_qpsk.Enabled = true;
this.timer_qpsk.Interval = 200;
this.timer_qpsk.Tick += new System.EventHandler(this.timer_qpsk_Tick);
//
- // panel_txspectrum
- //
- this.panel_txspectrum.BackColor = System.Drawing.SystemColors.ControlLight;
- this.panel_txspectrum.Location = new System.Drawing.Point(92, 590);
- this.panel_txspectrum.Name = "panel_txspectrum";
- this.panel_txspectrum.Size = new System.Drawing.Size(442, 76);
- this.panel_txspectrum.TabIndex = 6;
- this.panel_txspectrum.Paint += new System.Windows.Forms.PaintEventHandler(this.panel_txspectrum_Paint);
- this.panel_txspectrum.DoubleClick += new System.EventHandler(this.panel_txspectrum_DoubleClick);
- //
// tabPage_ber
//
this.tabPage_ber.BackColor = System.Drawing.Color.Transparent;
@@ -459,12 +449,12 @@
//
// groupBox1
//
+ this.groupBox1.Controls.Add(this.cb_picres);
this.groupBox1.Controls.Add(this.cb_loop);
this.groupBox1.Controls.Add(this.bt_rximages);
this.groupBox1.Controls.Add(this.button_loadimage);
this.groupBox1.Controls.Add(this.comboBox_quality);
this.groupBox1.Controls.Add(this.label2);
- this.groupBox1.Controls.Add(this.checkBox_big);
this.groupBox1.Controls.Add(this.button_cancelimg);
this.groupBox1.Controls.Add(this.button_sendimage);
this.groupBox1.Location = new System.Drawing.Point(3, 508);
@@ -472,6 +462,23 @@
this.groupBox1.Size = new System.Drawing.Size(1277, 42);
this.groupBox1.TabIndex = 12;
//
+ // cb_picres
+ //
+ this.cb_picres.FormattingEnabled = true;
+ this.cb_picres.Items.AddRange(new object[] {
+ "160x120",
+ "240x180",
+ "320x240",
+ "400x300",
+ "480x360",
+ "560x420",
+ "640x480"});
+ this.cb_picres.Location = new System.Drawing.Point(174, 7);
+ this.cb_picres.Name = "cb_picres";
+ this.cb_picres.Size = new System.Drawing.Size(85, 21);
+ this.cb_picres.TabIndex = 12;
+ this.cb_picres.Text = "640x480";
+ //
// cb_loop
//
this.cb_loop.AutoSize = true;
@@ -519,7 +526,7 @@
"very high, 4min"});
this.comboBox_quality.Location = new System.Drawing.Point(57, 7);
this.comboBox_quality.Name = "comboBox_quality";
- this.comboBox_quality.Size = new System.Drawing.Size(109, 21);
+ this.comboBox_quality.Size = new System.Drawing.Size(111, 21);
this.comboBox_quality.TabIndex = 6;
this.comboBox_quality.Text = "medium, 1min";
//
@@ -532,19 +539,6 @@
this.label2.TabIndex = 7;
this.label2.Text = "Quality:";
//
- // checkBox_big
- //
- this.checkBox_big.AutoSize = true;
- this.checkBox_big.Checked = true;
- this.checkBox_big.CheckState = System.Windows.Forms.CheckState.Checked;
- this.checkBox_big.Location = new System.Drawing.Point(187, 9);
- this.checkBox_big.Name = "checkBox_big";
- this.checkBox_big.Size = new System.Drawing.Size(79, 17);
- this.checkBox_big.TabIndex = 8;
- this.checkBox_big.Text = "640(320)px";
- this.checkBox_big.UseVisualStyleBackColor = true;
- this.checkBox_big.CheckedChanged += new System.EventHandler(this.checkBox_small_CheckedChanged);
- //
// button_cancelimg
//
this.button_cancelimg.ForeColor = System.Drawing.Color.Red;
@@ -601,7 +595,6 @@
this.pictureBox_rximage.Location = new System.Drawing.Point(642, 27);
this.pictureBox_rximage.Name = "pictureBox_rximage";
this.pictureBox_rximage.Size = new System.Drawing.Size(640, 480);
- this.pictureBox_rximage.SizeMode = System.Windows.Forms.PictureBoxSizeMode.AutoSize;
this.pictureBox_rximage.TabIndex = 3;
this.pictureBox_rximage.TabStop = false;
//
@@ -634,6 +627,8 @@
// tabPage_file
//
this.tabPage_file.BackColor = System.Drawing.Color.Transparent;
+ this.tabPage_file.Controls.Add(this.groupBox8);
+ this.tabPage_file.Controls.Add(this.bt_open_html);
this.tabPage_file.Controls.Add(this.pictureBox1);
this.tabPage_file.Controls.Add(this.button2);
this.tabPage_file.Controls.Add(this.bt_openrxfile);
@@ -652,11 +647,76 @@
this.tabPage_file.TabIndex = 2;
this.tabPage_file.Text = "File";
//
+ // groupBox8
+ //
+ this.groupBox8.Controls.Add(this.cb_file_pause);
+ this.groupBox8.Controls.Add(this.label13);
+ this.groupBox8.Controls.Add(this.cb_file_loop);
+ this.groupBox8.Location = new System.Drawing.Point(17, 192);
+ this.groupBox8.Name = "groupBox8";
+ this.groupBox8.Size = new System.Drawing.Size(137, 103);
+ this.groupBox8.TabIndex = 16;
+ this.groupBox8.TabStop = false;
+ this.groupBox8.Text = "Send all files in folder";
+ //
+ // cb_file_pause
+ //
+ this.cb_file_pause.FormattingEnabled = true;
+ this.cb_file_pause.Items.AddRange(new object[] {
+ "0 s",
+ "10s",
+ "20s",
+ "30s",
+ "40s",
+ "50s",
+ "1min",
+ "2min",
+ "5min",
+ "10min"});
+ this.cb_file_pause.Location = new System.Drawing.Point(14, 71);
+ this.cb_file_pause.Name = "cb_file_pause";
+ this.cb_file_pause.Size = new System.Drawing.Size(92, 21);
+ this.cb_file_pause.TabIndex = 16;
+ this.cb_file_pause.Text = "1min";
+ //
+ // label13
+ //
+ this.label13.AutoSize = true;
+ this.label13.Location = new System.Drawing.Point(11, 53);
+ this.label13.Name = "label13";
+ this.label13.Size = new System.Drawing.Size(105, 13);
+ this.label13.TabIndex = 15;
+ this.label13.Text = "Pause between files:";
+ //
+ // cb_file_loop
+ //
+ this.cb_file_loop.AutoSize = true;
+ this.cb_file_loop.Location = new System.Drawing.Point(16, 25);
+ this.cb_file_loop.Name = "cb_file_loop";
+ this.cb_file_loop.Size = new System.Drawing.Size(65, 17);
+ this.cb_file_loop.TabIndex = 14;
+ this.cb_file_loop.Text = "ON / off";
+ this.cb_file_loop.UseVisualStyleBackColor = true;
+ //
+ // bt_open_html
+ //
+ this.bt_open_html.ImageAlign = System.Drawing.ContentAlignment.MiddleLeft;
+ this.bt_open_html.ImageIndex = 2;
+ this.bt_open_html.ImageList = this.imageList1;
+ this.bt_open_html.Location = new System.Drawing.Point(17, 337);
+ this.bt_open_html.Name = "bt_open_html";
+ this.bt_open_html.Size = new System.Drawing.Size(137, 51);
+ this.bt_open_html.TabIndex = 15;
+ this.bt_open_html.Text = "Open received \r\nHTML file";
+ this.bt_open_html.UseVisualStyleBackColor = true;
+ this.bt_open_html.Visible = false;
+ this.bt_open_html.Click += new System.EventHandler(this.bt_open_html_Click);
+ //
// pictureBox1
//
this.pictureBox1.BackgroundImage = ((System.Drawing.Image)(resources.GetObject("pictureBox1.BackgroundImage")));
this.pictureBox1.BackgroundImageLayout = System.Windows.Forms.ImageLayout.Center;
- this.pictureBox1.Location = new System.Drawing.Point(17, 371);
+ this.pictureBox1.Location = new System.Drawing.Point(17, 394);
this.pictureBox1.Name = "pictureBox1";
this.pictureBox1.Size = new System.Drawing.Size(127, 134);
this.pictureBox1.TabIndex = 13;
@@ -668,7 +728,7 @@
this.button2.ImageAlign = System.Drawing.ContentAlignment.MiddleLeft;
this.button2.ImageIndex = 8;
this.button2.ImageList = this.imageList1;
- this.button2.Location = new System.Drawing.Point(17, 218);
+ this.button2.Location = new System.Drawing.Point(17, 163);
this.button2.Name = "button2";
this.button2.Size = new System.Drawing.Size(137, 23);
this.button2.TabIndex = 12;
@@ -681,7 +741,7 @@
this.bt_openrxfile.ImageAlign = System.Drawing.ContentAlignment.MiddleLeft;
this.bt_openrxfile.ImageIndex = 5;
this.bt_openrxfile.ImageList = this.imageList1;
- this.bt_openrxfile.Location = new System.Drawing.Point(17, 306);
+ this.bt_openrxfile.Location = new System.Drawing.Point(17, 301);
this.bt_openrxfile.Name = "bt_openrxfile";
this.bt_openrxfile.Size = new System.Drawing.Size(137, 30);
this.bt_openrxfile.TabIndex = 11;
@@ -734,7 +794,7 @@
this.bt_file_send.ImageAlign = System.Drawing.ContentAlignment.MiddleLeft;
this.bt_file_send.ImageIndex = 10;
this.bt_file_send.ImageList = this.imageList1;
- this.bt_file_send.Location = new System.Drawing.Point(17, 157);
+ this.bt_file_send.Location = new System.Drawing.Point(17, 124);
this.bt_file_send.Name = "bt_file_send";
this.bt_file_send.Size = new System.Drawing.Size(137, 30);
this.bt_file_send.TabIndex = 3;
@@ -1576,9 +1636,9 @@
//
// groupBox4
//
- this.groupBox4.Controls.Add(this.label13);
+ this.groupBox4.Controls.Add(this.cb_extIF);
+ this.groupBox4.Controls.Add(this.textBox7);
this.groupBox4.Controls.Add(this.label12);
- this.groupBox4.Controls.Add(this.cb_safemode);
this.groupBox4.Controls.Add(this.cb_language);
this.groupBox4.Controls.Add(this.cb_autostart);
this.groupBox4.Controls.Add(this.bt_shutdown);
@@ -1592,44 +1652,46 @@
this.groupBox4.TabStop = false;
this.groupBox4.Text = "Maintenance";
//
- // label13
+ // cb_extIF
//
- this.label13.AutoSize = true;
- this.label13.Location = new System.Drawing.Point(240, 78);
- this.label13.Name = "label13";
- this.label13.Size = new System.Drawing.Size(70, 13);
- this.label13.TabIndex = 27;
- this.label13.Text = "data security:";
+ this.cb_extIF.AutoSize = true;
+ this.cb_extIF.Location = new System.Drawing.Point(213, 22);
+ this.cb_extIF.Name = "cb_extIF";
+ this.cb_extIF.Size = new System.Drawing.Size(135, 17);
+ this.cb_extIF.TabIndex = 27;
+ this.cb_extIF.Text = "External Data Interface";
+ this.cb_extIF.UseVisualStyleBackColor = true;
+ this.cb_extIF.CheckedChanged += new System.EventHandler(this.cb_extIF_CheckedChanged);
+ //
+ // textBox7
+ //
+ this.textBox7.BackColor = System.Drawing.SystemColors.Control;
+ this.textBox7.BorderStyle = System.Windows.Forms.BorderStyle.None;
+ this.textBox7.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
+ this.textBox7.ForeColor = System.Drawing.Color.Black;
+ this.textBox7.Location = new System.Drawing.Point(211, 45);
+ this.textBox7.Multiline = true;
+ this.textBox7.Name = "textBox7";
+ this.textBox7.Size = new System.Drawing.Size(151, 39);
+ this.textBox7.TabIndex = 28;
+ this.textBox7.Text = "for advanced users only, see developers manual";
//
// label12
//
this.label12.AutoSize = true;
- this.label12.Location = new System.Drawing.Point(221, 23);
+ this.label12.Location = new System.Drawing.Point(428, 23);
this.label12.Name = "label12";
this.label12.Size = new System.Drawing.Size(103, 13);
this.label12.TabIndex = 26;
this.label12.Text = "Language/Sprache:";
//
- // cb_safemode
- //
- this.cb_safemode.FormattingEnabled = true;
- this.cb_safemode.Items.AddRange(new object[] {
- "off (fast)",
- "medium",
- "high (slow)"});
- this.cb_safemode.Location = new System.Drawing.Point(326, 75);
- this.cb_safemode.Name = "cb_safemode";
- this.cb_safemode.Size = new System.Drawing.Size(110, 21);
- this.cb_safemode.TabIndex = 25;
- this.cb_safemode.Text = "off";
- //
// cb_language
//
this.cb_language.FormattingEnabled = true;
this.cb_language.Items.AddRange(new object[] {
"English",
"German/Deutsch"});
- this.cb_language.Location = new System.Drawing.Point(326, 19);
+ this.cb_language.Location = new System.Drawing.Point(428, 42);
this.cb_language.Name = "cb_language";
this.cb_language.Size = new System.Drawing.Size(110, 21);
this.cb_language.TabIndex = 24;
@@ -1643,15 +1705,15 @@
this.cb_autostart.CheckState = System.Windows.Forms.CheckState.Checked;
this.cb_autostart.Location = new System.Drawing.Point(17, 23);
this.cb_autostart.Name = "cb_autostart";
- this.cb_autostart.Size = new System.Drawing.Size(156, 17);
+ this.cb_autostart.Size = new System.Drawing.Size(155, 17);
this.cb_autostart.TabIndex = 4;
- this.cb_autostart.Text = "AUTO start/stop HSmodem";
+ this.cb_autostart.Text = "LOCAL/(remote) HSmodem";
this.cb_autostart.UseVisualStyleBackColor = true;
this.cb_autostart.CheckedChanged += new System.EventHandler(this.cb_autostart_CheckedChanged);
//
// bt_shutdown
//
- this.bt_shutdown.Location = new System.Drawing.Point(491, 19);
+ this.bt_shutdown.Location = new System.Drawing.Point(564, 18);
this.bt_shutdown.Name = "bt_shutdown";
this.bt_shutdown.Size = new System.Drawing.Size(155, 23);
this.bt_shutdown.TabIndex = 4;
@@ -1665,7 +1727,7 @@
this.tb_shutdown.BorderStyle = System.Windows.Forms.BorderStyle.None;
this.tb_shutdown.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.tb_shutdown.ForeColor = System.Drawing.Color.Red;
- this.tb_shutdown.Location = new System.Drawing.Point(491, 48);
+ this.tb_shutdown.Location = new System.Drawing.Point(564, 47);
this.tb_shutdown.Multiline = true;
this.tb_shutdown.Name = "tb_shutdown";
this.tb_shutdown.Size = new System.Drawing.Size(155, 50);
@@ -1674,7 +1736,7 @@
//
// bt_resetmodem
//
- this.bt_resetmodem.Location = new System.Drawing.Point(529, 92);
+ this.bt_resetmodem.Location = new System.Drawing.Point(602, 91);
this.bt_resetmodem.Name = "bt_resetmodem";
this.bt_resetmodem.Size = new System.Drawing.Size(117, 23);
this.bt_resetmodem.TabIndex = 6;
@@ -1692,7 +1754,7 @@
this.textBox3.Location = new System.Drawing.Point(15, 46);
this.textBox3.Multiline = true;
this.textBox3.Name = "textBox3";
- this.textBox3.Size = new System.Drawing.Size(151, 50);
+ this.textBox3.Size = new System.Drawing.Size(151, 39);
this.textBox3.TabIndex = 12;
this.textBox3.Text = "only uncheck if modem runs on a separate PC";
//
@@ -2142,78 +2204,11 @@
this.richTextBox1.TabIndex = 0;
this.richTextBox1.Text = resources.GetString("richTextBox1.Text");
//
- // cb_speed
- //
- this.cb_speed.FormattingEnabled = true;
- this.cb_speed.Items.AddRange(new object[] {
- "1200 BPSK BW: 1300 Hz",
- "2400 BPSK BW: 2500 Hz",
- "3000 QPSK BW: 1700 Hz ",
- "4000 QPSK BW: 2400 Hz ",
- "4410 QPSK BW: 2500 Hz (QO-100 Standard)",
- "4800 QPSK BW: 2700 Hz",
- "5500 8APSK BW: 2300 Hz",
- "6000 8APSK BW: 2500 Hz (QO-100 Transceiver)",
- "6600 8APSK BW: 2600 Hz",
- "7200 8APSK BW: 2700 Hz (QO-100 SDR)",
- "45.45 Baud RTTY"});
- this.cb_speed.Location = new System.Drawing.Point(122, 2);
- this.cb_speed.Name = "cb_speed";
- this.cb_speed.Size = new System.Drawing.Size(304, 21);
- this.cb_speed.TabIndex = 11;
- this.cb_speed.Text = "4410 QPSK BW: 2500 Hz (QO-100)";
- this.cb_speed.SelectedIndexChanged += new System.EventHandler(this.comboBox1_SelectedIndexChanged);
- //
- // label_speed
- //
- this.label_speed.AutoSize = true;
- this.label_speed.Location = new System.Drawing.Point(31, 5);
- this.label_speed.Name = "label_speed";
- this.label_speed.Size = new System.Drawing.Size(71, 13);
- this.label_speed.TabIndex = 12;
- this.label_speed.Text = "Speed [bit/s]:";
- //
// timer_searchmodem
//
this.timer_searchmodem.Interval = 1000;
this.timer_searchmodem.Tick += new System.EventHandler(this.timer_searchmodem_Tick);
//
- // label_fifo
- //
- this.label_fifo.AutoSize = true;
- this.label_fifo.Location = new System.Drawing.Point(31, 31);
- this.label_fifo.Name = "label_fifo";
- this.label_fifo.Size = new System.Drawing.Size(55, 13);
- this.label_fifo.TabIndex = 14;
- this.label_fifo.Text = "TX Buffer:";
- //
- // label_capfifo
- //
- this.label_capfifo.AutoSize = true;
- this.label_capfifo.Location = new System.Drawing.Point(31, 54);
- this.label_capfifo.Name = "label_capfifo";
- this.label_capfifo.Size = new System.Drawing.Size(56, 13);
- this.label_capfifo.TabIndex = 16;
- this.label_capfifo.Text = "RX Buffer:";
- //
- // lb_rxsignal
- //
- this.lb_rxsignal.AutoSize = true;
- this.lb_rxsignal.Location = new System.Drawing.Point(448, 54);
- this.lb_rxsignal.Name = "lb_rxsignal";
- this.lb_rxsignal.Size = new System.Drawing.Size(57, 13);
- this.lb_rxsignal.TabIndex = 18;
- this.lb_rxsignal.Text = "RX Signal:";
- //
- // lb_rxsync
- //
- this.lb_rxsync.AutoSize = true;
- this.lb_rxsync.Location = new System.Drawing.Point(448, 7);
- this.lb_rxsync.Name = "lb_rxsync";
- this.lb_rxsync.Size = new System.Drawing.Size(52, 13);
- this.lb_rxsync.TabIndex = 20;
- this.lb_rxsync.Text = "RX Sync:";
- //
// pn1
//
this.pn1.Controls.Add(this.progressBar_fifo);
@@ -2242,6 +2237,24 @@
this.progressBar_fifo.Style = System.Windows.Forms.ProgressBarStyle.Continuous;
this.progressBar_fifo.TabIndex = 13;
//
+ // label_capfifo
+ //
+ this.label_capfifo.AutoSize = true;
+ this.label_capfifo.Location = new System.Drawing.Point(31, 54);
+ this.label_capfifo.Name = "label_capfifo";
+ this.label_capfifo.Size = new System.Drawing.Size(56, 13);
+ this.label_capfifo.TabIndex = 16;
+ this.label_capfifo.Text = "RX Buffer:";
+ //
+ // label_speed
+ //
+ this.label_speed.AutoSize = true;
+ this.label_speed.Location = new System.Drawing.Point(31, 5);
+ this.label_speed.Name = "label_speed";
+ this.label_speed.Size = new System.Drawing.Size(71, 13);
+ this.label_speed.TabIndex = 12;
+ this.label_speed.Text = "Speed [bit/s]:";
+ //
// pb_rxsignal
//
this.pb_rxsignal.BackgroundImage = ((System.Drawing.Image)(resources.GetObject("pb_rxsignal.BackgroundImage")));
@@ -2252,6 +2265,15 @@
this.pb_rxsignal.TabIndex = 17;
this.pb_rxsignal.TabStop = false;
//
+ // lb_rxsync
+ //
+ this.lb_rxsync.AutoSize = true;
+ this.lb_rxsync.Location = new System.Drawing.Point(448, 7);
+ this.lb_rxsync.Name = "lb_rxsync";
+ this.lb_rxsync.Size = new System.Drawing.Size(52, 13);
+ this.lb_rxsync.TabIndex = 20;
+ this.lb_rxsync.Text = "RX Sync:";
+ //
// progressBar_capfifo
//
this.progressBar_capfifo.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(255)))), ((int)(((byte)(192)))), ((int)(((byte)(128)))));
@@ -2262,6 +2284,37 @@
this.progressBar_capfifo.Style = System.Windows.Forms.ProgressBarStyle.Continuous;
this.progressBar_capfifo.TabIndex = 15;
//
+ // cb_speed
+ //
+ this.cb_speed.FormattingEnabled = true;
+ this.cb_speed.Items.AddRange(new object[] {
+ "1200 BPSK BW: 1300 Hz",
+ "2400 BPSK BW: 2500 Hz",
+ "3000 QPSK BW: 1700 Hz ",
+ "4000 QPSK BW: 2400 Hz ",
+ "4410 QPSK BW: 2500 Hz (QO-100 Standard)",
+ "4800 QPSK BW: 2700 Hz",
+ "5500 8APSK BW: 2300 Hz",
+ "6000 8APSK BW: 2500 Hz (QO-100 Transceiver)",
+ "6600 8APSK BW: 2600 Hz",
+ "7200 8APSK BW: 2700 Hz (QO-100 SDR)",
+ "45.45 Baud RTTY"});
+ this.cb_speed.Location = new System.Drawing.Point(122, 2);
+ this.cb_speed.Name = "cb_speed";
+ this.cb_speed.Size = new System.Drawing.Size(304, 21);
+ this.cb_speed.TabIndex = 11;
+ this.cb_speed.Text = "4410 QPSK BW: 2500 Hz (QO-100)";
+ this.cb_speed.SelectedIndexChanged += new System.EventHandler(this.comboBox1_SelectedIndexChanged);
+ //
+ // lb_rxsignal
+ //
+ this.lb_rxsignal.AutoSize = true;
+ this.lb_rxsignal.Location = new System.Drawing.Point(448, 54);
+ this.lb_rxsignal.Name = "lb_rxsignal";
+ this.lb_rxsignal.Size = new System.Drawing.Size(57, 13);
+ this.lb_rxsignal.TabIndex = 18;
+ this.lb_rxsignal.Text = "RX Signal:";
+ //
// pb_rxsync
//
this.pb_rxsync.BackgroundImage = ((System.Drawing.Image)(resources.GetObject("pb_rxsync.BackgroundImage")));
@@ -2272,6 +2325,34 @@
this.pb_rxsync.TabIndex = 19;
this.pb_rxsync.TabStop = false;
//
+ // label_fifo
+ //
+ this.label_fifo.AutoSize = true;
+ this.label_fifo.Location = new System.Drawing.Point(31, 31);
+ this.label_fifo.Name = "label_fifo";
+ this.label_fifo.Size = new System.Drawing.Size(55, 13);
+ this.label_fifo.TabIndex = 14;
+ this.label_fifo.Text = "TX Buffer:";
+ //
+ // panel_txspectrum
+ //
+ this.panel_txspectrum.BackColor = System.Drawing.SystemColors.ControlLight;
+ this.panel_txspectrum.Location = new System.Drawing.Point(92, 590);
+ this.panel_txspectrum.Name = "panel_txspectrum";
+ this.panel_txspectrum.Size = new System.Drawing.Size(442, 76);
+ this.panel_txspectrum.TabIndex = 6;
+ this.panel_txspectrum.Paint += new System.Windows.Forms.PaintEventHandler(this.panel_txspectrum_Paint);
+ this.panel_txspectrum.DoubleClick += new System.EventHandler(this.panel_txspectrum_DoubleClick);
+ //
+ // panel_constel
+ //
+ this.panel_constel.BackColor = System.Drawing.Color.AliceBlue;
+ this.panel_constel.Location = new System.Drawing.Point(11, 590);
+ this.panel_constel.Name = "panel_constel";
+ this.panel_constel.Size = new System.Drawing.Size(75, 75);
+ this.panel_constel.TabIndex = 5;
+ this.panel_constel.Paint += new System.Windows.Forms.PaintEventHandler(this.panel_constel_Paint);
+ //
// Form1
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
@@ -2286,7 +2367,7 @@
this.ForeColor = System.Drawing.SystemColors.ControlText;
this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon")));
this.Name = "Form1";
- this.Text = "AMSAT-DL Multimedia HS Modem V0.72 by DJ0ABR";
+ this.Text = "AMSAT-DL Multimedia HS Modem V0.84 by DJ0ABR";
this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.Form1_FormClosing);
this.statusStrip1.ResumeLayout(false);
this.statusStrip1.PerformLayout();
@@ -2301,6 +2382,8 @@
this.tabControl1.ResumeLayout(false);
this.tabPage_file.ResumeLayout(false);
this.tabPage_file.PerformLayout();
+ this.groupBox8.ResumeLayout(false);
+ this.groupBox8.PerformLayout();
((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).EndInit();
this.tabPage_audio.ResumeLayout(false);
this.groupBox7.ResumeLayout(false);
@@ -2349,9 +2432,9 @@
private System.Windows.Forms.Timer timer_udprx;
private System.Windows.Forms.StatusStrip statusStrip1;
private System.Windows.Forms.ToolStripStatusLabel toolStripStatusLabel;
- private System.Windows.Forms.Panel panel_constel;
+ private DoubleBufferedPanel panel_constel;
private System.Windows.Forms.Timer timer_qpsk;
- private System.Windows.Forms.Panel panel_txspectrum;
+ private DoubleBufferedPanel panel_txspectrum;
private System.Windows.Forms.TabPage tabPage_ber;
private System.Windows.Forms.Button button_stopBERtest;
private System.Windows.Forms.Button button_startBERtest;
@@ -2361,7 +2444,6 @@
private System.Windows.Forms.Button button_loadimage;
private System.Windows.Forms.Button button_cancelimg;
private System.Windows.Forms.Button button_sendimage;
- private System.Windows.Forms.CheckBox checkBox_big;
private System.Windows.Forms.Label label2;
private System.Windows.Forms.Label label_rximage;
private System.Windows.Forms.Label label_tximage;
@@ -2369,7 +2451,7 @@
private System.Windows.Forms.PictureBox pictureBox_tximage;
private System.Windows.Forms.TabControl tabControl1;
private System.Windows.Forms.ToolStripStatusLabel ts_ip;
- private System.Windows.Forms.Panel groupBox1;
+ private DoubleBufferedPanel groupBox1;
private System.Windows.Forms.TabPage tabPage_file;
private System.Windows.Forms.RichTextBox rtb_TXfile;
private System.Windows.Forms.Button bt_file_send;
@@ -2449,9 +2531,7 @@
private System.Windows.Forms.Label lb_rxsync;
private System.Windows.Forms.PictureBox pb_rxsync;
private System.Windows.Forms.ComboBox cb_language;
- private System.Windows.Forms.Label label13;
private System.Windows.Forms.Label label12;
- private System.Windows.Forms.ComboBox cb_safemode;
private KmProgressBar vu_cap;
private KmProgressBar vu_pb;
private System.Windows.Forms.ToolStripStatusLabel toolStrip_Type;
@@ -2501,8 +2581,8 @@
private System.Windows.Forms.Button bt_rxfont;
private System.Windows.Forms.Button button4;
private System.Windows.Forms.Button bt_rtty_RY;
- private System.Windows.Forms.Panel pn1;
- private System.Windows.Forms.Panel panel1;
+ private DoubleBufferedPanel pn1;
+ private DoubleBufferedPanel panel1;
private System.Windows.Forms.Label label4;
private System.Windows.Forms.Label label3;
private System.Windows.Forms.Button button5;
@@ -2518,6 +2598,14 @@
private System.Windows.Forms.Button button6;
private System.Windows.Forms.ToolStripStatusLabel toolStrip_spacer;
private System.Windows.Forms.ToolStripStatusLabel ts_userinfo;
+ private System.Windows.Forms.CheckBox cb_file_loop;
+ private System.Windows.Forms.ComboBox cb_picres;
+ private System.Windows.Forms.CheckBox cb_extIF;
+ private System.Windows.Forms.TextBox textBox7;
+ private System.Windows.Forms.Button bt_open_html;
+ private System.Windows.Forms.GroupBox groupBox8;
+ private System.Windows.Forms.ComboBox cb_file_pause;
+ private System.Windows.Forms.Label label13;
}
}
diff --git a/oscardata/oscardata/Form1.cs b/oscardata/oscardata/Form1.cs
index 83c4bf8..63b0fe2 100755
--- a/oscardata/oscardata/Form1.cs
+++ b/oscardata/oscardata/Form1.cs
@@ -31,6 +31,8 @@ using System.Diagnostics;
using System.Threading;
using oscardata.Properties;
using System.Reflection;
+using System.Globalization;
+using System.Text.RegularExpressions;
namespace oscardata
{
@@ -50,7 +52,7 @@ namespace oscardata
int recPhase = 0;
const int Rtty_deftext_anz = 20;
String[] Rtty_deftext = new string[Rtty_deftext_anz];
-
+ DateTime dtfile = DateTime.UtcNow;
public Form1()
{
@@ -167,10 +169,10 @@ namespace oscardata
button_sendimage.Enabled = false;
}
- if (TXfoldername == "" || lastFullName == "")
+ /*if (TXfoldername == "" || lastFullName == "")
cb_loop.Enabled = false;
else
- cb_loop.Enabled = true;
+ cb_loop.Enabled = true;*/
ShowTXstatus();
@@ -183,7 +185,7 @@ namespace oscardata
if (ArraySend.getSending() == false)
{
// transmission is finished, wait until data in TXfifo have been sent
- if (statics.PBfifousage < 2)
+ if (statics.PBfifousage < 4)
{
// start sending a new picture
startNextImage();
@@ -192,6 +194,29 @@ namespace oscardata
}
}
+ if (txcommand == statics.AsciiFile || txcommand == statics.HTMLFile || txcommand == statics.BinaryFile)
+ {
+ // if "loop" is selected send the next image in folder
+ if (cb_file_loop.Checked)
+ {
+ // check pause time
+ if(retransmitPause() == false)
+ {
+ // check if we are ready with any transmission
+ if (ArraySend.getSending() == false)
+ {
+ // transmission is finished, wait until data in TXfifo have been sent
+ if (statics.PBfifousage < 4)
+ {
+ // start sending a new picture
+ startNextFile();
+ dtfile = DateTime.UtcNow;
+ }
+ }
+ }
+ }
+ }
+
if (ts_ip.Text.Contains("?") || ts_ip.Text.Contains("1.2.3.4") || old_tsip != statics.ModemIP)
{
if (statics.ModemIP == "1.2.3.4")
@@ -251,7 +276,7 @@ namespace oscardata
if (setPBvolume >= 0)
{
Byte[] txdata = new byte[2];
- txdata[0] = (Byte)statics.SetPBvolume;
+ txdata[0] = statics.SetPBvolume;
txdata[1] = (Byte)setPBvolume;
Udp.UdpSendCtrl(txdata);
setPBvolume = -1;
@@ -260,7 +285,7 @@ namespace oscardata
if (setCAPvolume != -1)
{
Byte[] txdata = new byte[2];
- txdata[0] = (Byte)statics.SetCAPvolume;
+ txdata[0] = statics.SetCAPvolume;
txdata[1] = (Byte)setCAPvolume;
Udp.UdpSendCtrl(txdata);
setCAPvolume = -1;
@@ -269,7 +294,7 @@ namespace oscardata
if (setLSvolume >= 0)
{
Byte[] txdata = new byte[2];
- txdata[0] = (Byte)statics.SetLSvolume;
+ txdata[0] = statics.SetLSvolume;
txdata[1] = (Byte)setLSvolume;
Udp.UdpSendCtrl(txdata);
setLSvolume = -1;
@@ -278,7 +303,7 @@ namespace oscardata
if (setMICvolume != -1)
{
Byte[] txdata = new byte[2];
- txdata[0] = (Byte)statics.SetMICvolume;
+ txdata[0] = statics.SetMICvolume;
txdata[1] = (Byte)setMICvolume;
Udp.UdpSendCtrl(txdata);
setMICvolume = -1;
@@ -315,6 +340,35 @@ namespace oscardata
}
}
+ bool sendInProgress = false;
+ bool retransmitPause()
+ {
+ // check if file send is in progress
+ bool fstat = statics.PBfifousage > 1;
+ if (fstat == true)
+ {
+ // file send in progress
+ sendInProgress = true;
+ return true;
+ }
+
+ if(fstat == false && sendInProgress == true)
+ {
+ // just finished sending
+ sendInProgress = false;
+ dtfile = DateTime.UtcNow;
+ return true;
+ }
+
+ // not sending, wait until pause time elapsed
+ String s = cb_file_pause.Text;
+ int dur = int.Parse(Regex.Match(s, @"\d+").Value, NumberFormatInfo.InvariantInfo);
+ if (s.Contains("min")) dur *= 60;
+ TimeSpan ts = DateTime.UtcNow - dtfile;
+ if (ts.TotalSeconds >= dur) return false;
+ return true;
+ }
+
// correct entries in the Audio Device Comboboxes if devices have changed
void findDevice(ComboBox cb)
{
@@ -362,7 +416,7 @@ namespace oscardata
{
// tell hsmodem to terminate itself
Byte[] txdata = new byte[1];
- txdata[0] = (Byte)statics.terminate;
+ txdata[0] = statics.terminate;
Udp.UdpSendCtrl(txdata);
Thread.Sleep(250);
@@ -456,8 +510,15 @@ namespace oscardata
{
// reception complete, show stored file
Console.WriteLine("load " + recfile.filename);
- pictureBox_rximage.BackgroundImage = Image.FromFile(recfile.filename);
- pictureBox_rximage.Invalidate();
+ try
+ {
+ pictureBox_rximage.BackgroundImage = Image.FromFile(recfile.filename);
+ pictureBox_rximage.Invalidate();
+ }
+ catch
+ {
+ // invalid picture
+ }
}
if (recfile.pbmp != null)
{
@@ -474,7 +535,7 @@ namespace oscardata
if (rxtype == statics.AsciiFile)
{
int fret = recfile.receive(rxd);
- if (fret == 1)
+ if (fret >= 1)
{
// ASCII file received, show in window
String serg = File.ReadAllText(recfile.filename);
@@ -487,13 +548,15 @@ namespace oscardata
if (rxtype == statics.HTMLFile)
{
int fret = recfile.receive(rxd);
- if (fret == 1)
+ if (fret >= 1)
{
// HTML file received, show in window
String serg = File.ReadAllText(recfile.filename);
printText(rtb_RXfile, serg);
- // and show in browser
- OpenUrl(recfile.filename);
+ // save filename
+ statics.lastRXedHTMLfile = recfile.filename;
+ // show the HTML-Show Button
+ bt_open_html.Visible = true;
}
if (fret == -5)
printBadBlocks();
@@ -502,7 +565,7 @@ namespace oscardata
if (rxtype == statics.BinaryFile)
{
int fret = recfile.receive(rxd);
- if (fret == 1)
+ if (fret >= 1)
{
// Binary file received, show statistics in window
try
@@ -661,7 +724,6 @@ namespace oscardata
if (ba != null)
{
int rtty_val = ba[0];
- rtty_txon = ba[1];
rtty_sync = ba[2];
if (rtty_val != 0)
{
@@ -683,7 +745,7 @@ namespace oscardata
}
}
}
- if (rtty_txon == 1)
+ if (statics.rtty_txon == 1)
{
bt_rtty_tx.BackColor = Color.Red;
tb_rtty_TX.Enabled = true;
@@ -702,6 +764,7 @@ namespace oscardata
RTTYmousepos.X = e.X;
RTTYmousepos.Y = e.Y;
int cidx = tb_rtty_RX.GetCharIndexFromPosition(RTTYmousepos);
+ Console.WriteLine("cidx: " + cidx.ToString());
// get the word under this position
// text after pos
@@ -853,6 +916,7 @@ namespace oscardata
long TXRealFileSize = 0;
String TXfoldername = "";
String lastFullName = "";
+ String TXfullfilename = "";
// prepare an image file for transmission
void prepareImage(String fullfn)
@@ -860,10 +924,10 @@ namespace oscardata
if (statics.checkImage(fullfn) == false) return;
// all images are converted to jpg, make the new filename
+ TXfullfilename = fullfn;
TXfoldername = statics.purePath(fullfn);
TXRealFilename = statics.pureFilename(fullfn);
TXRealFilename = statics.AddReplaceFileExtension(TXRealFilename,"jpg");
- lastFullName = fullfn;
// random filename for picturebox control (picturebox cannot reload image from actual filename)
try {
@@ -888,11 +952,13 @@ namespace oscardata
if (cb_stampcall.Checked == false) cs = "";
String inf = tb_info.Text;
if (cb_stampinfo.Checked == false) inf = "";
- if (!checkBox_big.Checked)
+ Size picsize = getResolution();
+ if(picsize.Width != 0 && picsize.Height != 0)
{
- img = ih.ResizeImage(img, 320, 240, cs, inf);
- // set quality by reducing the file size and save under default name
- ih.SaveJpgAtFileSize(img, TXimagefilename, max_size / 2);
+ int reduc = 640 / picsize.Width;
+ if (reduc < 1) reduc = 1;
+ img = ih.ResizeImage(img, picsize.Width, picsize.Height, cs, inf);
+ ih.SaveJpgAtFileSize(img, TXimagefilename, max_size/ reduc);
}
else
{
@@ -901,12 +967,31 @@ namespace oscardata
ih.SaveJpgAtFileSize(img, TXimagefilename, max_size);
}
- //pictureBox_tximage.Load(TXimagefilename); this does not work under ARM mono
+ //pictureBox_tximage.Load(TXimagefilename); // this does not work under ARM mono
pictureBox_tximage.BackgroundImage = Image.FromFile(TXimagefilename);
TXRealFileSize = statics.GetFileSize(TXimagefilename);
ShowTXstatus();
txcommand = statics.Image;
}
+
+ Size getResolution()
+ {
+ Size sz = new Size(0,0);
+
+ String r = cb_picres.Text;
+ String[] ra = r.Split(new char[] { 'x' });
+ try
+ {
+ int w = Convert.ToInt32(ra[0]);
+ int h = Convert.ToInt32(ra[1]);
+ sz.Width = w;
+ sz.Height = h;
+ }
+ catch { }
+
+
+ return sz;
+ }
void ShowTXstatus()
{
@@ -952,8 +1037,51 @@ namespace oscardata
button_sendimage_Click(null, null);
}
+ // in loop mode only: send the next picture in current image folder
+ void startNextFile()
+ {
+ if (lastFullName == "") return;
+
+ // read all file from folder
+ String folder = Path.GetDirectoryName(lastFullName);
+ String[] files = Directory.GetFiles(folder);
+ Array.Sort(files);
+ int i;
+ bool found = false;
+ for (i = 0; i < files.Length; i++)
+ {
+ // look for the last transmitted file
+ if (files[i] == lastFullName)
+ {
+ // choose the next file
+ if (++i == files.Length) i = 0;
+ // check if the file is valid
+ try
+ {
+ long sz = statics.GetFileSize(files[i]);
+ if (sz < 1000000)
+ {
+ // do not try to send a file > 1MB
+ found = true;
+ break;
+ }
+ }
+ catch
+ {
+ lastFullName = files[i];
+ }
+ }
+ }
+ if (!found) return;
+
+ // files[i] is the filename to be sent
+ bt_prepareAndSendFile(files[i], Path.GetFileName(files[i]), statics.BinaryFile);
+ bt_file_send_Click(null, null);
+ }
+
private void button_loadimage_Click(object sender, EventArgs e)
{
+ lastFullName = "";
OpenFileDialog open = new OpenFileDialog();
open.Filter = statics.langstr[13];
if (open.ShowDialog() == DialogResult.OK)
@@ -964,6 +1092,7 @@ namespace oscardata
private void button_sendimage_Click(object sender, EventArgs e)
{
+ lastFullName = TXfullfilename;
txcommand = statics.Image;
rxbytecounter = 0;
pictureBox_rximage.Image = null;
@@ -1207,9 +1336,22 @@ namespace oscardata
//Console.WriteLine("BCip: " + ip);
}
}*/
+
+ // if hsmodem is local, use local IP instead of broadcast
+ if (cb_autostart.Checked)
+ {
+ String[] myips = statics.getOwnIPs();
+ if (myips.Length >= 1)
+ {
+ statics.MyIP = myips[0];
+ }
+ return statics.MyIP;
+ }
+
+
return ip;
}
-
+
/*
* search for the modem IP:
* send a search message via UDP to port UdpBCport
@@ -1226,69 +1368,84 @@ namespace oscardata
* 4 ... DV loudspeaker volume
* 5 ... DV mic volume
* 6 ... safe mode
- * 7..9 ... unused
- * 10 .. 109 ... PB device name
- * 110 .. 209 ... CAP device name
+ * 7 ... send introduction voice record
+ * 8 ... rtty autosync on/off
+ * 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
*
*/
private void search_modem()
{
- Byte safemode = 0; //number of frame repeats
- if (cb_safemode.Text.Contains("medium")) safemode = 2;
- else if (cb_safemode.Text.Contains("high")) safemode = 4;
+ Byte[] txb = new byte[270];
+ int idx = 0;
+ txb[idx++] = 0x3c; // ID of this message
+ txb[idx++] = (Byte)tb_PBvol.Value;
+ txb[idx++] = (Byte)tb_CAPvol.Value;
+ txb[idx++] = (Byte)cb_announcement.Items.IndexOf(cb_announcement.Text);
+ txb[idx++] = (Byte)tb_loadspeaker.Value;
+ txb[idx++] = (Byte)tb_mic.Value;
+ txb[idx++] = 0; // unused
+ txb[idx++] = (Byte)(cb_sendIntro.Checked?1:0);
+ txb[idx++] = (Byte)(cb_rx_autosync.Checked ? 1 : 0);
+ if(cb_speed.DroppedDown == false)
+ txb[idx++] = (Byte)cb_speed.SelectedIndex;
+ else
+ txb[idx++] = (Byte)255; // invalid, hsmodem does not use this value
+ txb[idx++] = (Byte)(cb_extIF.Checked ? 1 : 0);
- Byte[] txb = new byte[260];
- txb[0] = 0x3c; // ID of this message
- txb[1] = (Byte)tb_PBvol.Value;
- txb[2] = (Byte)tb_CAPvol.Value;
- txb[3] = (Byte)cb_announcement.Items.IndexOf(cb_announcement.Text);
- txb[4] = (Byte)tb_loadspeaker.Value;
- txb[5] = (Byte)tb_mic.Value;
- txb[6] = safemode;
- txb[7] = (Byte)(cb_sendIntro.Checked?1:0);
- txb[8] = (Byte)(cb_rx_autosync.Checked ? 1 : 0);
- txb[9] = (Byte)0; // unused
Byte[] bpb = statics.StringToByteArrayUtf8(cb_audioPB.Text);
Byte[] bcap = statics.StringToByteArrayUtf8(cb_audioCAP.Text);
//Byte[] bpb = statics.StringToByteArray(cb_audioPB.Text);
//Byte[] bcap = statics.StringToByteArray(cb_audioCAP.Text);
- // 200 Bytes (from 10..209) name of selected sound device
+ // 200 Bytes (from 20..219) name of selected sound device
for (int i=0; i<100; i++)
{
if (i >= bpb.Length)
- txb[i + 10] = 0;
+ txb[i + 20] = 0;
else
- txb[i+10] = bpb[i];
+ txb[i + 20] = bpb[i];
if (i >= bcap.Length)
- txb[i + 110] = 0;
+ txb[i + 120] = 0;
else
- txb[i + 110] = bcap[i];
+ txb[i + 120] = bcap[i];
}
- // 210 .. 229 = Callsign
+ // 220 .. 239 = Callsign
Byte[] callarr = statics.StringToByteArray(tb_callsign.Text);
for (int i = 0; i < 20; i++)
{
- if (i >= callarr.Length) txb[i+210] = 0;
- else txb[i + 210] = callarr[i];
+ if (i >= callarr.Length)
+ txb[i + 220] = 0;
+ else
+ txb[i + 220] = callarr[i];
}
- // 230 .. 239 = qthloc
+ // 240 .. 249 = qthloc
Byte[] qtharr = statics.StringToByteArray(tb_myqthloc.Text);
for (int i = 0; i < 10; i++)
{
- if (i >= qtharr.Length) txb[i + 230] = 0;
- else txb[i+230] = qtharr[i];
+ if (i >= qtharr.Length)
+ txb[i + 240] = 0;
+ else
+ txb[i + 240] = qtharr[i];
}
- // 240 .. 259 = Name
+ // 250 .. 269 = Name
Byte[] namearr = statics.StringToByteArray(tb_myname.Text);
for (int i = 0; i < 20; i++)
{
- if (i >= namearr.Length) txb[i+240] = 0;
- else txb[i + 240] = namearr[i];
+ if (i >= namearr.Length)
+ txb[i + 250] = 0;
+ else
+ txb[i + 250] = namearr[i];
}
if (statics.ModemIP == "1.2.3.4")
@@ -1316,6 +1473,7 @@ namespace oscardata
Byte[] textarr = File.ReadAllBytes(statics.zip_TXtempfilename);
ArraySend.Send(textarr, (Byte)txcommand, TXfilename, TXRealFilename);
+ lastFullName = TXfilename;
}
private void bt_file_ascii_Click(object sender, EventArgs e)
@@ -1335,38 +1493,32 @@ namespace oscardata
private void bt_sendFile(String filter, int cmd)
{
+ lastFullName = "";
OpenFileDialog open = new OpenFileDialog();
open.Filter = filter;
if (open.ShowDialog() == DialogResult.OK)
{
- txcommand = cmd;
- TXfilename = open.FileName;
- TXRealFilename = open.SafeFileName;
- if (txcommand == statics.BinaryFile)
- rtb_TXfile.Text = statics.langstr[20] + TXfilename + statics.langstr[21];
- else
- rtb_TXfile.Text = File.ReadAllText(TXfilename);
-
- // compress file
- ZipStorer zs = new ZipStorer();
- zs.zipFile(statics.zip_TXtempfilename, open.SafeFileName, open.FileName);
-
- TXRealFileSize = statics.GetFileSize(statics.zip_TXtempfilename);
- ShowTXstatus();
+ bt_prepareAndSendFile(open.FileName, open.SafeFileName, cmd);
}
}
- /*void removeTab(TabPage tb)
+ private void bt_prepareAndSendFile(String FilenameAndPath, String Filename, int txcmd)
{
- if (tabControl1.TabPages.IndexOf(tb) != -1)
- tabControl1.TabPages.Remove(tb);
- }
+ txcommand = txcmd;
+ TXfilename = FilenameAndPath;
+ TXRealFilename = Filename;
+ if (txcommand == statics.BinaryFile)
+ rtb_TXfile.Text = statics.langstr[20] + TXfilename + statics.langstr[21];
+ else
+ rtb_TXfile.Text = File.ReadAllText(TXfilename);
- void addTab(int idx, TabPage tb)
- {
- if (tabControl1.TabPages.IndexOf(tb) == -1)
- tabControl1.TabPages.Insert(idx,tb);
- }*/
+ // compress file
+ ZipStorer zs = new ZipStorer();
+ zs.zipFile(statics.zip_TXtempfilename, Filename, FilenameAndPath);
+
+ TXRealFileSize = statics.GetFileSize(statics.zip_TXtempfilename);
+ ShowTXstatus();
+ }
private void comboBox1_SelectedIndexChanged(object sender, EventArgs e)
{
@@ -1395,6 +1547,8 @@ namespace oscardata
removeTab(tabPage_rtty);*/
}
+ if (cb_speed.Text.Contains("1200")) statics.real_datarate = 1200;
+ if (cb_speed.Text.Contains("2400")) statics.real_datarate = 2400;
if (cb_speed.Text.Contains("3000")) statics.real_datarate = 3000;
if (cb_speed.Text.Contains("4000")) statics.real_datarate = 4000;
if (cb_speed.Text.Contains("4410")) statics.real_datarate = 4410;
@@ -1404,13 +1558,17 @@ namespace oscardata
if (cb_speed.Text.Contains("6600")) statics.real_datarate = 6600;
if (cb_speed.Text.Contains("7200")) statics.real_datarate = 7200;
- Byte[] txdata = new byte[statics.PayloadLen + 2];
+ /*Byte[] txdata = new byte[statics.PayloadLen + 2];
int idx = cb_speed.SelectedIndex;
txdata[0] = (Byte)statics.ResamplingRate; // BER Test Marker
txdata[1] = (Byte)idx;
+
+
// and send info to modem
- Udp.UdpSendCtrl(txdata);
+ Udp.UdpSendCtrl(txdata);*/
+
+ String s = cb_speed.Text;
txcommand = statics.noTX;
// stop any ongoing transmission
@@ -1689,10 +1847,10 @@ namespace oscardata
switch(i)
{
case 0:
- Rtty_deftext[0] = "\r\n\r\nCQ CQ CQ de %m CQ CQ CQ de %m pse k k k\r\n%r"; // CQ call
+ Rtty_deftext[0] = "\r\n\r\nRYRYRYRYRYRYRYRYRYRY\r\nCQ CQ CQ de %m CQ CQ CQ de %m pse k k k\r\n%r"; // CQ call
break;
case 1:
- Rtty_deftext[1] = "\r\n\r\n%c de %m pse k k k\r\n%r"; // answer CQ call
+ Rtty_deftext[1] = "\r\n\r\n%c %c de %m %m %m pse k k k\r\n%r"; // answer CQ call
break;
case 2:
Rtty_deftext[2] = "\r\n\r\n%c de %m\r\n"; // start TX
@@ -1724,7 +1882,7 @@ namespace oscardata
if (dr == DialogResult.Yes)
{
Byte[] txdata = new byte[1];
- txdata[0] = (Byte)statics.Modem_shutdown;
+ txdata[0] = statics.Modem_shutdown;
Udp.UdpSendCtrl(txdata);
MessageBox.Show(statics.langstr[24], statics.langstr[22], MessageBoxButtons.OK);
@@ -1736,7 +1894,7 @@ namespace oscardata
private void button1_Click(object sender, EventArgs e)
{
Byte[] txdata = new byte[1];
- txdata[0] = (Byte)statics.AutosendFile;
+ txdata[0] = statics.AutosendFile;
// and transmit it
Udp.UdpSendCtrl(txdata);
@@ -1745,7 +1903,7 @@ namespace oscardata
private void bt_resetmodem_Click(object sender, EventArgs e)
{
Byte[] txdata = new byte[1];
- txdata[0] = (Byte)statics.ResetModem;
+ txdata[0] = statics.ResetModem;
// and transmit it
Udp.UdpSendCtrl(txdata);
@@ -2019,7 +2177,6 @@ namespace oscardata
cb_sendIntro.Text = "send introduction before TX";
tb_recintro.Text = "record introduction";
lb_tuningqrgs.Text = "Send Marker Frequency:";
- label13.Text = "Data Security:";
textBox5.Text = "Click on Callsign or Name in RX window";
textBox2.Text = @"Special Markers:
%m... my call
@@ -2043,6 +2200,11 @@ namespace oscardata
bt_rtty_myinfo.Text = "My Info";
bt_rtty_station.Text = "My Station";
textBox6.Text = "or double click in spectrum";
+ textBox7.Text = "for advanced users only, see developers manual";
+ bt_open_html.Text = "Open received " + Environment.NewLine + "HTML file";
+ groupBox8.Text = "Send all files in folder";
+ cb_file_loop.Text = "ON / off";
+ label13.Text = "Pause between files";
}
if (language == 1)
@@ -2098,7 +2260,6 @@ namespace oscardata
cb_sendIntro.Text = "sende Vorstellung vor TX";
tb_recintro.Text = "Vorstellung aufnehmen";
lb_tuningqrgs.Text = "Sende Frequenzmarkierung:";
- label13.Text = "Datensicherheit:";
textBox5.Text = "Klicke auf Rufzeichen und Namen im RX Fenster";
textBox2.Text = @"Spezialzeichen:
%m... mein Rufzeichen
@@ -2122,6 +2283,11 @@ namespace oscardata
bt_rtty_myinfo.Text = "Meine Info";
bt_rtty_station.Text = "Meine Station";
textBox6.Text = "oder Doppelklick in Spektrum";
+ textBox7.Text = "nur für spezielle Nutzer, siehe Entwickler - Dokumentation";
+ bt_open_html.Text = "Öffne empfangene" + Environment.NewLine + "HTML Datei";
+ groupBox8.Text = "TX alle Dateien im Verz.";
+ cb_file_loop.Text = "EIN / aus";
+ label13.Text = "Pause zwischen Dateien";
}
}
@@ -2488,13 +2654,12 @@ namespace oscardata
ShowRTTYtext(selected_rtty_deftext, 0);
}
- int rtty_txon = 0;
int rtty_sync = 0;
private void bt_rtty_tx_Click(object sender, EventArgs e)
{
Byte[] txdata = new byte[2];
txdata[0] = statics.txonoff;
- txdata[1] = (Byte)((rtty_txon==1)?0:1);
+ txdata[1] = (Byte)((statics.rtty_txon==1)?0:1);
Udp.UdpSendCtrl(txdata);
}
@@ -2592,5 +2757,18 @@ namespace oscardata
{
ShowRTTYtext(13);
}
+
+ private void cb_extIF_CheckedChanged(object sender, EventArgs e)
+ {
+ statics.extData = (Byte)(cb_extIF.Checked?1:0);
+ }
+
+ private void bt_open_html_Click(object sender, EventArgs e)
+ {
+ if(statics.lastRXedHTMLfile.Length > 4)
+ OpenUrl(statics.lastRXedHTMLfile);
+ }
}
+
+ class DoubleBufferedPanel : Panel { public DoubleBufferedPanel() : base() { DoubleBuffered = true; } }
}
diff --git a/oscardata/oscardata/Form1.resx b/oscardata/oscardata/Form1.resx
index 2bf716c..0646b8e 100755
--- a/oscardata/oscardata/Form1.resx
+++ b/oscardata/oscardata/Form1.resx
@@ -137,7 +137,7 @@
AAEAAAD/////AQAAAAAAAAAMAgAAAFdTeXN0ZW0uV2luZG93cy5Gb3JtcywgVmVyc2lvbj00LjAuMC4w
LCBDdWx0dXJlPW5ldXRyYWwsIFB1YmxpY0tleVRva2VuPWI3N2E1YzU2MTkzNGUwODkFAQAAACZTeXN0
ZW0uV2luZG93cy5Gb3Jtcy5JbWFnZUxpc3RTdHJlYW1lcgEAAAAERGF0YQcCAgAAAAkDAAAADwMAAAA+
- JQAAAk1TRnQBSQFMAgEBFwEAAdgBDAHYAQwBEAEAARABAAT/AQkBAAj/AUIBTQE2AQQGAAE2AQQCAAEo
+ JQAAAk1TRnQBSQFMAgEBFwEAAcgBDQHIAQ0BEAEAARABAAT/AQkBAAj/AUIBTQE2AQQGAAE2AQQCAAEo
AwABQAMAAWADAAEBAQABCAYAARgYAAGAAgABgAMAAoABAAGAAwABgAEAAYABAAKAAgADwAEAAcAB3AHA
AQAB8AHKAaYBAAEzBQABMwEAATMBAAEzAQACMwIAAxYBAAMcAQADIgEAAykBAANVAQADTQEAA0IBAAM5
AQABgAF8Af8BAAJQAf8BAAGTAQAB1gEAAf8B7AHMAQABxgHWAe8BAAHWAucBAAGQAakBrQIAAf8BMwMA
@@ -5297,10 +5297,14 @@ GNU General Public License
FreeDV: https://github.com/drowe67/codec2
GNU Lesser General Public License v2.1
-Sound Library: libsoundio
+Sound Library Linux: libsoundio
https://github.com/andrewrk/libsoundio
MIT License
+Sound Library Windows: Portaudio
+https://github.com/PortAudio/portaudio
+https://github.com/PortAudio/portaudio/blob/master/LICENSE.txt
+
Reed Solomon error correction:
https://www.schifra.com/
License: Schifra User Group 1, GPL V2
diff --git a/oscardata/oscardata/bin/Release/oscardata.exe b/oscardata/oscardata/bin/Release/oscardata.exe
index ac7c65a..eb9e11c 100755
Binary files a/oscardata/oscardata/bin/Release/oscardata.exe and b/oscardata/oscardata/bin/Release/oscardata.exe differ
diff --git a/oscardata/oscardata/config.cs b/oscardata/oscardata/config.cs
index 614051f..0a66ac4 100755
--- a/oscardata/oscardata/config.cs
+++ b/oscardata/oscardata/config.cs
@@ -24,9 +24,10 @@ namespace oscardata
public static Byte BinaryFile = 5;
public static Byte Audio = 6;
public static Byte Userinfo = 7;
+ public static Byte ExternalDate = 8; // data from ext. application
// the upper values are for internal use
- public static Byte ResamplingRate = 16;
+ public static Byte BulletinFile = 16;
public static Byte AutosendFile = 17;
public static Byte AutosendFolder = 18;
public static Byte Modem_shutdown = 19;
@@ -92,6 +93,9 @@ namespace oscardata
public static String[] langstr;
public static int tuning_active = 0;
public static int tune_frequency = 1500;
+ public static int rtty_txon = 0;
+ public static Byte extData = 0;
+ public static String lastRXedHTMLfile = "";
public static String[] getOwnIPs()
{
diff --git a/oscardata/oscardata/receivefile.cs b/oscardata/oscardata/receivefile.cs
index fdc9c27..bcb54a9 100755
--- a/oscardata/oscardata/receivefile.cs
+++ b/oscardata/oscardata/receivefile.cs
@@ -81,6 +81,7 @@ namespace oscardata
bool autoRXnum = false;
String blockFilename = "";
+ // 2 ... file OK, file already exist
// 1 ... file OK
// 0 ... continue receiving
// -1 ... invalid header
@@ -154,9 +155,10 @@ namespace oscardata
if (rxtype == statics.AsciiFile || rxtype == statics.HTMLFile || rxtype == statics.BinaryFile)
{
// these file type must be unzipped
- handleZIPfiles();
+ int r = 1;
+ if (handleZIPfiles() == 1) r = 2;
receiving = false;
- return 1;
+ return r;
}
}
@@ -429,7 +431,7 @@ namespace oscardata
bool SaveFile()
{
- Console.WriteLine("save file");
+ //Console.WriteLine("save file");
// check if all blocks ok
for (int i = 0; i <= blockidx; i++)
@@ -450,7 +452,7 @@ namespace oscardata
// make filename
filename = makeRXfilename();
- Console.WriteLine("save at " + filename);
+ //Console.WriteLine("save at " + filename);
try
{
@@ -513,12 +515,18 @@ namespace oscardata
return bmp;
}
- void handleZIPfiles()
+ // returns:
+ // -1 ... error
+ // 0 ... ok
+ // 1 ... ok, but file exists already
+ int handleZIPfiles()
{
+ int ret = -1;
+
if (filename == null)
{
Console.WriteLine("handleZIPfile: no filename");
- return;
+ return ret;
}
try
@@ -530,7 +538,7 @@ namespace oscardata
if (fc.Length < ArraySend.FileSize)
{
Console.WriteLine("file not complete: got len=" + fc.Length + " expected len=" + ArraySend.FileSize);
- return;
+ return ret;
}
Array.Copy(fc, 0, fdst, 0, ArraySend.FileSize);
File.WriteAllBytes(statics.zip_RXtempfilename, fdst); // the received file (still zipped) is here
@@ -541,19 +549,28 @@ namespace oscardata
if (fl != null)
{
// save file
+ ret = 0;
// fl is the filename of the file inside the zip file, so the originally zipped file
// remove path to get just the filename
int idx = fl.LastIndexOf('/');
if (idx == -1) idx = fl.LastIndexOf('\\');
String fdest = fl.Substring(idx + 1);
fdest = statics.getHomePath("", fdest);
- // fdest is the file in the oscardata's user home directoty
- // remove old file with same name
- try { statics.FileDelete(fdest); } catch { }
+ // fdest is the file in the oscardata's user home directory
+
+ // check if file already exists
+ if (File.Exists(fdest))
+ {
+ ret = 1;
+ // remove old file with same name
+ try { statics.FileDelete(fdest); } catch { }
+ }
+
// move the unzipped file to the final location
File.Move(fl, fdest);
filesize = statics.GetFileSize(fdest);
StatusText = "unzip OK";
+ sendBulletin(fdest);
}
else
StatusText = "unzip failed";
@@ -564,6 +581,26 @@ namespace oscardata
{
StatusText = "unzip failed";
}
+
+ return ret;
+ }
+
+ // send a bulletin file to hsmodem
+ // which sends it via websocket to a browser
+ void sendBulletin(String fn)
+ {
+ if (Path.GetExtension(fn) != ".blt") return; // no bulletin, ignore
+
+ String s = File.ReadAllText(fn);
+ Byte[] tarr = statics.StringToByteArrayUtf8(s);
+
+ Byte[] tsend = new byte[tarr.Length + 1];
+
+ tsend[0] = statics.BulletinFile;
+ for (int i = 0; i < tarr.Length; i++)
+ tsend[i + 1] = tarr[i];
+
+ Udp.UdpSendCtrl(tsend);
}
}
}
diff --git a/oscardata/oscardata/udp.cs b/oscardata/oscardata/udp.cs
index 5e7b90f..269aa4b 100755
--- a/oscardata/oscardata/udp.cs
+++ b/oscardata/oscardata/udp.cs
@@ -158,6 +158,7 @@ namespace oscardata
statics.tune_frequency = b[idx++];
statics.tune_frequency <<= 8;
statics.tune_frequency += b[idx++];
+ statics.rtty_txon = b[idx++];
//Console.WriteLine("f:" + statics.tune_frequency);
Byte[] b1 = new byte[b.Length - idx];
Array.Copy(b, idx, b1, 0, b1.Length);
@@ -278,8 +279,8 @@ namespace oscardata
static int fftw = 410, ffth = 72;
static Bitmap bmskala = new Bitmap(fftw,ffth);
static bool bmf = false;
- static Font fnt = new Font("Verdana", 9.0f);
- static Font smallfnt = new Font("Verdana", 7.0f);
+ static Font fnt = new Font("Verdana", 10.0f);
+ static Font smallfnt = new Font("Verdana", 8.0f);
static Pen penyl = new Pen(Brushes.Yellow, 1);
static Pen pengn = new Pen(Brushes.LightGreen, 3);
@@ -294,6 +295,7 @@ namespace oscardata
bmf = true;
Pen pen = new Pen(Brushes.Navy, 1);
Pen pensolid = new Pen(Brushes.Navy, 1);
+ Pen pensolidwhite = new Pen(Brushes.White, 1);
pen.DashPattern = new float[] { 1.0F, 2.0F, 1.0F, 2.0F };
Pen penred = new Pen(Brushes.Red, 1);
Pen penred2 = new Pen(Brushes.Red, 2);
@@ -306,20 +308,28 @@ namespace oscardata
for (int x = 10; x <= 390; x += 10)
gr.DrawLine(pen, x, yl, x, yh);
- gr.DrawLine(penred2, 11, yl, 10, yh);
+ gr.DrawLine(penred2, 11, yl, 11, yh);
gr.DrawLine(penred, 150, yl, 150, yh);
gr.DrawLine(pensolid, 20, yl, 20, yh);
gr.DrawLine(pensolid, 280, yl, 280, yh);
gr.DrawLine(pensolid, 360, yl, 360, yh);
+ gr.DrawLine(pensolidwhite, 11, 12, 11,20);
+ gr.DrawLine(pensolidwhite, 150, yl + 2, 150, yl + 7);
+ gr.DrawLine(pensolidwhite, 20, yl + 2, 20, yl + 7);
+ gr.DrawLine(pensolidwhite, 280, yl + 2, 280, yl + 7);
+ gr.DrawLine(pensolidwhite, 360, yl+2, 360, yl+7);
+
gr.DrawRectangle(penred, 15, yh, 270, yl-yh);
- gr.DrawString("200", smallfnt, Brushes.Black, 8, yl);
- gr.DrawString("1500", smallfnt, Brushes.Black, 135, yl);
- gr.DrawString("2800", smallfnt, Brushes.Black, 265, yl);
- gr.DrawString("3600", smallfnt, Brushes.Black, 345, yl);
+ gr.DrawString("200", smallfnt, Brushes.DarkBlue, 8, yl+7);
+ gr.DrawString("1500", smallfnt, Brushes.DarkBlue, 135, yl + 7);
+ gr.DrawString("2800", smallfnt, Brushes.DarkBlue, 267, yl + 7);
+ gr.DrawString("3600", smallfnt, Brushes.DarkBlue, 345, yl + 7);
- gr.DrawString(statics.langstr[8], fnt, Brushes.Black, 100, 0);
+ gr.DrawString(statics.langstr[8], fnt, Brushes.White, 100, 0);
+
+ gr.DrawString("Ref", smallfnt, Brushes.LightCoral, 1, 0);
}
bmskala.MakeTransparent(Color.White);