From 13c66c8bb326b47c8a64f1633616829a2d81f79f Mon Sep 17 00:00:00 2001 From: Paul Date: Mon, 23 May 2022 01:02:43 +0200 Subject: [PATCH 1/8] hackrf update --- AudioInput.cpp | 8 +- FmDecode.cpp | 10 +- HidDev.cpp | 17 + SdrDevice.h | 10 +- Settings.cpp | 40 +- Settings.h | 9 +- ft8/constants.c | 392 ++++ ft8/constants.h | 81 + ft8/crc.c | 63 + ft8/crc.h | 22 + ft8/encode.c | 195 ++ ft8/encode.h | 32 + ft8/pack.c | 366 ++++ ft8/pack.h | 11 + ft8/text.c | 253 +++ ft8/text.h | 37 + gui_bar.cpp | 17 +- gui_setup.cpp | 97 +- gui_setup.h | 7 +- install/README | 3804 --------------------------------- install/sdrberry_settings.cfg | 25 +- install/update.sh | 11 - sdrberry.cpp | 17 +- sdrberry.vcxproj | 11 +- sdrberry.vcxproj.filters | 27 + sdrstream.cpp | 5 +- sdrstream.h | 2 - 27 files changed, 1707 insertions(+), 3862 deletions(-) create mode 100644 ft8/constants.c create mode 100644 ft8/constants.h create mode 100644 ft8/crc.c create mode 100644 ft8/crc.h create mode 100644 ft8/encode.c create mode 100644 ft8/encode.h create mode 100644 ft8/pack.c create mode 100644 ft8/pack.h create mode 100644 ft8/text.c create mode 100644 ft8/text.h delete mode 100644 install/README delete mode 100644 install/update.sh diff --git a/AudioInput.cpp b/AudioInput.cpp index eae2fb40..c57a2afb 100644 --- a/AudioInput.cpp +++ b/AudioInput.cpp @@ -144,7 +144,7 @@ bool AudioInput::open(unsigned int device) void AudioInput::set_volume(int vol) { // log volume - m_volume = exp(((double)vol * 6.908) / 100.0) /10.0; + m_volume = exp(((double)vol * 6.908) / 100.0) / 5.0; printf("mic vol %f\n", (float)m_volume); } @@ -188,13 +188,13 @@ double AudioInput::Nexttone() { double angle = (asteps*cw_keyer_sidetone_frequency)*TWOPIOVERSAMPLERATE; if (++asteps >= 48000) asteps = 0; - return sin(angle); + return sin(angle) / 100.0; } void AudioInput::ToneBuffer() { SampleVector buf; - printf("tone %d \n", tune_tone); + for (int i = 0; i < bufferFrames; i++) { Sample f; @@ -219,7 +219,7 @@ double AudioInput::NextTwotone() double angle = (asteps*cw_keyer_sidetone_frequency)*TWOPIOVERSAMPLERATE; double angle2 = (asteps*cw_keyer_sidetone_frequency2)*TWOPIOVERSAMPLERATE; if (++asteps >= 48000) asteps = 0; - return (sin(angle) + sin(angle)) /2.0; + return (sin(angle) + sin(angle)) /200.0; } int AudioInput::queued_samples() diff --git a/FmDecode.cpp b/FmDecode.cpp index 561be3d4..b70082cf 100644 --- a/FmDecode.cpp +++ b/FmDecode.cpp @@ -505,6 +505,7 @@ void* rx_fm_thread(void* fm_ptr) usleep(5000); continue; } + int nosamples = iqsamples.size(); pfm->adjust_gain(iqsamples, gbar.get_if()); buf_mix.clear(); for (auto& col : iqsamples) @@ -516,7 +517,7 @@ void* rx_fm_thread(void* fm_ptr) buf_mix.push_back(v); } - if (buf_mix.size() >= nfft_samples && fft_block == 5) + if (fft_block == 5) { fft_block = 0; Fft_calc.process_samples(buf_mix); @@ -528,7 +529,7 @@ void* rx_fm_thread(void* fm_ptr) // Measure audio level. samples_mean_rms(audiosamples, audio_mean, audio_rms); audio_level = 0.95 * audio_level + 0.05 * audio_rms; - + int noaudiosamples = audiosamples.size(); // Set nominal audio volume. audio_output->adjust_gain(audiosamples); for (auto& col : audiosamples) @@ -540,11 +541,12 @@ void* rx_fm_thread(void* fm_ptr) } } const auto now = std::chrono::high_resolution_clock::now(); - if (timeLastPrint + std::chrono::seconds(1) < now) + if (timeLastPrint + std::chrono::seconds(20) < now) { timeLastPrint = now; const auto timePassed = std::chrono::duration_cast(now - startTime); - printf("Queued Audio Samples %d underrun %d\n", audio_output->queued_samples() / 2, audio_output->get_underrun()); + printf("Buffer queue %d Radio samples %d Audio Samples %d Queued Audio Samples %d underrun %d\n", fm_demod->source_buffer->size(),nosamples, noaudiosamples, audio_output->queued_samples() / 2, audio_output->get_underrun()); + audio_output->clear_underrun(); } iqsamples.clear(); audiosamples.clear(); diff --git a/HidDev.cpp b/HidDev.cpp index bb0d3a00..ac99c393 100644 --- a/HidDev.cpp +++ b/HidDev.cpp @@ -205,6 +205,7 @@ void HidDev::step_vfo() if (bevent && in_event.type == EV_REL && in_event.code == 11) { + switch (in_event.value) { case -240: @@ -216,6 +217,9 @@ void HidDev::step_vfo() case -480: value = -3; break; + case -600: + value = -4; + break; case 240: value = 1; break; @@ -225,6 +229,9 @@ void HidDev::step_vfo() case 480: value = 3; break; + case 600: + value = 4; + break; } } @@ -250,6 +257,11 @@ void HidDev::step_vfo() vfo.step_vfo(step, false); last_time = now; } + if (timePassed.count() > 1 && value == 4) + { + vfo.step_vfo(1.5*step, false); + last_time = now; + } if (timePassed.count() > 20 && value == -1) { vfo.step_vfo(-1 * step, false); @@ -265,6 +277,11 @@ void HidDev::step_vfo() vfo.step_vfo(-1 * step, false); last_time = now; } + if (timePassed.count() > 1 && value == -4) + { + vfo.step_vfo(-1.5 * step, false); + last_time = now; + } return; } diff --git a/SdrDevice.h b/SdrDevice.h index edb64a92..0f2bf122 100644 --- a/SdrDevice.h +++ b/SdrDevice.h @@ -25,8 +25,10 @@ class SdrDeviceChannel std::string probeChannel(); SoapySDR::Range get_full_gain_range() {return full_gain_range;} bool get_agc() {return agc;} - -private: + int get_bandwith_count() { return bandwidth_range.size(); } + long get_bandwith(int no) { return bandwidth_range[no].minimum(); } + + private: SoapySDR::Device *soapyDevice {nullptr}; int dir {0}; @@ -69,7 +71,9 @@ class SdrDevice std::vector get_tx_sample_rates(int channel) {return tx_channels[channel]->get_sample_rates();} int get_txchannels() {return numTxChans;} int get_rxchannels() {return numRxChans;} - + int get_bandwith_count(int channel) { return rx_channels[channel]->get_bandwith_count(); } + long get_bandwith(int channel, int no) { return rx_channels[channel]->get_bandwith(no); } + void setFrequency(const int direction, const size_t channel, const double frequency) { if (direction == SOAPY_SDR_TX && numTxChans < 1) diff --git a/Settings.cpp b/Settings.cpp index d7b7a785..b3dac423 100644 --- a/Settings.cpp +++ b/Settings.cpp @@ -11,8 +11,12 @@ const cfg::File::ConfigMap defaultOptions = { {"CAT", {{"USB", cfg::makeOption("/dev/serial/by-id/usb-Silicon_Labs_CP2102_USB_to_UART_Bridge_Controller_0001-if00-port0")}}}, {"samplerate", {{"radioberry", cfg::makeOption(384)}, {"plutosdr", cfg::makeOption(1000)}, {"rtlsdr", cfg::makeOption(1000)}, {"sdrplay", cfg::makeOption(1000)}}}, {"samplerate_tx", {{"radioberry", cfg::makeOption(384)}}}, - {"Radio", {{"gain", cfg::makeOption(0, 0, 100)}, {"volume", cfg::makeOption(50)}, {"drive", cfg::makeOption(89)}, {"micgain", cfg::makeOption(50)}, {"band", cfg::makeOption("ham")}, {"AGC", cfg::makeOption("off")}}}, + {"radioberry", {{"gain", cfg::makeOption(60)}, {"drive", cfg::makeOption(89)}, {"if-gain", cfg::makeOption(30)}, {"AGC", cfg::makeOption("off")}}}, + {"sdrplay", {{"gain", cfg::makeOption(30)}, {"drive", cfg::makeOption(89)}, {"if-gain", cfg::makeOption(30)}, {"AGC", cfg::makeOption("off")}}}, + {"rtlsdr", {{"gain", cfg::makeOption(40)}, {"drive", cfg::makeOption(89)}, {"if-gain", cfg::makeOption(60)}}}, + {"hackrf", {{"gain", cfg::makeOption(30)}, {"drive", cfg::makeOption(89)}, {"if-gain", cfg::makeOption(3)}}}, + {"plutosdr", {{"gain", cfg::makeOption(60)}, {"drive", cfg::makeOption(89)}, {"if-gain", cfg::makeOption(30)}, {"AGC", cfg::makeOption("off")}}}, {"VFO1", {{"freq", cfg::makeOption(3500000)}, {"Mode", cfg::makeOption("LSB")}}}, {"VFO2", {{"freq", cfg::makeOption(3500000)}, {"Mode", cfg::makeOption("LSB")}}}, {"Audio", {{"device", cfg::makeOption("default")}}}, @@ -443,6 +447,40 @@ int Settings::if_gain() return 0; } +int Settings::if_gain(string sdrdevice) +{ + auto option = config->getSection(sdrdevice); + auto s = option.find("if-gain"); + string st = s->second; + return atoi((const char *)st.c_str()); +} + +int Settings::gain(string sdrdevice) +{ + auto option = config->getSection(sdrdevice); + auto s = option.find("gain"); + string st = s->second; + return atoi((const char *)st.c_str()); +} + +int Settings::get_int(string sdrdevice, string key) +{ + auto option = config->getSection(sdrdevice); + auto s = option.find(key); + string st = s->second; + return atoi((const char *)st.c_str()); +} + +string Settings::get_string(string sdrdevice, string key) +{ + string st; + auto option = config->getSection(sdrdevice); + auto s = option.find(key); + if (s != option.end()) + st = s->second; + return st; +} + int Settings::gain() { if (radio.find("gain") != radio.end()) diff --git a/Settings.h b/Settings.h index 36d76fff..efbd05ed 100644 --- a/Settings.h +++ b/Settings.h @@ -50,9 +50,12 @@ class Settings void save_rf(int rf); void save_vfo(int vfo, long freq); void save_span(int span); - - - vector meters; + int if_gain(string sdrdevice); + int gain(string sdrdevice); + int get_int(string sdrdevice, string key); + string get_string(string sdrdevice, string key); + + vector meters; vector labels; vector f_low; vector f_high; diff --git a/ft8/constants.c b/ft8/constants.c new file mode 100644 index 00000000..330342a9 --- /dev/null +++ b/ft8/constants.c @@ -0,0 +1,392 @@ +#include "constants.h" + +// Costas sync tone pattern +const uint8_t kFT8_Costas_pattern[7] = { 3, 1, 4, 0, 6, 5, 2 }; +const uint8_t kFT4_Costas_pattern[4][4] = { + { 0, 1, 3, 2 }, + { 1, 0, 2, 3 }, + { 2, 3, 1, 0 }, + { 3, 2, 0, 1 } +}; + +// Gray code map (FTx bits -> channel symbols) +const uint8_t kFT8_Gray_map[8] = { 0, 1, 3, 2, 5, 6, 4, 7 }; +const uint8_t kFT4_Gray_map[4] = { 0, 1, 3, 2 }; + +const uint8_t kFT4_XOR_sequence[10] = { + 0x4Au, // 01001010 + 0x5Eu, // 01011110 + 0x89u, // 10001001 + 0xB4u, // 10110100 + 0xB0u, // 10110000 + 0x8Au, // 10001010 + 0x79u, // 01111001 + 0x55u, // 01010101 + 0xBEu, // 10111110 + 0x28u, // 00101 [000] +}; + +// Parity generator matrix for (174,91) LDPC code, stored in bitpacked format (MSB first) +const uint8_t kFTX_LDPC_generator[FTX_LDPC_M][FTX_LDPC_K_BYTES] = { + { 0x83, 0x29, 0xce, 0x11, 0xbf, 0x31, 0xea, 0xf5, 0x09, 0xf2, 0x7f, 0xc0 }, + { 0x76, 0x1c, 0x26, 0x4e, 0x25, 0xc2, 0x59, 0x33, 0x54, 0x93, 0x13, 0x20 }, + { 0xdc, 0x26, 0x59, 0x02, 0xfb, 0x27, 0x7c, 0x64, 0x10, 0xa1, 0xbd, 0xc0 }, + { 0x1b, 0x3f, 0x41, 0x78, 0x58, 0xcd, 0x2d, 0xd3, 0x3e, 0xc7, 0xf6, 0x20 }, + { 0x09, 0xfd, 0xa4, 0xfe, 0xe0, 0x41, 0x95, 0xfd, 0x03, 0x47, 0x83, 0xa0 }, + { 0x07, 0x7c, 0xcc, 0xc1, 0x1b, 0x88, 0x73, 0xed, 0x5c, 0x3d, 0x48, 0xa0 }, + { 0x29, 0xb6, 0x2a, 0xfe, 0x3c, 0xa0, 0x36, 0xf4, 0xfe, 0x1a, 0x9d, 0xa0 }, + { 0x60, 0x54, 0xfa, 0xf5, 0xf3, 0x5d, 0x96, 0xd3, 0xb0, 0xc8, 0xc3, 0xe0 }, + { 0xe2, 0x07, 0x98, 0xe4, 0x31, 0x0e, 0xed, 0x27, 0x88, 0x4a, 0xe9, 0x00 }, + { 0x77, 0x5c, 0x9c, 0x08, 0xe8, 0x0e, 0x26, 0xdd, 0xae, 0x56, 0x31, 0x80 }, + { 0xb0, 0xb8, 0x11, 0x02, 0x8c, 0x2b, 0xf9, 0x97, 0x21, 0x34, 0x87, 0xc0 }, + { 0x18, 0xa0, 0xc9, 0x23, 0x1f, 0xc6, 0x0a, 0xdf, 0x5c, 0x5e, 0xa3, 0x20 }, + { 0x76, 0x47, 0x1e, 0x83, 0x02, 0xa0, 0x72, 0x1e, 0x01, 0xb1, 0x2b, 0x80 }, + { 0xff, 0xbc, 0xcb, 0x80, 0xca, 0x83, 0x41, 0xfa, 0xfb, 0x47, 0xb2, 0xe0 }, + { 0x66, 0xa7, 0x2a, 0x15, 0x8f, 0x93, 0x25, 0xa2, 0xbf, 0x67, 0x17, 0x00 }, + { 0xc4, 0x24, 0x36, 0x89, 0xfe, 0x85, 0xb1, 0xc5, 0x13, 0x63, 0xa1, 0x80 }, + { 0x0d, 0xff, 0x73, 0x94, 0x14, 0xd1, 0xa1, 0xb3, 0x4b, 0x1c, 0x27, 0x00 }, + { 0x15, 0xb4, 0x88, 0x30, 0x63, 0x6c, 0x8b, 0x99, 0x89, 0x49, 0x72, 0xe0 }, + { 0x29, 0xa8, 0x9c, 0x0d, 0x3d, 0xe8, 0x1d, 0x66, 0x54, 0x89, 0xb0, 0xe0 }, + { 0x4f, 0x12, 0x6f, 0x37, 0xfa, 0x51, 0xcb, 0xe6, 0x1b, 0xd6, 0xb9, 0x40 }, + { 0x99, 0xc4, 0x72, 0x39, 0xd0, 0xd9, 0x7d, 0x3c, 0x84, 0xe0, 0x94, 0x00 }, + { 0x19, 0x19, 0xb7, 0x51, 0x19, 0x76, 0x56, 0x21, 0xbb, 0x4f, 0x1e, 0x80 }, + { 0x09, 0xdb, 0x12, 0xd7, 0x31, 0xfa, 0xee, 0x0b, 0x86, 0xdf, 0x6b, 0x80 }, + { 0x48, 0x8f, 0xc3, 0x3d, 0xf4, 0x3f, 0xbd, 0xee, 0xa4, 0xea, 0xfb, 0x40 }, + { 0x82, 0x74, 0x23, 0xee, 0x40, 0xb6, 0x75, 0xf7, 0x56, 0xeb, 0x5f, 0xe0 }, + { 0xab, 0xe1, 0x97, 0xc4, 0x84, 0xcb, 0x74, 0x75, 0x71, 0x44, 0xa9, 0xa0 }, + { 0x2b, 0x50, 0x0e, 0x4b, 0xc0, 0xec, 0x5a, 0x6d, 0x2b, 0xdb, 0xdd, 0x00 }, + { 0xc4, 0x74, 0xaa, 0x53, 0xd7, 0x02, 0x18, 0x76, 0x16, 0x69, 0x36, 0x00 }, + { 0x8e, 0xba, 0x1a, 0x13, 0xdb, 0x33, 0x90, 0xbd, 0x67, 0x18, 0xce, 0xc0 }, + { 0x75, 0x38, 0x44, 0x67, 0x3a, 0x27, 0x78, 0x2c, 0xc4, 0x20, 0x12, 0xe0 }, + { 0x06, 0xff, 0x83, 0xa1, 0x45, 0xc3, 0x70, 0x35, 0xa5, 0xc1, 0x26, 0x80 }, + { 0x3b, 0x37, 0x41, 0x78, 0x58, 0xcc, 0x2d, 0xd3, 0x3e, 0xc3, 0xf6, 0x20 }, + { 0x9a, 0x4a, 0x5a, 0x28, 0xee, 0x17, 0xca, 0x9c, 0x32, 0x48, 0x42, 0xc0 }, + { 0xbc, 0x29, 0xf4, 0x65, 0x30, 0x9c, 0x97, 0x7e, 0x89, 0x61, 0x0a, 0x40 }, + { 0x26, 0x63, 0xae, 0x6d, 0xdf, 0x8b, 0x5c, 0xe2, 0xbb, 0x29, 0x48, 0x80 }, + { 0x46, 0xf2, 0x31, 0xef, 0xe4, 0x57, 0x03, 0x4c, 0x18, 0x14, 0x41, 0x80 }, + { 0x3f, 0xb2, 0xce, 0x85, 0xab, 0xe9, 0xb0, 0xc7, 0x2e, 0x06, 0xfb, 0xe0 }, + { 0xde, 0x87, 0x48, 0x1f, 0x28, 0x2c, 0x15, 0x39, 0x71, 0xa0, 0xa2, 0xe0 }, + { 0xfc, 0xd7, 0xcc, 0xf2, 0x3c, 0x69, 0xfa, 0x99, 0xbb, 0xa1, 0x41, 0x20 }, + { 0xf0, 0x26, 0x14, 0x47, 0xe9, 0x49, 0x0c, 0xa8, 0xe4, 0x74, 0xce, 0xc0 }, + { 0x44, 0x10, 0x11, 0x58, 0x18, 0x19, 0x6f, 0x95, 0xcd, 0xd7, 0x01, 0x20 }, + { 0x08, 0x8f, 0xc3, 0x1d, 0xf4, 0xbf, 0xbd, 0xe2, 0xa4, 0xea, 0xfb, 0x40 }, + { 0xb8, 0xfe, 0xf1, 0xb6, 0x30, 0x77, 0x29, 0xfb, 0x0a, 0x07, 0x8c, 0x00 }, + { 0x5a, 0xfe, 0xa7, 0xac, 0xcc, 0xb7, 0x7b, 0xbc, 0x9d, 0x99, 0xa9, 0x00 }, + { 0x49, 0xa7, 0x01, 0x6a, 0xc6, 0x53, 0xf6, 0x5e, 0xcd, 0xc9, 0x07, 0x60 }, + { 0x19, 0x44, 0xd0, 0x85, 0xbe, 0x4e, 0x7d, 0xa8, 0xd6, 0xcc, 0x7d, 0x00 }, + { 0x25, 0x1f, 0x62, 0xad, 0xc4, 0x03, 0x2f, 0x0e, 0xe7, 0x14, 0x00, 0x20 }, + { 0x56, 0x47, 0x1f, 0x87, 0x02, 0xa0, 0x72, 0x1e, 0x00, 0xb1, 0x2b, 0x80 }, + { 0x2b, 0x8e, 0x49, 0x23, 0xf2, 0xdd, 0x51, 0xe2, 0xd5, 0x37, 0xfa, 0x00 }, + { 0x6b, 0x55, 0x0a, 0x40, 0xa6, 0x6f, 0x47, 0x55, 0xde, 0x95, 0xc2, 0x60 }, + { 0xa1, 0x8a, 0xd2, 0x8d, 0x4e, 0x27, 0xfe, 0x92, 0xa4, 0xf6, 0xc8, 0x40 }, + { 0x10, 0xc2, 0xe5, 0x86, 0x38, 0x8c, 0xb8, 0x2a, 0x3d, 0x80, 0x75, 0x80 }, + { 0xef, 0x34, 0xa4, 0x18, 0x17, 0xee, 0x02, 0x13, 0x3d, 0xb2, 0xeb, 0x00 }, + { 0x7e, 0x9c, 0x0c, 0x54, 0x32, 0x5a, 0x9c, 0x15, 0x83, 0x6e, 0x00, 0x00 }, + { 0x36, 0x93, 0xe5, 0x72, 0xd1, 0xfd, 0xe4, 0xcd, 0xf0, 0x79, 0xe8, 0x60 }, + { 0xbf, 0xb2, 0xce, 0xc5, 0xab, 0xe1, 0xb0, 0xc7, 0x2e, 0x07, 0xfb, 0xe0 }, + { 0x7e, 0xe1, 0x82, 0x30, 0xc5, 0x83, 0xcc, 0xcc, 0x57, 0xd4, 0xb0, 0x80 }, + { 0xa0, 0x66, 0xcb, 0x2f, 0xed, 0xaf, 0xc9, 0xf5, 0x26, 0x64, 0x12, 0x60 }, + { 0xbb, 0x23, 0x72, 0x5a, 0xbc, 0x47, 0xcc, 0x5f, 0x4c, 0xc4, 0xcd, 0x20 }, + { 0xde, 0xd9, 0xdb, 0xa3, 0xbe, 0xe4, 0x0c, 0x59, 0xb5, 0x60, 0x9b, 0x40 }, + { 0xd9, 0xa7, 0x01, 0x6a, 0xc6, 0x53, 0xe6, 0xde, 0xcd, 0xc9, 0x03, 0x60 }, + { 0x9a, 0xd4, 0x6a, 0xed, 0x5f, 0x70, 0x7f, 0x28, 0x0a, 0xb5, 0xfc, 0x40 }, + { 0xe5, 0x92, 0x1c, 0x77, 0x82, 0x25, 0x87, 0x31, 0x6d, 0x7d, 0x3c, 0x20 }, + { 0x4f, 0x14, 0xda, 0x82, 0x42, 0xa8, 0xb8, 0x6d, 0xca, 0x73, 0x35, 0x20 }, + { 0x8b, 0x8b, 0x50, 0x7a, 0xd4, 0x67, 0xd4, 0x44, 0x1d, 0xf7, 0x70, 0xe0 }, + { 0x22, 0x83, 0x1c, 0x9c, 0xf1, 0x16, 0x94, 0x67, 0xad, 0x04, 0xb6, 0x80 }, + { 0x21, 0x3b, 0x83, 0x8f, 0xe2, 0xae, 0x54, 0xc3, 0x8e, 0xe7, 0x18, 0x00 }, + { 0x5d, 0x92, 0x6b, 0x6d, 0xd7, 0x1f, 0x08, 0x51, 0x81, 0xa4, 0xe1, 0x20 }, + { 0x66, 0xab, 0x79, 0xd4, 0xb2, 0x9e, 0xe6, 0xe6, 0x95, 0x09, 0xe5, 0x60 }, + { 0x95, 0x81, 0x48, 0x68, 0x2d, 0x74, 0x8a, 0x38, 0xdd, 0x68, 0xba, 0xa0 }, + { 0xb8, 0xce, 0x02, 0x0c, 0xf0, 0x69, 0xc3, 0x2a, 0x72, 0x3a, 0xb1, 0x40 }, + { 0xf4, 0x33, 0x1d, 0x6d, 0x46, 0x16, 0x07, 0xe9, 0x57, 0x52, 0x74, 0x60 }, + { 0x6d, 0xa2, 0x3b, 0xa4, 0x24, 0xb9, 0x59, 0x61, 0x33, 0xcf, 0x9c, 0x80 }, + { 0xa6, 0x36, 0xbc, 0xbc, 0x7b, 0x30, 0xc5, 0xfb, 0xea, 0xe6, 0x7f, 0xe0 }, + { 0x5c, 0xb0, 0xd8, 0x6a, 0x07, 0xdf, 0x65, 0x4a, 0x90, 0x89, 0xa2, 0x00 }, + { 0xf1, 0x1f, 0x10, 0x68, 0x48, 0x78, 0x0f, 0xc9, 0xec, 0xdd, 0x80, 0xa0 }, + { 0x1f, 0xbb, 0x53, 0x64, 0xfb, 0x8d, 0x2c, 0x9d, 0x73, 0x0d, 0x5b, 0xa0 }, + { 0xfc, 0xb8, 0x6b, 0xc7, 0x0a, 0x50, 0xc9, 0xd0, 0x2a, 0x5d, 0x03, 0x40 }, + { 0xa5, 0x34, 0x43, 0x30, 0x29, 0xea, 0xc1, 0x5f, 0x32, 0x2e, 0x34, 0xc0 }, + { 0xc9, 0x89, 0xd9, 0xc7, 0xc3, 0xd3, 0xb8, 0xc5, 0x5d, 0x75, 0x13, 0x00 }, + { 0x7b, 0xb3, 0x8b, 0x2f, 0x01, 0x86, 0xd4, 0x66, 0x43, 0xae, 0x96, 0x20 }, + { 0x26, 0x44, 0xeb, 0xad, 0xeb, 0x44, 0xb9, 0x46, 0x7d, 0x1f, 0x42, 0xc0 }, + { 0x60, 0x8c, 0xc8, 0x57, 0x59, 0x4b, 0xfb, 0xb5, 0x5d, 0x69, 0x60, 0x00 } +}; + +// Each row describes one LDPC parity check. +// Each number is an index into the codeword (1-origin). +// The codeword bits mentioned in each row must XOR to zero. +const uint8_t kFTX_LDPC_Nm[FTX_LDPC_M][7] = { + { 4, 31, 59, 91, 92, 96, 153 }, + { 5, 32, 60, 93, 115, 146, 0 }, + { 6, 24, 61, 94, 122, 151, 0 }, + { 7, 33, 62, 95, 96, 143, 0 }, + { 8, 25, 63, 83, 93, 96, 148 }, + { 6, 32, 64, 97, 126, 138, 0 }, + { 5, 34, 65, 78, 98, 107, 154 }, + { 9, 35, 66, 99, 139, 146, 0 }, + { 10, 36, 67, 100, 107, 126, 0 }, + { 11, 37, 67, 87, 101, 139, 158 }, + { 12, 38, 68, 102, 105, 155, 0 }, + { 13, 39, 69, 103, 149, 162, 0 }, + { 8, 40, 70, 82, 104, 114, 145 }, + { 14, 41, 71, 88, 102, 123, 156 }, + { 15, 42, 59, 106, 123, 159, 0 }, + { 1, 33, 72, 106, 107, 157, 0 }, + { 16, 43, 73, 108, 141, 160, 0 }, + { 17, 37, 74, 81, 109, 131, 154 }, + { 11, 44, 75, 110, 121, 166, 0 }, + { 45, 55, 64, 111, 130, 161, 173 }, + { 8, 46, 71, 112, 119, 166, 0 }, + { 18, 36, 76, 89, 113, 114, 143 }, + { 19, 38, 77, 104, 116, 163, 0 }, + { 20, 47, 70, 92, 138, 165, 0 }, + { 2, 48, 74, 113, 128, 160, 0 }, + { 21, 45, 78, 83, 117, 121, 151 }, + { 22, 47, 58, 118, 127, 164, 0 }, + { 16, 39, 62, 112, 134, 158, 0 }, + { 23, 43, 79, 120, 131, 145, 0 }, + { 19, 35, 59, 73, 110, 125, 161 }, + { 20, 36, 63, 94, 136, 161, 0 }, + { 14, 31, 79, 98, 132, 164, 0 }, + { 3, 44, 80, 124, 127, 169, 0 }, + { 19, 46, 81, 117, 135, 167, 0 }, + { 7, 49, 58, 90, 100, 105, 168 }, + { 12, 50, 61, 118, 119, 144, 0 }, + { 13, 51, 64, 114, 118, 157, 0 }, + { 24, 52, 76, 129, 148, 149, 0 }, + { 25, 53, 69, 90, 101, 130, 156 }, + { 20, 46, 65, 80, 120, 140, 170 }, + { 21, 54, 77, 100, 140, 171, 0 }, + { 35, 82, 133, 142, 171, 174, 0 }, + { 14, 30, 83, 113, 125, 170, 0 }, + { 4, 29, 68, 120, 134, 173, 0 }, + { 1, 4, 52, 57, 86, 136, 152 }, + { 26, 51, 56, 91, 122, 137, 168 }, + { 52, 84, 110, 115, 145, 168, 0 }, + { 7, 50, 81, 99, 132, 173, 0 }, + { 23, 55, 67, 95, 172, 174, 0 }, + { 26, 41, 77, 109, 141, 148, 0 }, + { 2, 27, 41, 61, 62, 115, 133 }, + { 27, 40, 56, 124, 125, 126, 0 }, + { 18, 49, 55, 124, 141, 167, 0 }, + { 6, 33, 85, 108, 116, 156, 0 }, + { 28, 48, 70, 85, 105, 129, 158 }, + { 9, 54, 63, 131, 147, 155, 0 }, + { 22, 53, 68, 109, 121, 174, 0 }, + { 3, 13, 48, 78, 95, 123, 0 }, + { 31, 69, 133, 150, 155, 169, 0 }, + { 12, 43, 66, 89, 97, 135, 159 }, + { 5, 39, 75, 102, 136, 167, 0 }, + { 2, 54, 86, 101, 135, 164, 0 }, + { 15, 56, 87, 108, 119, 171, 0 }, + { 10, 44, 82, 91, 111, 144, 149 }, + { 23, 34, 71, 94, 127, 153, 0 }, + { 11, 49, 88, 92, 142, 157, 0 }, + { 29, 34, 87, 97, 147, 162, 0 }, + { 30, 50, 60, 86, 137, 142, 162 }, + { 10, 53, 66, 84, 112, 128, 165 }, + { 22, 57, 85, 93, 140, 159, 0 }, + { 28, 32, 72, 103, 132, 166, 0 }, + { 28, 29, 84, 88, 117, 143, 150 }, + { 1, 26, 45, 80, 128, 147, 0 }, + { 17, 27, 89, 103, 116, 153, 0 }, + { 51, 57, 98, 163, 165, 172, 0 }, + { 21, 37, 73, 138, 152, 169, 0 }, + { 16, 47, 76, 130, 137, 154, 0 }, + { 3, 24, 30, 72, 104, 139, 0 }, + { 9, 40, 90, 106, 134, 151, 0 }, + { 15, 58, 60, 74, 111, 150, 163 }, + { 18, 42, 79, 144, 146, 152, 0 }, + { 25, 38, 65, 99, 122, 160, 0 }, + { 17, 42, 75, 129, 170, 172, 0 } +}; + +// Each row corresponds to a codeword bit. +// The numbers indicate which three LDPC parity checks (rows in Nm) refer to the codeword bit. +// 1-origin. +const uint8_t kFTX_LDPC_Mn[FTX_LDPC_N][3] = { + { 16, 45, 73 }, + { 25, 51, 62 }, + { 33, 58, 78 }, + { 1, 44, 45 }, + { 2, 7, 61 }, + { 3, 6, 54 }, + { 4, 35, 48 }, + { 5, 13, 21 }, + { 8, 56, 79 }, + { 9, 64, 69 }, + { 10, 19, 66 }, + { 11, 36, 60 }, + { 12, 37, 58 }, + { 14, 32, 43 }, + { 15, 63, 80 }, + { 17, 28, 77 }, + { 18, 74, 83 }, + { 22, 53, 81 }, + { 23, 30, 34 }, + { 24, 31, 40 }, + { 26, 41, 76 }, + { 27, 57, 70 }, + { 29, 49, 65 }, + { 3, 38, 78 }, + { 5, 39, 82 }, + { 46, 50, 73 }, + { 51, 52, 74 }, + { 55, 71, 72 }, + { 44, 67, 72 }, + { 43, 68, 78 }, + { 1, 32, 59 }, + { 2, 6, 71 }, + { 4, 16, 54 }, + { 7, 65, 67 }, + { 8, 30, 42 }, + { 9, 22, 31 }, + { 10, 18, 76 }, + { 11, 23, 82 }, + { 12, 28, 61 }, + { 13, 52, 79 }, + { 14, 50, 51 }, + { 15, 81, 83 }, + { 17, 29, 60 }, + { 19, 33, 64 }, + { 20, 26, 73 }, + { 21, 34, 40 }, + { 24, 27, 77 }, + { 25, 55, 58 }, + { 35, 53, 66 }, + { 36, 48, 68 }, + { 37, 46, 75 }, + { 38, 45, 47 }, + { 39, 57, 69 }, + { 41, 56, 62 }, + { 20, 49, 53 }, + { 46, 52, 63 }, + { 45, 70, 75 }, + { 27, 35, 80 }, + { 1, 15, 30 }, + { 2, 68, 80 }, + { 3, 36, 51 }, + { 4, 28, 51 }, + { 5, 31, 56 }, + { 6, 20, 37 }, + { 7, 40, 82 }, + { 8, 60, 69 }, + { 9, 10, 49 }, + { 11, 44, 57 }, + { 12, 39, 59 }, + { 13, 24, 55 }, + { 14, 21, 65 }, + { 16, 71, 78 }, + { 17, 30, 76 }, + { 18, 25, 80 }, + { 19, 61, 83 }, + { 22, 38, 77 }, + { 23, 41, 50 }, + { 7, 26, 58 }, + { 29, 32, 81 }, + { 33, 40, 73 }, + { 18, 34, 48 }, + { 13, 42, 64 }, + { 5, 26, 43 }, + { 47, 69, 72 }, + { 54, 55, 70 }, + { 45, 62, 68 }, + { 10, 63, 67 }, + { 14, 66, 72 }, + { 22, 60, 74 }, + { 35, 39, 79 }, + { 1, 46, 64 }, + { 1, 24, 66 }, + { 2, 5, 70 }, + { 3, 31, 65 }, + { 4, 49, 58 }, + { 1, 4, 5 }, + { 6, 60, 67 }, + { 7, 32, 75 }, + { 8, 48, 82 }, + { 9, 35, 41 }, + { 10, 39, 62 }, + { 11, 14, 61 }, + { 12, 71, 74 }, + { 13, 23, 78 }, + { 11, 35, 55 }, + { 15, 16, 79 }, + { 7, 9, 16 }, + { 17, 54, 63 }, + { 18, 50, 57 }, + { 19, 30, 47 }, + { 20, 64, 80 }, + { 21, 28, 69 }, + { 22, 25, 43 }, + { 13, 22, 37 }, + { 2, 47, 51 }, + { 23, 54, 74 }, + { 26, 34, 72 }, + { 27, 36, 37 }, + { 21, 36, 63 }, + { 29, 40, 44 }, + { 19, 26, 57 }, + { 3, 46, 82 }, + { 14, 15, 58 }, + { 33, 52, 53 }, + { 30, 43, 52 }, + { 6, 9, 52 }, + { 27, 33, 65 }, + { 25, 69, 73 }, + { 38, 55, 83 }, + { 20, 39, 77 }, + { 18, 29, 56 }, + { 32, 48, 71 }, + { 42, 51, 59 }, + { 28, 44, 79 }, + { 34, 60, 62 }, + { 31, 45, 61 }, + { 46, 68, 77 }, + { 6, 24, 76 }, + { 8, 10, 78 }, + { 40, 41, 70 }, + { 17, 50, 53 }, + { 42, 66, 68 }, + { 4, 22, 72 }, + { 36, 64, 81 }, + { 13, 29, 47 }, + { 2, 8, 81 }, + { 56, 67, 73 }, + { 5, 38, 50 }, + { 12, 38, 64 }, + { 59, 72, 80 }, + { 3, 26, 79 }, + { 45, 76, 81 }, + { 1, 65, 74 }, + { 7, 18, 77 }, + { 11, 56, 59 }, + { 14, 39, 54 }, + { 16, 37, 66 }, + { 10, 28, 55 }, + { 15, 60, 70 }, + { 17, 25, 82 }, + { 20, 30, 31 }, + { 12, 67, 68 }, + { 23, 75, 80 }, + { 27, 32, 62 }, + { 24, 69, 75 }, + { 19, 21, 71 }, + { 34, 53, 61 }, + { 35, 46, 47 }, + { 33, 59, 76 }, + { 40, 43, 83 }, + { 41, 42, 63 }, + { 49, 75, 83 }, + { 20, 44, 48 }, + { 42, 49, 57 } +}; + +const uint8_t kFTX_LDPC_Num_rows[FTX_LDPC_M] = { + 7, 6, 6, 6, 7, 6, 7, 6, 6, 7, 6, 6, 7, 7, 6, 6, + 6, 7, 6, 7, 6, 7, 6, 6, 6, 7, 6, 6, 6, 7, 6, 6, + 6, 6, 7, 6, 6, 6, 7, 7, 6, 6, 6, 6, 7, 7, 6, 6, + 6, 6, 7, 6, 6, 6, 7, 6, 6, 6, 6, 7, 6, 6, 6, 7, + 6, 6, 6, 7, 7, 6, 6, 7, 6, 6, 6, 6, 6, 6, 6, 7, + 6, 6, 6 +}; \ No newline at end of file diff --git a/ft8/constants.h b/ft8/constants.h new file mode 100644 index 00000000..b7531a45 --- /dev/null +++ b/ft8/constants.h @@ -0,0 +1,81 @@ +#ifndef _INCLUDE_CONSTANTS_H_ +#define _INCLUDE_CONSTANTS_H_ + +#include + +typedef enum +{ + PROTO_FT4, + PROTO_FT8 +} ftx_protocol_t; + +#define FT8_SYMBOL_PERIOD (0.160f) ///< FT8 symbol duration, defines tone deviation in Hz and symbol rate +#define FT8_SLOT_TIME (15.0f) ///< FT8 slot period + +#define FT4_SYMBOL_PERIOD (0.048f) ///< FT4 symbol duration, defines tone deviation in Hz and symbol rate +#define FT4_SLOT_TIME (7.5f) ///< FT4 slot period + +// Define FT8 symbol counts +// FT8 message structure: +// S D1 S D2 S +// S - sync block (7 symbols of Costas pattern) +// D1 - first data block (29 symbols each encoding 3 bits) +#define FT8_ND (58) ///< Data symbols +#define FT8_NN (79) ///< Total channel symbols (FT8_NS + FT8_ND) +#define FT8_LENGTH_SYNC (7) ///< Length of each sync group +#define FT8_NUM_SYNC (3) ///< Number of sync groups +#define FT8_SYNC_OFFSET (36) ///< Offset between sync groups + +// Define FT4 symbol counts +// FT4 message structure: +// R Sa D1 Sb D2 Sc D3 Sd R +// R - ramping symbol (no payload information conveyed) +// Sx - one of four _different_ sync blocks (4 symbols of Costas pattern) +// Dy - data block (29 symbols each encoding 2 bits) +#define FT4_ND (87) ///< Data symbols +#define FT4_NR (2) ///< Ramp symbols (beginning + end) +#define FT4_NN (105) ///< Total channel symbols (FT4_NS + FT4_ND + FT4_NR) +#define FT4_LENGTH_SYNC (4) ///< Length of each sync group +#define FT4_NUM_SYNC (4) ///< Number of sync groups +#define FT4_SYNC_OFFSET (33) ///< Offset between sync groups + +// Define LDPC parameters +#define FTX_LDPC_N (174) ///< Number of bits in the encoded message (payload with LDPC checksum bits) +#define FTX_LDPC_K (91) ///< Number of payload bits (including CRC) +#define FTX_LDPC_M (83) ///< Number of LDPC checksum bits (FTX_LDPC_N - FTX_LDPC_K) +#define FTX_LDPC_N_BYTES ((FTX_LDPC_N + 7) / 8) ///< Number of whole bytes needed to store 174 bits (full message) +#define FTX_LDPC_K_BYTES ((FTX_LDPC_K + 7) / 8) ///< Number of whole bytes needed to store 91 bits (payload + CRC only) + +// Define CRC parameters +#define FT8_CRC_POLYNOMIAL ((uint16_t)0x2757u) ///< CRC-14 polynomial without the leading (MSB) 1 +#define FT8_CRC_WIDTH (14) + +/// Costas 7x7 tone pattern for synchronization +extern const uint8_t kFT8_Costas_pattern[7]; +extern const uint8_t kFT4_Costas_pattern[4][4]; + +/// Gray code map to encode 8 symbols (tones) +extern const uint8_t kFT8_Gray_map[8]; +extern const uint8_t kFT4_Gray_map[4]; + +extern const uint8_t kFT4_XOR_sequence[10]; + +/// Parity generator matrix for (174,91) LDPC code, stored in bitpacked format (MSB first) +extern const uint8_t kFTX_LDPC_generator[FTX_LDPC_M][FTX_LDPC_K_BYTES]; + +/// LDPC(174,91) parity check matrix, containing 83 rows, +/// each row describes one parity check, +/// each number is an index into the codeword (1-origin). +/// The codeword bits mentioned in each row must xor to zero. +/// From WSJT-X's ldpc_174_91_c_reordered_parity.f90. +extern const uint8_t kFTX_LDPC_Nm[FTX_LDPC_M][7]; + +/// Mn from WSJT-X's bpdecode174.f90. Each row corresponds to a codeword bit. +/// The numbers indicate which three parity checks (rows in Nm) refer to the codeword bit. +/// The numbers use 1 as the origin (first entry). +extern const uint8_t kFTX_LDPC_Mn[FTX_LDPC_N][3]; + +/// Number of rows (columns in C/C++) in the array Nm. +extern const uint8_t kFTX_LDPC_Num_rows[FTX_LDPC_M]; + +#endif // _INCLUDE_CONSTANTS_H_ diff --git a/ft8/crc.c b/ft8/crc.c new file mode 100644 index 00000000..72a4c896 --- /dev/null +++ b/ft8/crc.c @@ -0,0 +1,63 @@ +#include "crc.h" +#include "constants.h" + +#define TOPBIT (1u << (FT8_CRC_WIDTH - 1)) + +// Compute 14-bit CRC for a sequence of given number of bits +// Adapted from https://barrgroup.com/Embedded-Systems/How-To/CRC-Calculation-C-Code +// [IN] message - byte sequence (MSB first) +// [IN] num_bits - number of bits in the sequence +uint16_t ftx_compute_crc(const uint8_t message[], int num_bits) +{ + uint16_t remainder = 0; + int idx_byte = 0; + + // Perform modulo-2 division, a bit at a time. + for (int idx_bit = 0; idx_bit < num_bits; ++idx_bit) + { + if (idx_bit % 8 == 0) + { + // Bring the next byte into the remainder. + remainder ^= (message[idx_byte] << (FT8_CRC_WIDTH - 8)); + ++idx_byte; + } + + // Try to divide the current data bit. + if (remainder & TOPBIT) + { + remainder = (remainder << 1) ^ FT8_CRC_POLYNOMIAL; + } + else + { + remainder = (remainder << 1); + } + } + + return remainder & ((TOPBIT << 1) - 1u); +} + +uint16_t ftx_extract_crc(const uint8_t a91[]) +{ + uint16_t chksum = ((a91[9] & 0x07) << 11) | (a91[10] << 3) | (a91[11] >> 5); + return chksum; +} + +void ftx_add_crc(const uint8_t payload[], uint8_t a91[]) +{ + // Copy 77 bits of payload data + for (int i = 0; i < 10; i++) + a91[i] = payload[i]; + + // Clear 3 bits after the payload to make 82 bits + a91[9] &= 0xF8u; + a91[10] = 0; + + // Calculate CRC of 82 bits (77 + 5 zeros) + // 'The CRC is calculated on the source-encoded message, zero-extended from 77 to 82 bits' + uint16_t checksum = ftx_compute_crc(a91, 96 - 14); + + // Store the CRC at the end of 77 bit message + a91[9] |= (uint8_t)(checksum >> 11); + a91[10] = (uint8_t)(checksum >> 3); + a91[11] = (uint8_t)(checksum << 5); +} diff --git a/ft8/crc.h b/ft8/crc.h new file mode 100644 index 00000000..b510122e --- /dev/null +++ b/ft8/crc.h @@ -0,0 +1,22 @@ +#ifndef _INCLUDE_CRC_H_ +#define _INCLUDE_CRC_H_ + +#include +#include + +// Compute 14-bit CRC for a sequence of given number of bits using FT8/FT4 CRC polynomial +// [IN] message - byte sequence (MSB first) +// [IN] num_bits - number of bits in the sequence +uint16_t ftx_compute_crc(const uint8_t message[], int num_bits); + +/// Extract the FT8/FT4 CRC of a packed message (during decoding) +/// @param[in] a91 77 bits of payload data + CRC +/// @return Extracted CRC +uint16_t ftx_extract_crc(const uint8_t a91[]); + +/// Add FT8/FT4 CRC to a packed message (during encoding) +/// @param[in] payload 77 bits of payload data +/// @param[out] a91 91 bits of payload data + CRC +void ftx_add_crc(const uint8_t payload[], uint8_t a91[]); + +#endif // _INCLUDE_CRC_H_ \ No newline at end of file diff --git a/ft8/encode.c b/ft8/encode.c new file mode 100644 index 00000000..33c249cd --- /dev/null +++ b/ft8/encode.c @@ -0,0 +1,195 @@ +#include "encode.h" +#include "constants.h" +#include "crc.h" + +#include + +// Returns 1 if an odd number of bits are set in x, zero otherwise +static uint8_t parity8(uint8_t x) +{ + x ^= x >> 4; // a b c d ae bf cg dh + x ^= x >> 2; // a b ac bd cae dbf aecg bfdh + x ^= x >> 1; // a ab bac acbd bdcae caedbf aecgbfdh + return x % 2; // modulo 2 +} + +// Encode via LDPC a 91-bit message and return a 174-bit codeword. +// The generator matrix has dimensions (87,87). +// The code is a (174,91) regular LDPC code with column weight 3. +// Arguments: +// [IN] message - array of 91 bits stored as 12 bytes (MSB first) +// [OUT] codeword - array of 174 bits stored as 22 bytes (MSB first) +static void encode174(const uint8_t* message, uint8_t* codeword) +{ + // This implementation accesses the generator bits straight from the packed binary representation in kFTX_LDPC_generator + + // Fill the codeword with message and zeros, as we will only update binary ones later + for (int j = 0; j < FTX_LDPC_N_BYTES; ++j) + { + codeword[j] = (j < FTX_LDPC_K_BYTES) ? message[j] : 0; + } + + // Compute the byte index and bit mask for the first checksum bit + uint8_t col_mask = (0x80u >> (FTX_LDPC_K % 8u)); // bitmask of current byte + uint8_t col_idx = FTX_LDPC_K_BYTES - 1; // index into byte array + + // Compute the LDPC checksum bits and store them in codeword + for (int i = 0; i < FTX_LDPC_M; ++i) + { + // Fast implementation of bitwise multiplication and parity checking + // Normally nsum would contain the result of dot product between message and kFTX_LDPC_generator[i], + // but we only compute the sum modulo 2. + uint8_t nsum = 0; + for (int j = 0; j < FTX_LDPC_K_BYTES; ++j) + { + uint8_t bits = message[j] & kFTX_LDPC_generator[i][j]; // bitwise AND (bitwise multiplication) + nsum ^= parity8(bits); // bitwise XOR (addition modulo 2) + } + + // Set the current checksum bit in codeword if nsum is odd + if (nsum % 2) + { + codeword[col_idx] |= col_mask; + } + + // Update the byte index and bit mask for the next checksum bit + col_mask >>= 1; + if (col_mask == 0) + { + col_mask = 0x80u; + ++col_idx; + } + } +} + +void ft8_encode(const uint8_t* payload, uint8_t* tones) +{ + uint8_t a91[FTX_LDPC_K_BYTES]; // Store 77 bits of payload + 14 bits CRC + + // Compute and add CRC at the end of the message + // a91 contains 77 bits of payload + 14 bits of CRC + ftx_add_crc(payload, a91); + + uint8_t codeword[FTX_LDPC_N_BYTES]; + encode174(a91, codeword); + + // Message structure: S7 D29 S7 D29 S7 + // Total symbols: 79 (FT8_NN) + + uint8_t mask = 0x80u; // Mask to extract 1 bit from codeword + int i_byte = 0; // Index of the current byte of the codeword + for (int i_tone = 0; i_tone < FT8_NN; ++i_tone) + { + if ((i_tone >= 0) && (i_tone < 7)) + { + tones[i_tone] = kFT8_Costas_pattern[i_tone]; + } + else if ((i_tone >= 36) && (i_tone < 43)) + { + tones[i_tone] = kFT8_Costas_pattern[i_tone - 36]; + } + else if ((i_tone >= 72) && (i_tone < 79)) + { + tones[i_tone] = kFT8_Costas_pattern[i_tone - 72]; + } + else + { + // Extract 3 bits from codeword at i-th position + uint8_t bits3 = 0; + + if (codeword[i_byte] & mask) + bits3 |= 4; + if (0 == (mask >>= 1)) + { + mask = 0x80u; + i_byte++; + } + if (codeword[i_byte] & mask) + bits3 |= 2; + if (0 == (mask >>= 1)) + { + mask = 0x80u; + i_byte++; + } + if (codeword[i_byte] & mask) + bits3 |= 1; + if (0 == (mask >>= 1)) + { + mask = 0x80u; + i_byte++; + } + + tones[i_tone] = kFT8_Gray_map[bits3]; + } + } +} + +void ft4_encode(const uint8_t* payload, uint8_t* tones) +{ + uint8_t a91[FTX_LDPC_K_BYTES]; // Store 77 bits of payload + 14 bits CRC + uint8_t payload_xor[10]; // Encoded payload data + + // '[..] for FT4 only, in order to avoid transmitting a long string of zeros when sending CQ messages, + // the assembled 77-bit message is bitwise exclusive-OR’ed with [a] pseudorandom sequence before computing the CRC and FEC parity bits' + for (int i = 0; i < 10; ++i) + { + payload_xor[i] = payload[i] ^ kFT4_XOR_sequence[i]; + } + + // Compute and add CRC at the end of the message + // a91 contains 77 bits of payload + 14 bits of CRC + ftx_add_crc(payload_xor, a91); + + uint8_t codeword[FTX_LDPC_N_BYTES]; + encode174(a91, codeword); // 91 bits -> 174 bits + + // Message structure: R S4_1 D29 S4_2 D29 S4_3 D29 S4_4 R + // Total symbols: 105 (FT4_NN) + + uint8_t mask = 0x80u; // Mask to extract 1 bit from codeword + int i_byte = 0; // Index of the current byte of the codeword + for (int i_tone = 0; i_tone < FT4_NN; ++i_tone) + { + if ((i_tone == 0) || (i_tone == 104)) + { + tones[i_tone] = 0; // R (ramp) symbol + } + else if ((i_tone >= 1) && (i_tone < 5)) + { + tones[i_tone] = kFT4_Costas_pattern[0][i_tone - 1]; + } + else if ((i_tone >= 34) && (i_tone < 38)) + { + tones[i_tone] = kFT4_Costas_pattern[1][i_tone - 34]; + } + else if ((i_tone >= 67) && (i_tone < 71)) + { + tones[i_tone] = kFT4_Costas_pattern[2][i_tone - 67]; + } + else if ((i_tone >= 100) && (i_tone < 104)) + { + tones[i_tone] = kFT4_Costas_pattern[3][i_tone - 100]; + } + else + { + // Extract 2 bits from codeword at i-th position + uint8_t bits2 = 0; + + if (codeword[i_byte] & mask) + bits2 |= 2; + if (0 == (mask >>= 1)) + { + mask = 0x80u; + i_byte++; + } + if (codeword[i_byte] & mask) + bits2 |= 1; + if (0 == (mask >>= 1)) + { + mask = 0x80u; + i_byte++; + } + tones[i_tone] = kFT4_Gray_map[bits2]; + } + } +} diff --git a/ft8/encode.h b/ft8/encode.h new file mode 100644 index 00000000..d5e2759a --- /dev/null +++ b/ft8/encode.h @@ -0,0 +1,32 @@ +#ifndef _INCLUDE_ENCODE_H_ +#define _INCLUDE_ENCODE_H_ + +#include + +// typedef struct +// { +// uint8_t tones[FT8_NN]; +// // for waveform readout: +// int n_spsym; // Number of waveform samples per symbol +// float *pulse; // [3 * n_spsym] +// int idx_symbol; // Index of the current symbol +// float f0; // Base frequency, Hertz +// float signal_rate; // Waveform sample rate, Hertz +// } encoder_t; + +// void encoder_init(float signal_rate, float *pulse_buffer); +// void encoder_set_f0(float f0); +// void encoder_process(const message_t *message); // in: message +// void encoder_generate(float *block); // out: block of waveforms + +/// Generate FT8 tone sequence from payload data +/// @param[in] payload - 10 byte array consisting of 77 bit payload +/// @param[out] tones - array of FT8_NN (79) bytes to store the generated tones (encoded as 0..7) +void ft8_encode(const uint8_t* payload, uint8_t* tones); + +/// Generate FT4 tone sequence from payload data +/// @param[in] payload - 10 byte array consisting of 77 bit payload +/// @param[out] tones - array of FT4_NN (105) bytes to store the generated tones (encoded as 0..3) +void ft4_encode(const uint8_t* payload, uint8_t* tones); + +#endif // _INCLUDE_ENCODE_H_ diff --git a/ft8/pack.c b/ft8/pack.c new file mode 100644 index 00000000..c00a0d15 --- /dev/null +++ b/ft8/pack.c @@ -0,0 +1,366 @@ +#include "pack.h" +#include "text.h" + +#include +#include +#include +#include + +#define NTOKENS ((uint32_t)2063592L) +#define MAX22 ((uint32_t)4194304L) +#define MAXGRID4 ((uint16_t)32400) + +// TODO: This is wasteful, should figure out something more elegant +const char A0[] = " 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ+-./?"; +const char A1[] = " 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; +const char A2[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; +const char A3[] = "0123456789"; +const char A4[] = " ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + +// Pack a special token, a 22-bit hash code, or a valid base call +// into a 28-bit integer. +int32_t pack28(const char* callsign) +{ + // Check for special tokens first + if (starts_with(callsign, "DE ")) + return 0; + if (starts_with(callsign, "QRZ ")) + return 1; + if (starts_with(callsign, "CQ ")) + return 2; + + if (starts_with(callsign, "CQ_")) + { + int nnum = 0, nlet = 0; + + // TODO: + } + + // TODO: Check for <...> callsign + + char c6[6] = { ' ', ' ', ' ', ' ', ' ', ' ' }; + + int length = 0; // strlen(callsign); // We will need it later + while (callsign[length] != ' ' && callsign[length] != 0) + { + length++; + } + + // Copy callsign to 6 character buffer + if (starts_with(callsign, "3DA0") && length <= 7) + { + // Work-around for Swaziland prefix: 3DA0XYZ -> 3D0XYZ + memcpy(c6, "3D0", 3); + memcpy(c6 + 3, callsign + 4, length - 4); + } + else if (starts_with(callsign, "3X") && is_letter(callsign[2]) && length <= 7) + { + // Work-around for Guinea prefixes: 3XA0XYZ -> QA0XYZ + memcpy(c6, "Q", 1); + memcpy(c6 + 1, callsign + 2, length - 2); + } + else + { + if (is_digit(callsign[2]) && length <= 6) + { + // AB0XYZ + memcpy(c6, callsign, length); + } + else if (is_digit(callsign[1]) && length <= 5) + { + // A0XYZ -> " A0XYZ" + memcpy(c6 + 1, callsign, length); + } + } + + // Check for standard callsign + int i0, i1, i2, i3, i4, i5; + if ((i0 = char_index(A1, c6[0])) >= 0 && (i1 = char_index(A2, c6[1])) >= 0 && (i2 = char_index(A3, c6[2])) >= 0 && (i3 = char_index(A4, c6[3])) >= 0 && (i4 = char_index(A4, c6[4])) >= 0 && (i5 = char_index(A4, c6[5])) >= 0) + { + // This is a standard callsign + int32_t n28 = i0; + n28 = n28 * 36 + i1; + n28 = n28 * 10 + i2; + n28 = n28 * 27 + i3; + n28 = n28 * 27 + i4; + n28 = n28 * 27 + i5; + return NTOKENS + MAX22 + n28; + } + + //char text[13]; + //if (length > 13) return -1; + + // TODO: + // Treat this as a nonstandard callsign: compute its 22-bit hash + return -1; +} + +// Check if a string could be a valid standard callsign or a valid +// compound callsign. +// Return base call "bc" and a logical "cok" indicator. +bool chkcall(const char* call, char* bc) +{ + int length = strlen(call); // n1=len_trim(w) + if (length > 11) + return false; + if (0 != strchr(call, '.')) + return false; + if (0 != strchr(call, '+')) + return false; + if (0 != strchr(call, '-')) + return false; + if (0 != strchr(call, '?')) + return false; + if (length > 6 && 0 != strchr(call, '/')) + return false; + + // TODO: implement suffix parsing (or rework?) + + return true; +} + +uint16_t packgrid(const char* grid4) +{ + if (grid4 == 0) + { + // Two callsigns only, no report/grid + return MAXGRID4 + 1; + } + + // Take care of special cases + if (equals(grid4, "RRR")) + return MAXGRID4 + 2; + if (equals(grid4, "RR73")) + return MAXGRID4 + 3; + if (equals(grid4, "73")) + return MAXGRID4 + 4; + + // Check for standard 4 letter grid + if (in_range(grid4[0], 'A', 'R') && in_range(grid4[1], 'A', 'R') && is_digit(grid4[2]) && is_digit(grid4[3])) + { + uint16_t igrid4 = (grid4[0] - 'A'); + igrid4 = igrid4 * 18 + (grid4[1] - 'A'); + igrid4 = igrid4 * 10 + (grid4[2] - '0'); + igrid4 = igrid4 * 10 + (grid4[3] - '0'); + return igrid4; + } + + // Parse report: +dd / -dd / R+dd / R-dd + // TODO: check the range of dd + if (grid4[0] == 'R') + { + int dd = dd_to_int(grid4 + 1, 3); + uint16_t irpt = 35 + dd; + return (MAXGRID4 + irpt) | 0x8000; // ir = 1 + } + else + { + int dd = dd_to_int(grid4, 3); + uint16_t irpt = 35 + dd; + return (MAXGRID4 + irpt); // ir = 0 + } + + return MAXGRID4 + 1; +} + +// Pack Type 1 (Standard 77-bit message) and Type 2 (ditto, with a "/P" call) +int pack77_1(const char* msg, uint8_t* b77) +{ + // Locate the first delimiter + const char* s1 = strchr(msg, ' '); + if (s1 == 0) + return -1; + + const char* call1 = msg; // 1st call + const char* call2 = s1 + 1; // 2nd call + + int32_t n28a = pack28(call1); + int32_t n28b = pack28(call2); + + if (n28a < 0 || n28b < 0) + return -1; + + uint16_t igrid4; + + // Locate the second delimiter + const char* s2 = strchr(s1 + 1, ' '); + if (s2 != 0) + { + igrid4 = packgrid(s2 + 1); + } + else + { + // Two callsigns, no grid/report + igrid4 = packgrid(0); + } + + uint8_t i3 = 1; // No suffix or /R + + // TODO: check for suffixes + + // Shift in ipa and ipb bits into n28a and n28b + n28a <<= 1; // ipa = 0 + n28b <<= 1; // ipb = 0 + + // Pack into (28 + 1) + (28 + 1) + (1 + 15) + 3 bits + b77[0] = (n28a >> 21); + b77[1] = (n28a >> 13); + b77[2] = (n28a >> 5); + b77[3] = (uint8_t)(n28a << 3) | (uint8_t)(n28b >> 26); + b77[4] = (n28b >> 18); + b77[5] = (n28b >> 10); + b77[6] = (n28b >> 2); + b77[7] = (uint8_t)(n28b << 6) | (uint8_t)(igrid4 >> 10); + b77[8] = (igrid4 >> 2); + b77[9] = (uint8_t)(igrid4 << 6) | (uint8_t)(i3 << 3); + + return 0; +} + +void packtext77(const char* text, uint8_t* b77) +{ + int length = strlen(text); + + // Skip leading and trailing spaces + while (*text == ' ' && *text != 0) + { + ++text; + --length; + } + while (length > 0 && text[length - 1] == ' ') + { + --length; + } + + // Clear the first 72 bits representing a long number + for (int i = 0; i < 9; ++i) + { + b77[i] = 0; + } + + // Now express the text as base-42 number stored + // in the first 72 bits of b77 + for (int j = 0; j < 13; ++j) + { + // Multiply the long integer in b77 by 42 + uint16_t x = 0; + for (int i = 8; i >= 0; --i) + { + x += b77[i] * (uint16_t)42; + b77[i] = (x & 0xFF); + x >>= 8; + } + + // Get the index of the current char + if (j < length) + { + int q = char_index(A0, text[j]); + x = (q > 0) ? q : 0; + } + else + { + x = 0; + } + // Here we double each added number in order to have the result multiplied + // by two as well, so that it's a 71 bit number left-aligned in 72 bits (9 bytes) + x <<= 1; + + // Now add the number to our long number + for (int i = 8; i >= 0; --i) + { + if (x == 0) + break; + x += b77[i]; + b77[i] = (x & 0xFF); + x >>= 8; + } + } + + // Set n3=0 (bits 71..73) and i3=0 (bits 74..76) + b77[8] &= 0xFE; + b77[9] &= 0x00; +} + +int pack77(const char* msg, uint8_t* c77) +{ + // Check Type 1 (Standard 77-bit message) or Type 2, with optional "/P" + if (0 == pack77_1(msg, c77)) + { + return 0; + } + + // TODO: + // Check 0.5 (telemetry) + + // Check Type 4 (One nonstandard call and one hashed call) + + // Default to free text + // i3=0 n3=0 + packtext77(msg, c77); + return 0; +} + +#ifdef UNIT_TEST + +#include + +bool test1() +{ + const char* inputs[] = { + "", + " ", + "ABC", + "A9", + "L9A", + "L7BC", + "L0ABC", + "LL3JG", + "LL3AJG", + "CQ ", + 0 + }; + + for (int i = 0; inputs[i]; ++i) + { + int32_t result = ft8_v2::pack28(inputs[i]); + printf("pack28(\"%s\") = %d\n", inputs[i], result); + } + + return true; +} + +bool test2() +{ + const char* inputs[] = { + "CQ LL3JG", + "CQ LL3JG KO26", + "L0UAA LL3JG KO26", + "L0UAA LL3JG +02", + "L0UAA LL3JG RRR", + "L0UAA LL3JG 73", + 0 + }; + + for (int i = 0; inputs[i]; ++i) + { + uint8_t result[10]; + int rc = ft8_v2::pack77_1(inputs[i], result); + printf("pack77_1(\"%s\") = %d\t[", inputs[i], rc); + for (int j = 0; j < 10; ++j) + { + printf("%02x ", result[j]); + } + printf("]\n"); + } + + return true; +} + +int main() +{ + test1(); + test2(); + return 0; +} + +#endif \ No newline at end of file diff --git a/ft8/pack.h b/ft8/pack.h new file mode 100644 index 00000000..4c03c9ec --- /dev/null +++ b/ft8/pack.h @@ -0,0 +1,11 @@ +#ifndef _INCLUDE_PACK_H_ +#define _INCLUDE_PACK_H_ + +#include + +// Pack FT8 text message into 72 bits +// [IN] msg - FT8 message (e.g. "CQ TE5T KN01") +// [OUT] c77 - 10 byte array to store the 77 bit payload (MSB first) +int pack77(const char* msg, uint8_t* c77); + +#endif // _INCLUDE_PACK_H_ diff --git a/ft8/text.c b/ft8/text.c new file mode 100644 index 00000000..68393a91 --- /dev/null +++ b/ft8/text.c @@ -0,0 +1,253 @@ +#include "text.h" + +#include + +const char* trim_front(const char* str) +{ + // Skip leading whitespace + while (*str == ' ') + { + str++; + } + return str; +} + +void trim_back(char* str) +{ + // Skip trailing whitespace by replacing it with '\0' characters + int idx = strlen(str) - 1; + while (idx >= 0 && str[idx] == ' ') + { + str[idx--] = '\0'; + } +} + +// 1) trims a string from the back by changing whitespaces to '\0' +// 2) trims a string from the front by skipping whitespaces +char* trim(char* str) +{ + str = (char*)trim_front(str); + trim_back(str); + // return a pointer to the first non-whitespace character + return str; +} + +char to_upper(char c) +{ + return (c >= 'a' && c <= 'z') ? (c - 'a' + 'A') : c; +} + +bool is_digit(char c) +{ + return (c >= '0') && (c <= '9'); +} + +bool is_letter(char c) +{ + return ((c >= 'A') && (c <= 'Z')) || ((c >= 'a') && (c <= 'z')); +} + +bool is_space(char c) +{ + return (c == ' '); +} + +bool in_range(char c, char min, char max) +{ + return (c >= min) && (c <= max); +} + +bool starts_with(const char* string, const char* prefix) +{ + return 0 == memcmp(string, prefix, strlen(prefix)); +} + +bool equals(const char* string1, const char* string2) +{ + return 0 == strcmp(string1, string2); +} + +int char_index(const char* string, char c) +{ + for (int i = 0; *string; ++i, ++string) + { + if (c == *string) + { + return i; + } + } + return -1; // Not found +} + +// Text message formatting: +// - replaces lowercase letters with uppercase +// - merges consecutive spaces into single space +void fmtmsg(char* msg_out, const char* msg_in) +{ + char c; + char last_out = 0; + while ((c = *msg_in)) + { + if (c != ' ' || last_out != ' ') + { + last_out = to_upper(c); + *msg_out = last_out; + ++msg_out; + } + ++msg_in; + } + *msg_out = 0; // Add zero termination +} + +// Parse a 2 digit integer from string +int dd_to_int(const char* str, int length) +{ + int result = 0; + bool negative; + int i; + if (str[0] == '-') + { + negative = true; + i = 1; // Consume the - sign + } + else + { + negative = false; + i = (str[0] == '+') ? 1 : 0; // Consume a + sign if found + } + + while (i < length) + { + if (str[i] == 0) + break; + if (!is_digit(str[i])) + break; + result *= 10; + result += (str[i] - '0'); + ++i; + } + + return negative ? -result : result; +} + +// Convert a 2 digit integer to string +void int_to_dd(char* str, int value, int width, bool full_sign) +{ + if (value < 0) + { + *str = '-'; + ++str; + value = -value; + } + else if (full_sign) + { + *str = '+'; + ++str; + } + + int divisor = 1; + for (int i = 0; i < width - 1; ++i) + { + divisor *= 10; + } + + while (divisor >= 1) + { + int digit = value / divisor; + + *str = '0' + digit; + ++str; + + value -= digit * divisor; + divisor /= 10; + } + *str = 0; // Add zero terminator +} + +// convert integer index to ASCII character according to one of 6 tables: +// table 0: " 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ+-./?" +// table 1: " 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ" +// table 2: "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ" +// table 3: "0123456789" +// table 4: " ABCDEFGHIJKLMNOPQRSTUVWXYZ" +// table 5: " 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ/" +char charn(int c, int table_idx) +{ + if (table_idx != 2 && table_idx != 3) + { + if (c == 0) + return ' '; + c -= 1; + } + if (table_idx != 4) + { + if (c < 10) + return '0' + c; + c -= 10; + } + if (table_idx != 3) + { + if (c < 26) + return 'A' + c; + c -= 26; + } + + if (table_idx == 0) + { + if (c < 5) + return "+-./?"[c]; + } + else if (table_idx == 5) + { + if (c == 0) + return '/'; + } + + return '_'; // unknown character, should never get here +} + +// Convert character to its index (charn in reverse) according to a table +int nchar(char c, int table_idx) +{ + int n = 0; + if (table_idx != 2 && table_idx != 3) + { + if (c == ' ') + return n + 0; + n += 1; + } + if (table_idx != 4) + { + if (c >= '0' && c <= '9') + return n + (c - '0'); + n += 10; + } + if (table_idx != 3) + { + if (c >= 'A' && c <= 'Z') + return n + (c - 'A'); + n += 26; + } + + if (table_idx == 0) + { + if (c == '+') + return n + 0; + if (c == '-') + return n + 1; + if (c == '.') + return n + 2; + if (c == '/') + return n + 3; + if (c == '?') + return n + 4; + } + else if (table_idx == 5) + { + if (c == '/') + return n + 0; + } + + // Character not found + return -1; +} diff --git a/ft8/text.h b/ft8/text.h new file mode 100644 index 00000000..aac99213 --- /dev/null +++ b/ft8/text.h @@ -0,0 +1,37 @@ +#ifndef _INCLUDE_TEXT_H_ +#define _INCLUDE_TEXT_H_ + +#include +#include + +// Utility functions for characters and strings + +const char* trim_front(const char* str); +void trim_back(char* str); +char* trim(char* str); + +char to_upper(char c); +bool is_digit(char c); +bool is_letter(char c); +bool is_space(char c); +bool in_range(char c, char min, char max); +bool starts_with(const char* string, const char* prefix); +bool equals(const char* string1, const char* string2); + +int char_index(const char* string, char c); + +// Text message formatting: +// - replaces lowercase letters with uppercase +// - merges consecutive spaces into single space +void fmtmsg(char* msg_out, const char* msg_in); + +// Parse a 2 digit integer from string +int dd_to_int(const char* str, int length); + +// Convert a 2 digit integer to string +void int_to_dd(char* str, int value, int width, bool full_sign); + +char charn(int c, int table_idx); +int nchar(char c, int table_idx); + +#endif // _INCLUDE_TEXT_H_ diff --git a/gui_bar.cpp b/gui_bar.cpp index 38f8d7d3..8c058ec0 100644 --- a/gui_bar.cpp +++ b/gui_bar.cpp @@ -155,7 +155,8 @@ static void if_slider_event_cb(lv_event_t *e) { lv_obj_t *slider = lv_event_get_target(e); lv_label_set_text_fmt(gbar.get_if_slider_label(), "if %d db", lv_slider_get_value(slider)); - gbar.m_if = 10 * lv_slider_get_value(slider); + int sl = lv_slider_get_value(slider); + gbar.m_if = std::pow(10.0, (float)sl / 20.0); Settings_file.save_ifgain(lv_slider_get_value(slider)); } @@ -467,9 +468,15 @@ void gui_bar::init(lv_obj_t *o_parent, lv_group_t *button_group, int mode, lv_co { if (SdrDevices.SdrDevices.at(default_radio)->rx_channels.at(default_rx_channel)->get_agc()) { - bool bAgc = SdrDevices.SdrDevices.at(default_radio)->getGainMode(SOAPY_SDR_RX, default_rx_channel); - if (bAgc) - lv_obj_add_state(button[9], LV_STATE_CHECKED); + string sagc = Settings_file.get_string(default_radio, "AGC"); + if (sagc == "off") + SdrDevices.SdrDevices.at(default_radio)->setGainMode(SOAPY_SDR_RX, default_rx_channel, false); + else + { + bool bAgc = SdrDevices.SdrDevices.at(default_radio)->getGainMode(SOAPY_SDR_RX, default_rx_channel); + if (bAgc) + lv_obj_add_state(button[9], LV_STATE_CHECKED); + } } } catch (const std::exception& e) @@ -646,10 +653,10 @@ float gui_bar::get_if() void gui_bar::set_if(int rf) { + m_if = std::pow(10.0, (float)rf / 20.0); lv_slider_set_value(if_slider, rf, LV_ANIM_ON); lv_label_set_text_fmt(if_slider_label, "if %d db", rf); Settings_file.save_ifgain(rf); - m_if = std::pow(10.0,(float)rf / 20.0); } void gui_bar::get_filter_range(vector &filters) diff --git a/gui_setup.cpp b/gui_setup.cpp index 77745f73..8c90298d 100644 --- a/gui_setup.cpp +++ b/gui_setup.cpp @@ -23,10 +23,32 @@ static void receivers_button_handler(lv_event_t * e) } } +static void contour_slider_event_cb(lv_event_t *e) +{ + lv_event_code_t code = lv_event_get_code(e); + lv_obj_t *obj = lv_event_get_target(e); + + int i = lv_slider_get_value(obj); + if (i > 0) + { + gsetup.set_contour_value(i); + } +} + +static void floor_slider_event_cb(lv_event_t *e) +{ + lv_event_code_t code = lv_event_get_code(e); + lv_obj_t *obj = lv_event_get_target(e); + + int i = lv_slider_get_value(obj); + if (i > 0) + { + gsetup.set_floor_value(i); + } +} + static void span_slider_event_cb(lv_event_t * e) { - char buf[20]; - lv_event_code_t code = lv_event_get_code(e); lv_obj_t *obj = lv_event_get_target(e); @@ -258,29 +280,52 @@ void gui_setup::init(lv_obj_t* o_tab, lv_coord_t w) } } } - //int span_y = 15 + y_margin + button_height_margin; + + check_cw = lv_checkbox_create(o_tab); + lv_group_add_obj(m_button_group, check_cw); + lv_checkbox_set_text(check_cw, "Morse Decoder"); + lv_obj_add_event_cb(check_cw, event_handler_morse, LV_EVENT_ALL, NULL); + lv_obj_align(check_cw, LV_ALIGN_TOP_LEFT, 4 * button_width_margin, y_margin + ibutton_y * button_height_margin); + ibutton_y++; int y_span = y_margin + ibutton_y * button_height_margin + button_height_margin /2; + int brightness_y = 15 + y_margin + 2 * button_height_margin; span_slider = lv_slider_create(o_tab); lv_group_add_obj(m_button_group, span_slider); lv_obj_set_width(span_slider, w / 2 - 50); //lv_obj_center(span_slider); - lv_obj_align(span_slider, LV_ALIGN_TOP_MID, 0, y_span); + //lv_obj_align(span_slider, LV_ALIGN_TOP_MID, 0, y_span); + lv_obj_align_to(span_slider, o_tab, LV_ALIGN_TOP_LEFT, 0, y_span); lv_obj_add_event_cb(span_slider, span_slider_event_cb, LV_EVENT_VALUE_CHANGED, NULL); span_slider_label = lv_label_create(o_tab); - lv_label_set_text(span_slider_label, "span"); - lv_obj_align_to(span_slider_label, span_slider, LV_ALIGN_OUT_TOP_MID, -30, -10); + lv_label_set_text(span_slider_label, "span 500 Khz"); + //lv_obj_align_to(span_slider_label, span_slider, LV_ALIGN_OUT_TOP_MID, -30, -10); + lv_obj_align_to(span_slider_label, span_slider, LV_ALIGN_OUT_TOP_MID, 0, -10); + string span = Settings_file.find_radio("span"); int i = atoi(span.c_str()); if (((i * 1000) > (ifrate / 2)) || i == 0) i = ifrate / 2000; set_span_range(ifrate/2); set_span_value(i * 1000); + + //lv_obj_t *contour_slider_label, *contour_slider; + //lv_obj_t *floor_slider_label, *floor_slider; + + contour_slider = lv_slider_create(o_tab); + lv_group_add_obj(m_button_group, contour_slider); + lv_obj_set_width(contour_slider, w / 2 - 50); + lv_obj_align_to(contour_slider, o_tab, LV_ALIGN_TOP_LEFT, w / 2, y_span); + lv_slider_set_range(contour_slider, 1, 10); - int brightness_y = 15 + y_margin + 2* button_height_margin; + lv_obj_add_event_cb(contour_slider, contour_slider_event_cb, LV_EVENT_VALUE_CHANGED, NULL); + contour_slider_label = lv_label_create(o_tab); + lv_label_set_text(contour_slider_label, "speed 1"); + lv_obj_align_to(contour_slider_label, contour_slider, LV_ALIGN_OUT_TOP_MID, 0, -10); + brightness_slider = lv_slider_create(o_tab); lv_group_add_obj(m_button_group, brightness_slider); lv_obj_set_width(brightness_slider, w / 2 - 50); @@ -293,12 +338,17 @@ void gui_setup::init(lv_obj_t* o_tab, lv_coord_t w) lv_label_set_text(brightness_slider_label, "brightness"); lv_obj_align_to(brightness_slider_label, brightness_slider, LV_ALIGN_OUT_TOP_MID, 0, -10); - check_cw = lv_checkbox_create(o_tab); - lv_group_add_obj(m_button_group, check_cw); - lv_checkbox_set_text(check_cw, "Morse Decoder"); - lv_obj_add_event_cb(check_cw, event_handler_morse, LV_EVENT_ALL, NULL); - lv_obj_align_to(check_cw, d_samplerate, LV_ALIGN_OUT_BOTTOM_LEFT, 0, 10); - //lv_obj_add_state(check_cw, LV_STATE_CHECKED); + floor_slider = lv_slider_create(o_tab); + lv_group_add_obj(m_button_group, floor_slider); + lv_obj_set_width(floor_slider, w / 2 - 50); + lv_obj_align_to(floor_slider, span_slider, LV_ALIGN_OUT_BOTTOM_MID, w / 2, 40); + lv_slider_set_range(floor_slider, 1, 20); + + lv_obj_add_event_cb(floor_slider, floor_slider_event_cb, LV_EVENT_VALUE_CHANGED, NULL); + floor_slider_label = lv_label_create(o_tab); + lv_label_set_text(floor_slider_label, "Noise floor 1"); + lv_obj_align_to(floor_slider_label, floor_slider, LV_ALIGN_OUT_TOP_MID, 0, -10); + lv_group_add_obj(m_button_group, lv_tabview_get_tab_btns(tabview_mid)); } @@ -376,6 +426,27 @@ void gui_setup::set_brightness(int brightness) myfile << std::string(buf); } +void gui_setup::set_contour_value(int speed) +{ + char buf[20]; + + sprintf(buf, " speed %d ",speed); + lv_label_set_text(contour_slider_label, buf); +} + +int gui_setup::get_contour_value() +{ + return lv_slider_get_value(contour_slider); +} + +void gui_setup::set_floor_value(int speed) +{ + char buf[20]; + + sprintf(buf, " noise floor %d ", speed); + lv_label_set_text(floor_slider_label, buf); +} + int gui_setup::get_brightness() { int brightness; diff --git a/gui_setup.h b/gui_setup.h index 2ebbab89..5db43281 100644 --- a/gui_setup.h +++ b/gui_setup.h @@ -31,7 +31,10 @@ class gui_setup void set_cw(bool bcw); void toggle_cw(); void set_group(); - + void set_contour_value(int speed); + void set_floor_value(int floor); + int get_contour_value(); + double m_ifrate; private: @@ -44,6 +47,8 @@ class gui_setup lv_obj_t* d_audio, *check_cw; atomic m_span; lv_group_t *m_button_group{nullptr}; + lv_obj_t *contour_slider_label, *contour_slider; + lv_obj_t *floor_slider_label, *floor_slider; }; extern gui_setup gsetup; \ No newline at end of file diff --git a/install/README b/install/README deleted file mode 100644 index f848a7fc..00000000 --- a/install/README +++ /dev/null @@ -1,3804 +0,0 @@ -Introduction -============ - -This directory contains Device Tree overlays. Device Tree makes it possible -to support many hardware configurations with a single kernel and without the -need to explicitly load or blacklist kernel modules. Note that this isn't a -"pure" Device Tree configuration (c.f. MACH_BCM2835) - some on-board devices -are still configured by the board support code, but the intention is to -eventually reach that goal. - -On Raspberry Pi, Device Tree usage is controlled from /boot/config.txt. By -default, the Raspberry Pi kernel boots with device tree enabled. You can -completely disable DT usage (for now) by adding: - - device_tree= - -to your config.txt, which should cause your Pi to revert to the old way of -doing things after a reboot. - -In /boot you will find a .dtb for each base platform. This describes the -hardware that is part of the Raspberry Pi board. The loader (start.elf and its -siblings) selects the .dtb file appropriate for the platform by name, and reads -it into memory. At this point, all of the optional interfaces (i2c, i2s, spi) -are disabled, but they can be enabled using Device Tree parameters: - - dtparam=i2c=on,i2s=on,spi=on - -However, this shouldn't be necessary in many use cases because loading an -overlay that requires one of those interfaces will cause it to be enabled -automatically, and it is advisable to only enable interfaces if they are -needed. - -Configuring additional, optional hardware is done using Device Tree overlays -(see below). - -GPIO numbering uses the hardware pin numbering scheme (aka BCM scheme) and -not the physical pin numbers. - -raspi-config -============ - -The Advanced Options section of the raspi-config utility can enable and disable -Device Tree use, as well as toggling the I2C and SPI interfaces. Note that it -is possible to both enable an interface and blacklist the driver, if for some -reason you should want to defer the loading. - -Modules -======= - -As well as describing the hardware, Device Tree also gives enough information -to allow suitable driver modules to be located and loaded, with the corollary -that unneeded modules are not loaded. As a result it should be possible to -remove lines from /etc/modules, and /etc/modprobe.d/raspi-blacklist.conf can -have its contents deleted (or commented out). - -Using Overlays -============== - -Overlays are loaded using the "dtoverlay" config.txt setting. As an example, -consider I2C Real Time Clock drivers. In the pre-DT world these would be loaded -by writing a magic string comprising a device identifier and an I2C address to -a special file in /sys/class/i2c-adapter, having first loaded the driver for -the I2C interface and the RTC device - something like this: - - modprobe i2c-bcm2835 - modprobe rtc-ds1307 - echo ds1307 0x68 > /sys/class/i2c-adapter/i2c-1/new_device - -With DT enabled, this becomes a line in config.txt: - - dtoverlay=i2c-rtc,ds1307 - -This causes the file /boot/overlays/i2c-rtc.dtbo to be loaded and a "node" -describing the DS1307 I2C device to be added to the Device Tree for the Pi. By -default it usees address 0x68, but this can be modified with an additional DT -parameter: - - dtoverlay=i2c-rtc,ds1307,addr=0x68 - -Parameters usually have default values, although certain parameters are -mandatory. See the list of overlays below for a description of the parameters -and their defaults. - -Making new Overlays based on existing Overlays -============================================== - -Recent overlays have been designed in a more general way, so that they can be -adapted to hardware by changing their parameters. When you have additional -hardware with more than one device of a kind, you end up using the same overlay -multiple times with other parameters, e.g. - - # 2 CAN FD interfaces on spi but with different pins - dtoverlay=mcp251xfd,spi0-0,interrupt=25 - dtoverlay=mcp251xfd,spi0-1,interrupt=24 - - # a realtime clock on i2c - dtoverlay=i2c-rtc,pcf85063 - -While this approach does work, it requires knowledge about the hardware design. -It is more feasible to simplify things for the end user by providing a single -overlay as it is done the traditional way. - -A new overlay can be generated by using ovmerge utility. -https://github.com/raspberrypi/utils/blob/master/ovmerge/ovmerge - -To generate an overlay for the above configuration we pass the configuration -to ovmerge and add the -c flag. - - ovmerge -c mcp251xfd-overlay.dts,spi0-0,interrupt=25 \ - mcp251xfd-overlay.dts,spi0-1,interrupt=24 \ - i2c-rtc-overlay.dts,pcf85063 \ - >> merged-overlay.dts - -The -c option writes the command above as a comment into the overlay as -a marker that this overlay is generated and how it was generated. -After compiling the overlay it can be loaded in a single line. - - dtoverlay=merged - -It does the same as the original configuration but without parameters. - -The Overlay and Parameter Reference -=================================== - -N.B. When editing this file, please preserve the indentation levels to make it -simple to parse programmatically. NO HARD TABS. - - -Name: -Info: Configures the base Raspberry Pi hardware -Load: -Params: - ant1 Select antenna 1 (default). CM4 only. - - ant2 Select antenna 2. CM4 only. - - noant Disable both antennas. CM4 only. - - audio Set to "on" to enable the onboard ALSA audio - interface (default "off") - - axiperf Set to "on" to enable the AXI bus performance - monitors. - See /sys/kernel/debug/raspberrypi_axi_monitor - for the results. - - cam0_reg Enables CAM 0 regulator. CM1 & 3 only. - - cam0_reg_gpio Set GPIO for CAM 0 regulator. Default 30. - CM1 & 3 only. - - cam1_reg Enables CAM 1 regulator. CM1 & 3 only. - - cam1_reg_gpio Set GPIO for CAM 1 regulator. Default 2. - CM1 & 3 only. - - eee Enable Energy Efficient Ethernet support for - compatible devices (default "on"). See also - "tx_lpi_timer". Pi3B+ only. - - eth_downshift_after Set the number of auto-negotiation failures - after which the 1000Mbps modes are disabled. - Legal values are 2, 3, 4, 5 and 0, where - 0 means never downshift (default 2). Pi3B+ only. - - eth_led0 Set mode of LED0 - amber on Pi3B+ (default "1"), - green on Pi4 (default "0"). - The legal values are: - - Pi3B+ - - 0=link/activity 1=link1000/activity - 2=link100/activity 3=link10/activity - 4=link100/1000/activity 5=link10/1000/activity - 6=link10/100/activity 14=off 15=on - - Pi4 - - 0=Speed/Activity 1=Speed - 2=Flash activity 3=FDX - 4=Off 5=On - 6=Alt 7=Speed/Flash - 8=Link 9=Activity - - eth_led1 Set mode of LED1 - green on Pi3B+ (default "6"), - amber on Pi4 (default "8"). See eth_led0 for - legal values. - - eth_max_speed Set the maximum speed a link is allowed - to negotiate. Legal values are 10, 100 and - 1000 (default 1000). Pi3B+ only. - - i2c_arm Set to "on" to enable the ARM's i2c interface - (default "off") - - i2c_vc Set to "on" to enable the i2c interface - usually reserved for the VideoCore processor - (default "off") - - i2c An alias for i2c_arm - - i2c_arm_baudrate Set the baudrate of the ARM's i2c interface - (default "100000") - - i2c_vc_baudrate Set the baudrate of the VideoCore i2c interface - (default "100000") - - i2c_baudrate An alias for i2c_arm_baudrate - - i2s Set to "on" to enable the i2s interface - (default "off") - - krnbt Set to "on" to enable autoprobing of Bluetooth - driver without need of hciattach/btattach - (default "off") - - krnbt_baudrate Set the baudrate of the PL011 UART when used - with krnbt=on - - spi Set to "on" to enable the spi interfaces - (default "off") - - spi_dma4 Use to enable 40-bit DMA on spi interfaces - (the assigned value doesn't matter) - (2711 only) - - random Set to "on" to enable the hardware random - number generator (default "on") - - sd_overclock Clock (in MHz) to use when the MMC framework - requests 50MHz - - sd_poll_once Looks for a card once after booting. Useful - for network booting scenarios to avoid the - overhead of continuous polling. N.B. Using - this option restricts the system to using a - single card per boot (or none at all). - (default off) - - sd_force_pio Disable DMA support for SD driver (default off) - - sd_pio_limit Number of blocks above which to use DMA for - SD card (default 1) - - sd_debug Enable debug output from SD driver (default off) - - sdio_overclock Clock (in MHz) to use when the MMC framework - requests 50MHz for the SDIO/WLAN interface. - - tx_lpi_timer Set the delay in microseconds between going idle - and entering the low power state (default 600). - Requires EEE to be enabled - see "eee". - - uart0 Set to "off" to disable uart0 (default "on") - - uart1 Set to "on" or "off" to enable or disable uart1 - (default varies) - - watchdog Set to "on" to enable the hardware watchdog - (default "off") - - act_led_trigger Choose which activity the LED tracks. - Use "heartbeat" for a nice load indicator. - (default "mmc") - - act_led_activelow Set to "on" to invert the sense of the LED - (default "off") - N.B. For Pi 3B, 3B+, 3A+ and 4B, use the act-led - overlay. - - act_led_gpio Set which GPIO to use for the activity LED - (in case you want to connect it to an external - device) - (default "16" on a non-Plus board, "47" on a - Plus or Pi 2) - N.B. For Pi 3B, 3B+, 3A+ and 4B, use the act-led - overlay. - - pwr_led_trigger - pwr_led_activelow - pwr_led_gpio - As for act_led_*, but using the PWR LED. - Not available on Model A/B boards. - - N.B. It is recommended to only enable those interfaces that are needed. - Leaving all interfaces enabled can lead to unwanted behaviour (i2c_vc - interfering with Pi Camera, I2S and SPI hogging GPIO pins, etc.) - Note also that i2c, i2c_arm and i2c_vc are aliases for the physical - interfaces i2c0 and i2c1. Use of the numeric variants is still possible - but deprecated because the ARM/VC assignments differ between board - revisions. The same board-specific mapping applies to i2c_baudrate, - and the other i2c baudrate parameters. - - -Name: act-led -Info: Pi 3B, 3B+, 3A+ and 4B use a GPIO expander to drive the LEDs which can - only be accessed from the VPU. There is a special driver for this with a - separate DT node, which has the unfortunate consequence of breaking the - act_led_gpio and act_led_activelow dtparams. - This overlay changes the GPIO controller back to the standard one and - restores the dtparams. -Load: dtoverlay=act-led,= -Params: activelow Set to "on" to invert the sense of the LED - (default "off") - - gpio Set which GPIO to use for the activity LED - (in case you want to connect it to an external - device) - REQUIRED - - -Name: adafruit-st7735r -Info: Overlay for the SPI-connected Adafruit 1.8" 160x128 or 128x128 displays, - based on the ST7735R chip. - This overlay uses the newer DRM/KMS "Tiny" driver. -Load: dtoverlay=adafruit-st7735r,= -Params: 128x128 Select the 128x128 driver (default 160x128) - rotate Display rotation {0,90,180,270} (default 90) - speed SPI bus speed in Hz (default 4000000) - dc_pin GPIO pin for D/C (default 24) - reset_pin GPIO pin for RESET (default 25) - led_pin GPIO used to control backlight (default 18) - - -Name: adafruit18 -Info: Overlay for the SPI-connected Adafruit 1.8" display (based on the - ST7735R chip). It includes support for the "green tab" version. - This overlay uses the older fbtft driver. -Load: dtoverlay=adafruit18,= -Params: green Use the adafruit18_green variant. - rotate Display rotation {0,90,180,270} - speed SPI bus speed in Hz (default 4000000) - fps Display frame rate in Hz - bgr Enable BGR mode (default off) - debug Debug output level {0-7} - dc_pin GPIO pin for D/C (default 24) - reset_pin GPIO pin for RESET (default 25) - led_pin GPIO used to control backlight (default 18) - - -Name: adau1977-adc -Info: Overlay for activation of ADAU1977 ADC codec over I2C for control - and I2S for data. -Load: dtoverlay=adau1977-adc -Params: - - -Name: adau7002-simple -Info: Overlay for the activation of ADAU7002 stereo PDM to I2S converter. -Load: dtoverlay=adau7002-simple,= -Params: card-name Override the default, "adau7002", card name. - - -Name: ads1015 -Info: Overlay for activation of Texas Instruments ADS1015 ADC over I2C -Load: dtoverlay=ads1015,= -Params: addr I2C bus address of device. Set based on how the - addr pin is wired. (default=0x48 assumes addr - is pulled to GND) - cha_enable Enable virtual channel a. (default=true) - cha_cfg Set the configuration for virtual channel a. - (default=4 configures this channel for the - voltage at A0 with respect to GND) - cha_datarate Set the datarate (samples/sec) for this channel. - (default=4 sets 1600 sps) - cha_gain Set the gain of the Programmable Gain - Amplifier for this channel. (default=2 sets the - full scale of the channel to 2.048 Volts) - - Channel (ch) parameters can be set for each enabled channel. - A maximum of 4 channels can be enabled (letters a thru d). - For more information refer to the device datasheet at: - http://www.ti.com/lit/ds/symlink/ads1015.pdf - - -Name: ads1115 -Info: Texas Instruments ADS1115 ADC -Load: dtoverlay=ads1115,[=] -Params: addr I2C bus address of device. Set based on how the - addr pin is wired. (default=0x48 assumes addr - is pulled to GND) - cha_enable Enable virtual channel a. - cha_cfg Set the configuration for virtual channel a. - (default=4 configures this channel for the - voltage at A0 with respect to GND) - cha_datarate Set the datarate (samples/sec) for this channel. - (default=7 sets 860 sps) - cha_gain Set the gain of the Programmable Gain - Amplifier for this channel. (Default 1 sets the - full scale of the channel to 4.096 Volts) - - Channel parameters can be set for each enabled channel. - A maximum of 4 channels can be enabled (letters a thru d). - For more information refer to the device datasheet at: - http://www.ti.com/lit/ds/symlink/ads1115.pdf - - -Name: ads7846 -Info: ADS7846 Touch controller -Load: dtoverlay=ads7846,= -Params: cs SPI bus Chip Select (default 1) - speed SPI bus speed (default 2MHz, max 3.25MHz) - penirq GPIO used for PENIRQ. REQUIRED - penirq_pull Set GPIO pull (default 0=none, 2=pullup) - swapxy Swap x and y axis - xmin Minimum value on the X axis (default 0) - ymin Minimum value on the Y axis (default 0) - xmax Maximum value on the X axis (default 4095) - ymax Maximum value on the Y axis (default 4095) - pmin Minimum reported pressure value (default 0) - pmax Maximum reported pressure value (default 65535) - xohms Touchpanel sensitivity (X-plate resistance) - (default 400) - - penirq is required and usually xohms (60-100) has to be set as well. - Apart from that, pmax (255) and swapxy are also common. - The rest of the calibration can be done with xinput-calibrator. - See: github.com/notro/fbtft/wiki/FBTFT-on-Raspian - Device Tree binding document: - www.kernel.org/doc/Documentation/devicetree/bindings/input/ads7846.txt - - -Name: adv7282m -Info: Analog Devices ADV7282M analogue video to CSI2 bridge. - Uses Unicam1, which is the standard camera connector on most Pi - variants. -Load: dtoverlay=adv7282m,= -Params: addr Overrides the I2C address (default 0x21) - media-controller Configure use of Media Controller API for - configuring the sensor (default off) - - -Name: adv728x-m -Info: Analog Devices ADV728[0|1|2]-M analogue video to CSI2 bridges. - This is a wrapper for adv7282m, and defaults to ADV7282M. -Load: dtoverlay=adv728x-m,= -Params: addr Overrides the I2C address (default 0x21) - adv7280m Select ADV7280-M. - adv7281m Select ADV7281-M. - adv7281ma Select ADV7281-MA. - media-controller Configure use of Media Controller API for - configuring the sensor (default off) - - -Name: akkordion-iqdacplus -Info: Configures the Digital Dreamtime Akkordion Music Player (based on the - OEM IQAudIO DAC+ or DAC Zero module). -Load: dtoverlay=akkordion-iqdacplus,= -Params: 24db_digital_gain Allow gain to be applied via the PCM512x codec - Digital volume control. Enable with - dtoverlay=akkordion-iqdacplus,24db_digital_gain - (The default behaviour is that the Digital - volume control is limited to a maximum of - 0dB. ie. it can attenuate but not provide - gain. For most users, this will be desired - as it will prevent clipping. By appending - the 24db_digital_gain parameter, the Digital - volume control will allow up to 24dB of - gain. If this parameter is enabled, it is the - responsibility of the user to ensure that - the Digital volume control is set to a value - that does not result in clipping/distortion!) - - -Name: allo-boss-dac-pcm512x-audio -Info: Configures the Allo Boss DAC audio cards. -Load: dtoverlay=allo-boss-dac-pcm512x-audio, -Params: 24db_digital_gain Allow gain to be applied via the PCM512x codec - Digital volume control. Enable with - "dtoverlay=allo-boss-dac-pcm512x-audio, - 24db_digital_gain" - (The default behaviour is that the Digital - volume control is limited to a maximum of - 0dB. ie. it can attenuate but not provide - gain. For most users, this will be desired - as it will prevent clipping. By appending - the 24db_digital_gain parameter, the Digital - volume control will allow up to 24dB of - gain. If this parameter is enabled, it is the - responsibility of the user to ensure that - the Digital volume control is set to a value - that does not result in clipping/distortion!) - slave Force Boss DAC into slave mode, using Pi a - master for bit clock and frame clock. Enable - with "dtoverlay=allo-boss-dac-pcm512x-audio, - slave" - - -Name: allo-boss2-dac-audio -Info: Configures the Allo Boss2 DAC audio card -Load: dtoverlay=allo-boss2-dac-audio -Params: - - -Name: allo-digione -Info: Configures the Allo Digione audio card -Load: dtoverlay=allo-digione -Params: - - -Name: allo-katana-dac-audio -Info: Configures the Allo Katana DAC audio card -Load: dtoverlay=allo-katana-dac-audio -Params: - - -Name: allo-piano-dac-pcm512x-audio -Info: Configures the Allo Piano DAC (2.0/2.1) audio cards. - (NB. This initial support is for 2.0 channel audio ONLY! ie. stereo. - The subwoofer outputs on the Piano 2.1 are not currently supported!) -Load: dtoverlay=allo-piano-dac-pcm512x-audio, -Params: 24db_digital_gain Allow gain to be applied via the PCM512x codec - Digital volume control. - (The default behaviour is that the Digital - volume control is limited to a maximum of - 0dB. ie. it can attenuate but not provide - gain. For most users, this will be desired - as it will prevent clipping. By appending - the 24db_digital_gain parameter, the Digital - volume control will allow up to 24dB of - gain. If this parameter is enabled, it is the - responsibility of the user to ensure that - the Digital volume control is set to a value - that does not result in clipping/distortion!) - - -Name: allo-piano-dac-plus-pcm512x-audio -Info: Configures the Allo Piano DAC (2.1) audio cards. -Load: dtoverlay=allo-piano-dac-plus-pcm512x-audio, -Params: 24db_digital_gain Allow gain to be applied via the PCM512x codec - Digital volume control. - (The default behaviour is that the Digital - volume control is limited to a maximum of - 0dB. ie. it can attenuate but not provide - gain. For most users, this will be desired - as it will prevent clipping. By appending - the 24db_digital_gain parameter, the Digital - volume control will allow up to 24dB of - gain. If this parameter is enabled, it is the - responsibility of the user to ensure that - the Digital volume control is set to a value - that does not result in clipping/distortion!) - glb_mclk This option is only with Kali board. If enabled, - MCLK for Kali is used and PLL is disabled for - better voice quality. (default Off) - - -Name: anyspi -Info: Universal device tree overlay for SPI devices - - Just specify the SPI address and device name ("compatible" property). - This overlay lacks any device-specific parameter support! - - For devices on spi1 or spi2, the interfaces should be enabled - with one of the spi1-1/2/3cs and/or spi2-1/2/3cs overlays. - - Examples: - 1. SPI NOR flash on spi0.1, maximum SPI clock frequency 45MHz: - dtoverlay=anyspi:spi0-1,dev="jedec,spi-nor",speed=45000000 - 2. MCP3204 ADC on spi1.2, maximum SPI clock frequency 500kHz: - dtoverlay=anyspi:spi1-2,dev="microchip,mcp3204" -Load: dtoverlay=anyspi,= -Params: spi- Configure device at spi, cs - (boolean, required) - dev Set device name to search compatible module - (string, required) - speed Set SPI clock frequency in Hz - (integer, optional, default 500000) - - -Name: apds9960 -Info: Configures the AVAGO APDS9960 digital proximity, ambient light, RGB and - gesture sensor -Load: dtoverlay=apds9960,= -Params: gpiopin GPIO used for INT (default 4) - noints Disable the interrupt GPIO line. - - -Name: applepi-dac -Info: Configures the Orchard Audio ApplePi-DAC audio card -Load: dtoverlay=applepi-dac -Params: - - -Name: at86rf233 -Info: Configures the Atmel AT86RF233 802.15.4 low-power WPAN transceiver, - connected to spi0.0 -Load: dtoverlay=at86rf233,= -Params: interrupt GPIO used for INT (default 23) - reset GPIO used for Reset (default 24) - sleep GPIO used for Sleep (default 25) - speed SPI bus speed in Hz (default 3000000) - trim Fine tuning of the internal capacitance - arrays (0=+0pF, 15=+4.5pF, default 15) - - -Name: audioinjector-addons -Info: Configures the audioinjector.net audio add on soundcards -Load: dtoverlay=audioinjector-addons,= -Params: non-stop-clocks Keeps the clocks running even when the stream - is paused or stopped (default off) - - -Name: audioinjector-isolated-soundcard -Info: Configures the audioinjector.net isolated soundcard -Load: dtoverlay=audioinjector-isolated-soundcard -Params: - - -Name: audioinjector-ultra -Info: Configures the audioinjector.net ultra soundcard -Load: dtoverlay=audioinjector-ultra -Params: - - -Name: audioinjector-wm8731-audio -Info: Configures the audioinjector.net audio add on soundcard -Load: dtoverlay=audioinjector-wm8731-audio -Params: - - -Name: audiosense-pi -Info: Configures the audiosense-pi add on soundcard - For more information refer to - https://gitlab.com/kakar0t/audiosense-pi -Load: dtoverlay=audiosense-pi -Params: - - -Name: audremap -Info: Switches PWM sound output to GPIOs on the 40-pin header -Load: dtoverlay=audremap,= -Params: swap_lr Reverse the channel allocation, which will also - swap the audio jack outputs (default off) - enable_jack Don't switch off the audio jack output - (default off) - pins_12_13 Select GPIOs 12 & 13 (default) - pins_18_19 Select GPIOs 18 & 19 - - -Name: balena-fin -Info: Overlay that enables WLAN, Bluetooth and the GPIO expander on the - balenaFin carrier board for the Raspberry Pi Compute Module 3/3+ Lite. -Load: dtoverlay=balena-fin -Params: - - -Name: bmp085_i2c-sensor -Info: This overlay is now deprecated - see i2c-sensor -Load: - - -Name: cap1106 -Info: Enables the ability to use the cap1106 touch sensor as a keyboard -Load: dtoverlay=cap1106,= -Params: int_pin GPIO pin for interrupt signal (default 23) - - -Name: chipdip-dac -Info: Configures Chip Dip audio cards. -Load: dtoverlay=chipdip-dac -Params: - - -Name: cma -Info: Set custom CMA sizes, only use if you know what you are doing, might - clash with other overlays like vc4-fkms-v3d and vc4-kms-v3d. -Load: dtoverlay=cma,= -Params: cma-512 CMA is 512MB (needs 1GB) - cma-448 CMA is 448MB (needs 1GB) - cma-384 CMA is 384MB (needs 1GB) - cma-320 CMA is 320MB (needs 1GB) - cma-256 CMA is 256MB (needs 1GB) - cma-192 CMA is 192MB (needs 1GB) - cma-128 CMA is 128MB - cma-96 CMA is 96MB - cma-64 CMA is 64MB - cma-size CMA size in bytes, 4MB aligned - cma-default Use upstream's default value - - -Name: cutiepi-panel -Info: 8" TFT LCD display and touch panel used by cutiepi.io -Load: dtoverlay=cutiepi-panel -Params: - - -Name: dht11 -Info: Overlay for the DHT11/DHT21/DHT22 humidity/temperature sensors - Also sometimes found with the part number(s) AM230x. -Load: dtoverlay=dht11,= -Params: gpiopin GPIO connected to the sensor's DATA output. - (default 4) - - -Name: dionaudio-loco -Info: Configures the Dion Audio LOCO DAC-AMP -Load: dtoverlay=dionaudio-loco -Params: - - -Name: dionaudio-loco-v2 -Info: Configures the Dion Audio LOCO-V2 DAC-AMP -Load: dtoverlay=dionaudio-loco-v2,= -Params: 24db_digital_gain Allow gain to be applied via the PCM512x codec - Digital volume control. Enable with - "dtoverlay=hifiberry-dacplus,24db_digital_gain" - (The default behaviour is that the Digital - volume control is limited to a maximum of - 0dB. ie. it can attenuate but not provide - gain. For most users, this will be desired - as it will prevent clipping. By appending - the 24dB_digital_gain parameter, the Digital - volume control will allow up to 24dB of - gain. If this parameter is enabled, it is the - responsibility of the user to ensure that - the Digital volume control is set to a value - that does not result in clipping/distortion!) - - -Name: disable-bt -Info: Disable onboard Bluetooth on Pi 3B, 3B+, 3A+, 4B and Zero W, restoring - UART0/ttyAMA0 over GPIOs 14 & 15. - N.B. To disable the systemd service that initialises the modem so it - doesn't use the UART, use 'sudo systemctl disable hciuart'. -Load: dtoverlay=disable-bt -Params: - - -Name: disable-wifi -Info: Disable onboard WLAN on Pi 3B, 3B+, 3A+, 4B and Zero W. -Load: dtoverlay=disable-wifi -Params: - - -Name: dpi18 -Info: Overlay for a generic 18-bit DPI display - This uses GPIOs 0-21 (so no I2C, uart etc.), and activates the output - 2-3 seconds after the kernel has started. -Load: dtoverlay=dpi18 -Params: - - -Name: dpi18cpadhi -Info: Overlay for a generic 18-bit DPI display (in 'mode 6' connection scheme) - This uses GPIOs 0-9,12-17,20-25 (so no I2C, uart etc.), and activates - the output 3-3 seconds after the kernel has started. -Load: dtoverlay=dpi18cpadhi -Params: - - -Name: dpi24 -Info: Overlay for a generic 24-bit DPI display - This uses GPIOs 0-27 (so no I2C, uart etc.), and activates the output - 2-3 seconds after the kernel has started. -Load: dtoverlay=dpi24 -Params: - - -Name: draws -Info: Configures the NW Digital Radio DRAWS Hat - - The board includes an ADC to measure various board values and also - provides two analog user inputs on the expansion header. The ADC - can be configured for various sample rates and gain values to adjust - the input range. Tables describing the two parameters follow. - - ADC Gain Values: - 0 = +/- 6.144V - 1 = +/- 4.096V - 2 = +/- 2.048V - 3 = +/- 1.024V - 4 = +/- 0.512V - 5 = +/- 0.256V - 6 = +/- 0.256V - 7 = +/- 0.256V - - ADC Datarate Values: - 0 = 128sps - 1 = 250sps - 2 = 490sps - 3 = 920sps - 4 = 1600sps (default) - 5 = 2400sps - 6 = 3300sps - 7 = 3300sps -Load: dtoverlay=draws,= -Params: draws_adc_ch4_gain Sets the full scale resolution of the ADCs - input voltage sensor (default 1) - - draws_adc_ch4_datarate Sets the datarate of the ADCs input voltage - sensor - - draws_adc_ch5_gain Sets the full scale resolution of the ADCs - 5V rail voltage sensor (default 1) - - draws_adc_ch5_datarate Sets the datarate of the ADCs 4V rail voltage - sensor - - draws_adc_ch6_gain Sets the full scale resolution of the ADCs - AIN2 input (default 2) - - draws_adc_ch6_datarate Sets the datarate of the ADCs AIN2 input - - draws_adc_ch7_gain Sets the full scale resolution of the ADCs - AIN3 input (default 2) - - draws_adc_ch7_datarate Sets the datarate of the ADCs AIN3 input - - alsaname Name of the ALSA audio device (default "draws") - - -Name: dwc-otg -Info: Selects the dwc_otg USB controller driver which has fiq support. This - is the default on all except the Pi Zero which defaults to dwc2. -Load: dtoverlay=dwc-otg -Params: - - -Name: dwc2 -Info: Selects the dwc2 USB controller driver -Load: dtoverlay=dwc2,= -Params: dr_mode Dual role mode: "host", "peripheral" or "otg" - - g-rx-fifo-size Size of rx fifo size in gadget mode - - g-np-tx-fifo-size Size of non-periodic tx fifo size in gadget - mode - - -[ The ds1307-rtc overlay has been deleted. See i2c-rtc. ] - - -Name: edt-ft5406 -Info: Overlay for the EDT FT5406 touchscreen on the CSI/DSI I2C interface. - This works with the Raspberry Pi 7" touchscreen when not being polled - by the firmware. - You MUST use either "disable_touchscreen=1" or "ignore_lcd=1" in - config.txt to stop the firmware polling the touchscreen. -Load: dtoverlay=edt-ft5406,= -Params: sizex Touchscreen size x (default 800) - sizey Touchscreen size y (default 480) - invx Touchscreen inverted x axis - invy Touchscreen inverted y axis - swapxy Touchscreen swapped x y axis - - -Name: enc28j60 -Info: Overlay for the Microchip ENC28J60 Ethernet Controller on SPI0 -Load: dtoverlay=enc28j60,= -Params: int_pin GPIO used for INT (default 25) - - speed SPI bus speed (default 12000000) - - -Name: enc28j60-spi2 -Info: Overlay for the Microchip ENC28J60 Ethernet Controller on SPI2 -Load: dtoverlay=enc28j60-spi2,= -Params: int_pin GPIO used for INT (default 39) - - speed SPI bus speed (default 12000000) - - -Name: exc3000 -Info: Enables I2C connected EETI EXC3000 multiple touch controller using - GPIO 4 (pin 7 on GPIO header) for interrupt. -Load: dtoverlay=exc3000,= -Params: interrupt GPIO used for interrupt (default 4) - sizex Touchscreen size x (default 4096) - sizey Touchscreen size y (default 4096) - invx Touchscreen inverted x axis - invy Touchscreen inverted y axis - swapxy Touchscreen swapped x y axis - - -Name: fbtft -Info: Overlay for SPI-connected displays using the fbtft drivers. - - This overlay seeks to replace the functionality provided by fbtft_device - which is now gone from the kernel. - - Most displays from fbtft_device have been ported over. - Example: - dtoverlay=fbtft,spi0-0,rpi-display,reset_pin=23,dc_pin=24,led_pin=18,rotate=270 - - It is also possible to specify the controller (this will use the default - init sequence in the driver). - Example: - dtoverlay=fbtft,spi0-0,ili9341,bgr,reset_pin=23,dc_pin=24,led_pin=18,rotate=270 - - For devices on spi1 or spi2, the interfaces should be enabled - with one of the spi1-1/2/3cs and/or spi2-1/2/3cs overlays. - - The following features of fbtft_device have not been ported over: - - parallel bus is not supported - - the init property which overrides the controller initialization - sequence is not supported as a parameter due to memory limitations in - the bootloader responsible for applying the overlay. - - See https://github.com/notro/fbtft/wiki/FBTFT-RPI-overlays for how to - create an overlay. - -Load: dtoverlay=fbtft,= -Params: - spi- Configure device at spi, cs - (boolean, required) - speed SPI bus speed in Hz (default 32000000) - cpha Shifted clock phase (CPHA) mode - cpol Inverse clock polarity (CPOL) mode - - adafruit18 Adafruit 1.8 - adafruit22 Adafruit 2.2 (old) - adafruit22a Adafruit 2.2 - adafruit28 Adafruit 2.8 - adafruit13m Adafruit 1.3 OLED - admatec_c-berry28 C-Berry28 - dogs102 EA DOGS102 - er_tftm050_2 ER-TFTM070-2 - er_tftm070_5 ER-TFTM070-5 - ew24ha0 EW24HA0 - ew24ha0_9bit EW24HA0 in 9-bit mode - freetronicsoled128 Freetronics OLED128 - hy28a HY28A - hy28b HY28B - itdb28_spi ITDB02-2.8 with SPI interface circuit - mi0283qt-2 Watterott MI0283QT-2 - mi0283qt-9a Watterott MI0283QT-9A - nokia3310 Nokia 3310 - nokia3310a Nokia 3310a - nokia5110 Nokia 5110 - piscreen PiScreen - pitft Adafruit PiTFT 2.8 - pioled ILSoft OLED - rpi-display Watterott rpi-display - sainsmart18 Sainsmart 1.8 - sainsmart32_spi Sainsmart 3.2 with SPI interfce circuit - tinylcd35 TinyLCD 3.5 - tm022hdh26 Tianma TM022HDH26 - tontec35_9481 Tontect 3.5 with ILI9481 controller - tontec35_9486 Tontect 3.5 with ILI9486 controller - waveshare32b Waveshare 3.2 - waveshare22 Waveshare 2.2 - - bd663474 BD663474 display controller - hx8340bn HX8340BN display controller - hx8347d HX8347D display controller - hx8353d HX8353D display controller - hx8357d HX8357D display controller - ili9163 ILI9163 display controller - ili9320 ILI9320 display controller - ili9325 ILI9325 display controller - ili9340 ILI9340 display controller - ili9341 ILI9341 display controller - ili9481 ILI9481 display controller - ili9486 ILI9486 display controller - pcd8544 PCD8544 display controller - ra8875 RA8875 display controller - s6d02a1 S6D02A1 display controller - s6d1121 S6D1121 display controller - seps525 SEPS525 display controller - sh1106 SH1106 display controller - ssd1289 SSD1289 display controller - ssd1305 SSD1305 display controller - ssd1306 SSD1306 display controller - ssd1325 SSD1325 display controller - ssd1331 SSD1331 display controller - ssd1351 SSD1351 display controller - st7735r ST7735R display controller - st7789v ST7789V display controller - tls8204 TLS8204 display controller - uc1611 UC1611 display controller - uc1701 UC1701 display controller - upd161704 UPD161704 display controller - - width Display width in pixels - height Display height in pixels - regwidth Display controller register width (default is - driver specific) - buswidth Display bus interface width (default 8) - debug Debug output level {0-7} - rotate Display rotation {0, 90, 180, 270} (counter - clockwise). Not supported by all drivers. - bgr Enable BGR mode (default off). Use if Red and - Blue are swapped. Not supported by all drivers. - fps Frames per second (default 30). In effect this - states how long the driver will wait after video - memory has been changed until display update - transfer is started. - txbuflen Length of the FBTFT transmit buffer - (default 4096) - startbyte Sets the Start byte used by fb_ili9320, - fb_ili9325 and fb_hx8347d. Common value is 0x70. - gamma String representation of Gamma Curve(s). Driver - specific. Not supported by all drivers. - reset_pin GPIO pin for RESET - dc_pin GPIO pin for D/C - led_pin GPIO pin for LED backlight - - -Name: fe-pi-audio -Info: Configures the Fe-Pi Audio Sound Card -Load: dtoverlay=fe-pi-audio -Params: - - -Name: fsm-demo -Info: A demonstration of the gpio-fsm driver. The GPIOs are chosen to work - nicely with a "traffic-light" display of red, amber and green LEDs on - GPIOs 7, 8 and 25 respectively. -Load: dtoverlay=fsm-demo,= -Params: fsm_debug Enable debug logging (default off) - - -Name: ghost-amp -Info: An overlay for the Ghost amplifier. -Load: dtoverlay=ghost-amp,= -Params: fsm_debug Enable debug logging of the GPIO FSM (default - off) - - -Name: goodix -Info: Enables I2C connected Goodix gt9271 multiple touch controller using - GPIOs 4 and 17 (pins 7 and 11 on GPIO header) for interrupt and reset. -Load: dtoverlay=goodix,= -Params: interrupt GPIO used for interrupt (default 4) - reset GPIO used for reset (default 17) - - -Name: googlevoicehat-soundcard -Info: Configures the Google voiceHAT soundcard -Load: dtoverlay=googlevoicehat-soundcard -Params: - - -Name: gpio-fan -Info: Configure a GPIO pin to control a cooling fan. -Load: dtoverlay=gpio-fan,= -Params: gpiopin GPIO used to control the fan (default 12) - temp Temperature at which the fan switches on, in - millicelcius (default 55000) - - -Name: gpio-ir -Info: Use GPIO pin as rc-core style infrared receiver input. The rc-core- - based gpio_ir_recv driver maps received keys directly to a - /dev/input/event* device, all decoding is done by the kernel - LIRC is - not required! The key mapping and other decoding parameters can be - configured by "ir-keytable" tool. -Load: dtoverlay=gpio-ir,= -Params: gpio_pin Input pin number. Default is 18. - - gpio_pull Desired pull-up/down state (off, down, up) - Default is "up". - - invert "1" = invert the input (active-low signalling). - "0" = non-inverted input (active-high - signalling). Default is "1". - - rc-map-name Default rc keymap (can also be changed by - ir-keytable), defaults to "rc-rc6-mce" - - -Name: gpio-ir-tx -Info: Use GPIO pin as bit-banged infrared transmitter output. - This is an alternative to "pwm-ir-tx". gpio-ir-tx doesn't require - a PWM so it can be used together with onboard analog audio. -Load: dtoverlay=gpio-ir-tx,= -Params: gpio_pin Output GPIO (default 18) - - invert "1" = invert the output (make it active-low). - Default is "0" (active-high). - - -Name: gpio-key -Info: This is a generic overlay for activating GPIO keypresses using - the gpio-keys library and this dtoverlay. Multiple keys can be - set up using multiple calls to the overlay for configuring - additional buttons or joysticks. You can see available keycodes - at https://github.com/torvalds/linux/blob/v4.12/include/uapi/ - linux/input-event-codes.h#L64 -Load: dtoverlay=gpio-key,= -Params: gpio GPIO pin to trigger on (default 3) - active_low When this is 1 (active low), a falling - edge generates a key down event and a - rising edge generates a key up event. - When this is 0 (active high), this is - reversed. The default is 1 (active low) - gpio_pull Desired pull-up/down state (off, down, up) - Default is "up". Note that the default pin - (GPIO3) has an external pullup - label Set a label for the key - keycode Set the key code for the button - - - -Name: gpio-led -Info: This is a generic overlay for activating LEDs (or any other component) - by a GPIO pin. Multiple LEDs can be set up using multiple calls to the - overlay. While there are many existing methods to activate LEDs on the - RPi, this method offers some advantages: - 1) Does not require any userspace programs. - 2) LEDs can be connected to the kernel's led-trigger framework, - and drive the LED based on triggers such as cpu load, heartbeat, - kernel panic, key input, timers and others. - 3) LED can be tied to the input state of another GPIO pin. - 4) The LED is setup early during the kernel boot process (useful - for cpu/heartbeat/panic triggers). - - Typical electrical connection is: - RPI-GPIO.19 -> LED -> 300ohm resister -> RPI-GND - The GPIO pin number can be changed with the 'gpio=' parameter. - - To control an LED from userspace, write a 0 or 1 value: - echo 1 > /sys/class/leds/myled1/brightness - The 'myled1' name can be changed with the 'label=' parameter. - - To connect the LED to a kernel trigger from userspace: - echo cpu > /sys/class/leds/myled1/trigger - echo heartbeat > /sys/class/leds/myled1/trigger - echo none > /sys/class/leds/myled1/trigger - To connect the LED to GPIO.26 pin (physical pin 37): - echo gpio > /sys/class/leds/myled1/trigger - echo 26 > /sys/class/leds/myled1/gpio - Available triggers: - cat /sys/class/leds/myled1/trigger - - More information about the Linux kernel LED/Trigger system: - https://www.kernel.org/doc/Documentation/leds/leds-class.rst - https://www.kernel.org/doc/Documentation/leds/ledtrig-oneshot.rst -Load: dtoverlay=gpio-led,= -Params: gpio GPIO pin connected to the LED (default 19) - label The label for this LED. It will appear under - /sys/class/leds/