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);