From 79f145ccd968109ce6cf49ba5eb2e81078a03f18 Mon Sep 17 00:00:00 2001 From: Rob Riggs Date: Sat, 22 Feb 2020 19:35:11 -0600 Subject: [PATCH] Fix AFSK modem after FSK changes. Revert changes to DigitalPLL. Remove intrusive diagnostics in AudioInput, along with dead code. Clean up FIR filter code. Add initial passall support. Disable LSCO when transmitting in debug mode. Set fixed mid-range DAC output in FSK modulator when not transmitting to eliminate DC offset. New way to compute IDLE bytes for TX Delay. Clean up HDLC decoder, improve HDLC diagnostics, add Passall support. Add new extended response types for modem support. Update API version to 0x0201. Update firmware version to 2.0.0b1. --- TNC/AFSKModulator.hpp | 78 +++++++-- TNC/AFSKTestTone.cpp | 1 + TNC/Afsk1200Demodulator.cpp | 62 +++++++ TNC/Afsk1200Demodulator.hpp | 75 ++------ TNC/AfskDemodulator.cpp | 2 +- TNC/AfskDemodulator.hpp | 4 +- TNC/AudioInput.cpp | 149 ---------------- TNC/AudioLevel.cpp | 1 + TNC/DCD.cpp | 1 + TNC/Demodulator.cpp | 9 + TNC/Demodulator.hpp | 17 +- TNC/DigitalPLL.hpp | 16 +- TNC/FilterCoefficients.hpp | 7 +- TNC/FirFilter.hpp | 22 +-- TNC/Fsk9600Demodulator.cpp | 2 +- TNC/Fsk9600Demodulator.hpp | 11 +- TNC/Fsk9600Modulator.hpp | 74 ++++++-- TNC/HDLCEncoder.hpp | 26 ++- TNC/HdlcDecoder.cpp | 94 +++++----- TNC/HdlcDecoder.hpp | 335 ++---------------------------------- TNC/IOEventTask.cpp | 3 +- TNC/KissHardware.cpp | 140 ++++++++------- TNC/KissHardware.hpp | 103 ++++++----- TNC/Modulator.hpp | 39 +++++ TNC/ModulatorTask.cpp | 16 +- TNC/ModulatorTask.hpp | 22 ++- 26 files changed, 532 insertions(+), 777 deletions(-) diff --git a/TNC/AFSKModulator.hpp b/TNC/AFSKModulator.hpp index e95456f..c3b5480 100644 --- a/TNC/AFSKModulator.hpp +++ b/TNC/AFSKModulator.hpp @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Mobilinkd LLC +// Copyright 2015-2020 Mobilinkd LLC // All rights reserved. #pragma once @@ -75,7 +75,7 @@ struct AFSKModulator : Modulator PTT* ptt_; uint8_t twist_{50}; uint16_t volume_{4096}; - uint16_t buffer_[DAC_BUFFER_LEN]; + std::array buffer_; AFSKModulator(osMessageQId queue, PTT* ptt) : dacOutputQueueHandle_(queue), ptt_(ptt) @@ -88,13 +88,28 @@ struct AFSKModulator : Modulator { set_twist(hw.tx_twist); - // Configure 80MHz clock for 26.4ksps. - htim7.Init.Period = 3029; + // Configure 48MHz clock for 26.4ksps. + htim7.Init.Period = 1817; if (HAL_TIM_Base_Init(&htim7) != HAL_OK) { ERROR("htim7 init failed"); CxxErrorHandler(); } + + DAC_ChannelConfTypeDef sConfig; + + sConfig.DAC_SampleAndHold = DAC_SAMPLEANDHOLD_DISABLE; + sConfig.DAC_Trigger = DAC_TRIGGER_NONE; + sConfig.DAC_OutputBuffer = DAC_OUTPUTBUFFER_ENABLE; + sConfig.DAC_ConnectOnChipPeripheral = DAC_CHIPCONNECT_ENABLE; + sConfig.DAC_UserTrimming = DAC_TRIMMING_FACTORY; + if (HAL_DAC_ConfigChannel(&hdac1, &sConfig, DAC_CHANNEL_1) != HAL_OK) + { + CxxErrorHandler(); + } + + if (HAL_DAC_SetValue(&hdac1, DAC_CHANNEL_1, DAC_ALIGN_12B_R, 2048) != HAL_OK) CxxErrorHandler(); + if (HAL_DAC_Start(&hdac1, DAC_CHANNEL_1) != HAL_OK) CxxErrorHandler(); } void deinit() override @@ -120,19 +135,21 @@ struct AFSKModulator : Modulator void set_twist(uint8_t twist) {twist_ = twist;} - void send(bool bit) override - { + void send(bool bit) override + { switch (running_) { case -1: + ptt_->on(); + #ifdef KISS_LOGGING + HAL_RCCEx_DisableLSCO(); + #endif fill_first(bit); running_ = 0; break; case 0: fill_last(bit); running_ = 1; - ptt_->on(); - HAL_TIM_Base_Start(&htim7); - HAL_DAC_Start_DMA(&hdac1, DAC_CHANNEL_1, (uint32_t*) buffer_, DAC_BUFFER_LEN, DAC_ALIGN_12B_R); + start_conversion(); break; case 1: osMessagePut(dacOutputQueueHandle_, bit, osWaitForever); @@ -171,12 +188,12 @@ struct AFSKModulator : Modulator void fill_first(bool bit) override { - fill(buffer_, bit); + fill(buffer_.data(), bit); } void fill_last(bool bit) override { - fill(buffer_ + BIT_LEN, bit); + fill(buffer_.data() + BIT_LEN, bit); } void empty() override @@ -187,10 +204,12 @@ struct AFSKModulator : Modulator break; case 0: running_ = -1; - HAL_DAC_Stop_DMA(&hdac1, DAC_CHANNEL_1); - HAL_TIM_Base_Stop(&htim7); + stop_conversion(); ptt_->off(); pos_ = 0; + #ifdef KISS_LOGGING + HAL_RCCEx_EnableLSCO(RCC_LSCOSOURCE_LSE); + #endif break; case -1: break; @@ -200,10 +219,12 @@ struct AFSKModulator : Modulator void abort() override { running_ = -1; - HAL_DAC_Stop_DMA(&hdac1, DAC_CHANNEL_1); - HAL_TIM_Base_Stop(&htim7); + stop_conversion(); ptt_->off(); pos_ = 0; + #ifdef KISS_LOGGING + HAL_RCCEx_EnableLSCO(RCC_LSCOSOURCE_LSE); + #endif // Drain the queue. while (osMessageGet(dacOutputQueueHandle_, 0).status == osEventMessage); @@ -213,6 +234,33 @@ struct AFSKModulator : Modulator { return 1.2f; } + +private: + /** + * Configure the DAC for timer-based DMA conversion, start the timer, + * and start DMA to DAC. + */ + void start_conversion() + { + DAC_ChannelConfTypeDef sConfig; + + sConfig.DAC_SampleAndHold = DAC_SAMPLEANDHOLD_DISABLE; + sConfig.DAC_Trigger = DAC_TRIGGER_T7_TRGO; + sConfig.DAC_OutputBuffer = DAC_OUTPUTBUFFER_ENABLE; + sConfig.DAC_ConnectOnChipPeripheral = DAC_CHIPCONNECT_ENABLE; + sConfig.DAC_UserTrimming = DAC_TRIMMING_FACTORY; + if (HAL_DAC_ConfigChannel(&hdac1, &sConfig, DAC_CHANNEL_1) != HAL_OK) + { + CxxErrorHandler(); + } + + HAL_TIM_Base_Start(&htim7); + HAL_DAC_Start_DMA( + &hdac1, DAC_CHANNEL_1, + reinterpret_cast(buffer_.data()), buffer_.size(), + DAC_ALIGN_12B_R); + } + }; }} // mobilinkd::tnc diff --git a/TNC/AFSKTestTone.cpp b/TNC/AFSKTestTone.cpp index 16db756..483bdac 100644 --- a/TNC/AFSKTestTone.cpp +++ b/TNC/AFSKTestTone.cpp @@ -3,6 +3,7 @@ #include "AFSKTestTone.hpp" #include "ModulatorTask.hpp" +#include "Modulator.hpp" void startAfskToneTask(void const* arg) { diff --git a/TNC/Afsk1200Demodulator.cpp b/TNC/Afsk1200Demodulator.cpp index f9fe4d0..ccbef19 100644 --- a/TNC/Afsk1200Demodulator.cpp +++ b/TNC/Afsk1200Demodulator.cpp @@ -15,6 +15,68 @@ afsk1200::Demodulator Afsk1200Demodulator::demod1(26400, Afsk1200Demodulator::fi afsk1200::Demodulator Afsk1200Demodulator::demod2(26400, Afsk1200Demodulator::filter_2); afsk1200::Demodulator Afsk1200Demodulator::demod3(26400, Afsk1200Demodulator::filter_3); +hdlc::IoFrame* Afsk1200Demodulator::operator()(const q15_t* samples) +{ + hdlc::IoFrame* result = nullptr; + + q15_t* filtered = demod_filter.filter(const_cast(samples)); + + ++counter; + +#if 1 + auto frame1 = demod1(filtered, ADC_BLOCK_SIZE); + if (frame1) + { + if (frame1->fcs() != last_fcs or counter > last_counter + 2) + { + last_fcs = frame1->fcs(); + last_counter = counter; + result = frame1; + } + else + { + hdlc::release (frame1); + } + } +#endif + +#if 1 + auto frame2 = demod2(filtered, ADC_BLOCK_SIZE); + if (frame2) + { + if (frame2->fcs() != last_fcs or counter > last_counter + 2) + { + last_fcs = frame2->fcs(); + last_counter = counter; + result = frame2; + } + else + { + hdlc::release(frame2); + } + } +#endif + +#if 1 + auto frame3 = demod3(filtered, ADC_BLOCK_SIZE); + if (frame3) + { + if (frame3->fcs() != last_fcs or counter > last_counter + 2) + { + last_fcs = frame3->fcs(); + last_counter = counter; + result = frame3; + } + else + { + hdlc::release(frame3); + } + } +#endif + locked_ = demod1.locked() or demod2.locked() or demod3.locked(); + return result; +} + /* * Return twist as a the difference in dB between mark and space. The * expected values are about 0dB for discriminator output and about 5.5dB diff --git a/TNC/Afsk1200Demodulator.hpp b/TNC/Afsk1200Demodulator.hpp index 7377099..57dd59f 100644 --- a/TNC/Afsk1200Demodulator.hpp +++ b/TNC/Afsk1200Demodulator.hpp @@ -35,14 +35,15 @@ struct Afsk1200Demodulator : IDemodulator */ static constexpr size_t FILTER_TAP_NUM = 132; static constexpr uint32_t ADC_BLOCK_SIZE = afsk1200::ADC_BUFFER_SIZE; - static constexpr uint32_t SAMPLE_RATE = 26400; static_assert(audio::ADC_BUFFER_SIZE >= ADC_BLOCK_SIZE); + static constexpr uint32_t SAMPLE_RATE = 26400; + static constexpr uint16_t VREF = 16383; + using audio_filter_t = Q15FirFilter; static const q15_t bpf_coeffs[FILTER_TAP_NUM]; - static afsk1200::emphasis_filter_type filter_1; static afsk1200::emphasis_filter_type filter_2; static afsk1200::emphasis_filter_type filter_3; @@ -76,6 +77,7 @@ struct Afsk1200Demodulator : IDemodulator counter = 0; demod_filter.init(bpf_coeffs); + passall(kiss::settings().options & KISS_OPTION_PASSALL); hadc1.Init.OversamplingMode = ENABLE; if (HAL_ADC_Init(&hadc1) != HAL_OK) @@ -103,67 +105,7 @@ struct Afsk1200Demodulator : IDemodulator locked_ = false; } - hdlc::IoFrame* operator()(const q15_t* samples) override - { - hdlc::IoFrame* result = nullptr; - - q15_t* filtered = demod_filter(const_cast(samples)); - - ++counter; - -#if 1 - auto frame1 = demod1(filtered, ADC_BLOCK_SIZE); - if (frame1) - { - if (frame1->fcs() != last_fcs or counter > last_counter + 2) - { - last_fcs = frame1->fcs(); - last_counter = counter; - result = frame1; - } - else - { - hdlc::release (frame1); - } - } -#endif - -#if 1 - auto frame2 = demod2(filtered, ADC_BLOCK_SIZE); - if (frame2) - { - if (frame2->fcs() != last_fcs or counter > last_counter + 2) - { - last_fcs = frame2->fcs(); - last_counter = counter; - result = frame2; - } - else - { - hdlc::release(frame2); - } - } -#endif - -#if 1 - auto frame3 = demod3(filtered, ADC_BLOCK_SIZE); - if (frame3) - { - if (frame3->fcs() != last_fcs or counter > last_counter + 2) - { - last_fcs = frame3->fcs(); - last_counter = counter; - result = frame3; - } - else - { - hdlc::release(frame3); - } - } -#endif - locked_ = demod1.locked() or demod2.locked() or demod3.locked(); - return result; - } + hdlc::IoFrame* operator()(const q15_t* samples) override; float readTwist() override; @@ -176,6 +118,13 @@ struct Afsk1200Demodulator : IDemodulator { return ADC_BLOCK_SIZE; } + + void passall(bool enable) override + { + demod1.hdlc_decoder_.passall = enable; + demod2.hdlc_decoder_.passall = enable; + demod3.hdlc_decoder_.passall = enable; + } }; }} // mobilinkd::tnc diff --git a/TNC/AfskDemodulator.cpp b/TNC/AfskDemodulator.cpp index e520852..d4dba76 100644 --- a/TNC/AfskDemodulator.cpp +++ b/TNC/AfskDemodulator.cpp @@ -1,4 +1,4 @@ -// Copyright 2017-2019 Rob Riggs +// Copyright 2017-2020 Rob Riggs // All rights reserved. #include "AfskDemodulator.hpp" diff --git a/TNC/AfskDemodulator.hpp b/TNC/AfskDemodulator.hpp index 4769417..73e442a 100644 --- a/TNC/AfskDemodulator.hpp +++ b/TNC/AfskDemodulator.hpp @@ -48,11 +48,11 @@ struct Demodulator { emphasis_filter_type& audio_filter_; libafsk::FixedDelayLine<40> delay_line_; DPLL pll_; - Q15FirFilter lpf_filter_; + Q15FirFilter lpf_filter_; libafsk::NRZI nrzi_; hdlc::NewDecoder hdlc_decoder_; bool locked_; - q15_t buffer_[audio::ADC_BUFFER_SIZE]; + q15_t buffer_[ADC_BUFFER_SIZE]; Demodulator(size_t sample_rate, emphasis_filter_type& c) : sample_rate_(sample_rate) diff --git a/TNC/AudioInput.cpp b/TNC/AudioInput.cpp index 2bbe632..042f93a 100644 --- a/TNC/AudioInput.cpp +++ b/TNC/AudioInput.cpp @@ -176,8 +176,6 @@ void demodulatorTask() { continue; } - gpio::BAT_DIVIDER::off(); - auto block = (adc_pool_type::chunk_type*) evt.value.p; auto samples = (int16_t*) block->buffer; @@ -201,7 +199,6 @@ void demodulatorTask() { dcd_off(); } } - gpio::BAT_DIVIDER::on(); } demodulator->stop(); @@ -412,152 +409,6 @@ void pollInputTwist() DEBUG("exit pollInputTwist"); } -#if 0 -void streamAverageInputTwist() -{ - DEBUG("enter streamAverageInputTwist"); - - constexpr uint32_t channel = AUDIO_IN; - - startADC(channel); - - uint32_t acount = 0; - float g700 = 0.0f; - float g1200 = 0.0f; - float g1700 = 0.0f; - float g2200 = 0.0f; - float g2700 = 0.0f; - - GoertzelFilter gf700(700.0); - GoertzelFilter gf1200(1200.0); - GoertzelFilter gf1700(1700.0); - GoertzelFilter gf2200(2200.0); - GoertzelFilter gf2700(2700.0); - - while (true) { - osEvent peek = osMessagePeek(audioInputQueueHandle, 0); - if (peek.status == osEventMessage) break; - - acount++; - uint32_t count = 0; - while (count < TWIST_SAMPLE_SIZE) { - - osEvent evt = osMessageGet(adcInputQueueHandle, osWaitForever); - if (evt.status != osEventMessage) continue; - - count += ADC_BUFFER_SIZE; - - auto block = (adc_pool_type::chunk_type*) evt.value.v; - uint16_t* data = (uint16_t*) block->buffer; - gf700(data, ADC_BUFFER_SIZE); - gf1200(data, ADC_BUFFER_SIZE); - gf1700(data, ADC_BUFFER_SIZE); - gf2200(data, ADC_BUFFER_SIZE); - gf2700(data, ADC_BUFFER_SIZE); - - adcPool.deallocate(block); - } - - g700 += 10.0 * log10(gf700); - g1200 += 10.0 * log10(gf1200); - g1700 += 10.0 * log10(gf1700); - g2200 += 10.0 * log10(gf2200); - g2700 += 10.0 * log10(gf2700); - - char* buffer = 0; - // @TODO: Make re-entrant printf work (or convert to fixed-point). - int len = asiprintf_r( - &buffer, - "_%f, %f, %f, %f, %f\r\n", - g700 / acount, - g1200 / acount, - g1700 / acount, - g2200 / acount, - g2700 / acount); - - if (len > 0) { - buffer[0] = kiss::hardware::POLL_INPUT_TWIST; - ioport->write((uint8_t*)buffer, len - 1, 6, 10); - free(buffer); - } - - gf700.reset(); - gf1200.reset(); - gf1700.reset(); - gf2200.reset(); - gf2700.reset(); - } - - stopADC(); - DEBUG("exit streamAverageInputTwist"); -} - -void streamInstantInputTwist() -{ - DEBUG("enter streamInstantInputTwist"); - - constexpr uint32_t channel = AUDIO_IN; - - startADC(channel); - - GoertzelFilter gf700(700.0); - GoertzelFilter gf1200(1200.0); - GoertzelFilter gf1700(1700.0); - GoertzelFilter gf2200(2200.0); - GoertzelFilter gf2700(2700.0); - - while (true) { - osEvent peek = osMessagePeek(audioInputQueueHandle, 0); - if (peek.status == osEventMessage) break; - - uint32_t count = 0; - while (count < TWIST_SAMPLE_SIZE) { - - osEvent evt = osMessageGet(adcInputQueueHandle, osWaitForever); - if (evt.status != osEventMessage) continue; - - count += ADC_BUFFER_SIZE; - - auto block = (adc_pool_type::chunk_type*) evt.value.v; - uint16_t* data = (uint16_t*) block->buffer; - gf700(data, ADC_BUFFER_SIZE); - gf1200(data, ADC_BUFFER_SIZE); - gf1700(data, ADC_BUFFER_SIZE); - gf2200(data, ADC_BUFFER_SIZE); - gf2700(data, ADC_BUFFER_SIZE); - - adcPool.deallocate(block); - } - - char* buffer = 0; - // @TODO: Make re-entrant printf work (or convert to fixed-point). - int len = asiprintf_r( - &buffer, - "_%f, %f, %f, %f, %f\r\n", - 10.0 * log10(gf700), - 10.0 * log10(gf1200), - 10.0 * log10(gf1700), - 10.0 * log10(gf2200), - 10.0 * log10(gf2700)); - - if (len > 0) { - buffer[0] = kiss::hardware::POLL_INPUT_TWIST; - ioport->write((uint8_t*)buffer, len - 1, 6, 10); - free(buffer); - } - - gf700.reset(); - gf1200.reset(); - gf1700.reset(); - gf2200.reset(); - gf2700.reset(); - } - - stopADC(); - DEBUG("exit streamInstantInputTwist"); -} -#endif - void streamAmplifiedInputLevels() { DEBUG("enter streamAmplifiedInputLevels"); streamLevels(AUDIO_IN, kiss::hardware::POLL_INPUT_LEVEL); diff --git a/TNC/AudioLevel.cpp b/TNC/AudioLevel.cpp index 096fb25..5c8ad92 100644 --- a/TNC/AudioLevel.cpp +++ b/TNC/AudioLevel.cpp @@ -5,6 +5,7 @@ #include "AudioInput.hpp" #include "KissHardware.hpp" #include "ModulatorTask.hpp" +#include "Modulator.hpp" #include "GPIO.hpp" #include "LEDIndicator.h" #include "ModulatorTask.hpp" diff --git a/TNC/DCD.cpp b/TNC/DCD.cpp index f1b413e..9dc3733 100644 --- a/TNC/DCD.cpp +++ b/TNC/DCD.cpp @@ -3,6 +3,7 @@ #include "DCD.h" #include "LEDIndicator.h" +#include "GPIO.hpp" bool& dcd_status() { diff --git a/TNC/Demodulator.cpp b/TNC/Demodulator.cpp index 7fffc5e..4aa2bd7 100644 --- a/TNC/Demodulator.cpp +++ b/TNC/Demodulator.cpp @@ -41,4 +41,13 @@ void IDemodulator::startADC(uint32_t period, uint32_t block_size) } } +void IDemodulator::stopADC() +{ + if (HAL_ADC_Stop_DMA(&hadc1) != HAL_OK) + CxxErrorHandler(); + if (HAL_TIM_Base_Stop(&htim6) != HAL_OK) + CxxErrorHandler(); +} + + }} // mobilinkd::tnc diff --git a/TNC/Demodulator.hpp b/TNC/Demodulator.hpp index 9eb2b3c..a7ab9ec 100644 --- a/TNC/Demodulator.hpp +++ b/TNC/Demodulator.hpp @@ -28,16 +28,21 @@ struct IDemodulator virtual bool locked() const = 0; virtual size_t size() const = 0; + /** + * Tell the demodulator to return all "passable" HDLC frames. These + * are frames which consist of an even multiple of eight bits and are + * up to 330 bytes, but which do not have a valid checksum. + * + * @param enable is true when enabled and false when disabled. The + * default state is disabled. + */ + virtual void passall(bool enable) = 0; + virtual ~IDemodulator() {} static void startADC(uint32_t period, uint32_t block_size); - static void stopADC() { - if (HAL_ADC_Stop_DMA(&hadc1) != HAL_OK) - CxxErrorHandler(); - if (HAL_TIM_Base_Stop(&htim6) != HAL_OK) - CxxErrorHandler(); - } + static void stopADC(); }; }} // mobilinkd::tnc diff --git a/TNC/DigitalPLL.hpp b/TNC/DigitalPLL.hpp index 13a73a9..5f3b8b2 100644 --- a/TNC/DigitalPLL.hpp +++ b/TNC/DigitalPLL.hpp @@ -1,5 +1,7 @@ -#ifndef MOBILINKD__DIGITAL_PLL_H_ -#define MOBILINKD__DIGITAL_PLL_H_ +// Copyright 2015-2020 Mobilinkd LLC +// All rights reserved. + +#pragma once #include "Hysteresis.hpp" #include "IirFilter.hpp" @@ -43,8 +45,7 @@ constexpr std::array lock_a = { // constexpr std::array loop_coeffs = { // 0.08160962754214955, 0.25029850550446403, 0.3361837339067726, 0.2502985055044641, 0.08160962754214969 -// 3.196252e-02,1.204223e-01,2.176819e-01,2.598666e-01,2.176819e-01,1.204223e-01,3.196252e-02 - 0.0, 0.0, 0.5, 0.5, 0.0, 0.0, 0.0 + 3.196252e-02,1.204223e-01,2.176819e-01,2.598666e-01,2.176819e-01,1.204223e-01,3.196252e-02 }; } // pll @@ -73,8 +74,8 @@ struct BaseDigitalPLL BaseDigitalPLL(float_type sample_rate, float_type symbol_rate) : sample_rate_(sample_rate), symbol_rate_(symbol_rate) , sps_(sample_rate / symbol_rate) - , limit_(sps_ / float_type(2.0)) - , lock_(sps_ * float_type(0.03), sps_ * float_type(0.15), 1, 0) + , limit_(sps_ / 2.0) + , lock_(sps_ * 0.03, sps_ * 0.15, 1, 0) , last_(false), count_(0), sample_(false) , jitter_(0.0), bits_(1) {} @@ -128,6 +129,3 @@ typedef BaseDigitalPLL DigitalPLL; typedef BaseDigitalPLL FastDigitalPLL; }} // mobilinkd::tnc - -#endif // MOBILINKD__DIGITAL_PLL_H_ - diff --git a/TNC/FilterCoefficients.hpp b/TNC/FilterCoefficients.hpp index f422526..e24466a 100644 --- a/TNC/FilterCoefficients.hpp +++ b/TNC/FilterCoefficients.hpp @@ -1,8 +1,7 @@ -// Copyright 2015 Rob Riggs +// Copyright 2015-2020 Rob Riggs // All rights reserved. -#ifndef MOBILINKD__TNC__FILTER_COEFFICIENTS_HPP_ -#define MOBILINKD__TNC__FILTER_COEFFICIENTS_HPP_ +#pragma once #include "IirFilter.hpp" #include "FirFilter.hpp" @@ -303,5 +302,3 @@ extern const TFirCoefficients<9>* AfskFilters[19]; }}} // mobilinkd::tnc::filter - -#endif // MOBILINKD__TNC__FILTER_COEFFICIENTS_HPP_ diff --git a/TNC/FirFilter.hpp b/TNC/FirFilter.hpp index 5ec221d..8f64ae5 100644 --- a/TNC/FirFilter.hpp +++ b/TNC/FirFilter.hpp @@ -1,8 +1,7 @@ -// Copyright 2015-2019 Rob Riggs +// Copyright 2015-2020 Rob Riggs // All rights reserved. -#ifndef MOBILINKD__TNC__FIR_FILTER_H_ -#define MOBILINKD__TNC__FIR_FILTER_H_ +#pragma once #include @@ -114,25 +113,16 @@ struct Q15FirFilter { { vgnd_ = audio::virtual_ground; filter_taps = taps; - arm_fir_init_q15(&instance, FILTER_SIZE, const_cast(filter_taps), // WTF ARM?!? + arm_fir_init_q15(&instance, FILTER_SIZE, + const_cast(filter_taps), filter_state, BLOCK_SIZE); } - // ADC input - q15_t* operator()(q15_t* input) // __attribute__((section(".bss2"))) + q15_t* filter(const q15_t* input) { - arm_fir_fast_q15(&instance, input, filter_output, BLOCK_SIZE); - return filter_output; - } - - // LPF input - q15_t* filter(q15_t* input) // __attribute__((section(".bss2"))) - { - arm_fir_fast_q15(&instance, input, filter_output, BLOCK_SIZE); + arm_fir_fast_q15(&instance, const_cast(input), filter_output, BLOCK_SIZE); return filter_output; } }; }} // mobilinkd::tnc - -#endif // MOBILINKD__TNC__FIR_FILTER_H_ diff --git a/TNC/Fsk9600Demodulator.cpp b/TNC/Fsk9600Demodulator.cpp index 2c94cc6..8ec4ecb 100644 --- a/TNC/Fsk9600Demodulator.cpp +++ b/TNC/Fsk9600Demodulator.cpp @@ -11,7 +11,7 @@ hdlc::IoFrame* Fsk9600Demodulator::operator()(const q15_t* samples) { hdlc::IoFrame* result = nullptr; - auto filtered = demod_filter(const_cast(samples)); + auto filtered = demod_filter.filter(const_cast(samples)); for (size_t i = 0; i != ADC_BLOCK_SIZE; ++i) { diff --git a/TNC/Fsk9600Demodulator.hpp b/TNC/Fsk9600Demodulator.hpp index c2b1dc7..880ed51 100644 --- a/TNC/Fsk9600Demodulator.hpp +++ b/TNC/Fsk9600Demodulator.hpp @@ -55,17 +55,13 @@ struct Fsk9600Demodulator : IDemodulator auto const& bpf_coeffs = bpf_bank[kiss::settings().rx_twist + 3]; const q15_t* bpf = bpf_coeffs.data(); demod_filter.init(bpf); + passall(kiss::settings().options & KISS_OPTION_PASSALL); hadc1.Init.OversamplingMode = DISABLE; if (HAL_ADC_Init(&hadc1) != HAL_OK) { CxxErrorHandler(); } -// -// if (HAL_ADCEx_Calibration_Start(&hadc1, ADC_SINGLE_ENDED) != HAL_OK) -// { -// CxxErrorHandler(); -// } ADC_ChannelConfTypeDef sConfig; @@ -102,6 +98,11 @@ struct Fsk9600Demodulator : IDemodulator { return ADC_BLOCK_SIZE; } + + void passall(bool enable) override + { + hdlc_decoder_.passall = enable; + } }; }} // mobilinkd::tnc diff --git a/TNC/Fsk9600Modulator.hpp b/TNC/Fsk9600Modulator.hpp index f9e491a..a98ac80 100644 --- a/TNC/Fsk9600Modulator.hpp +++ b/TNC/Fsk9600Modulator.hpp @@ -27,6 +27,7 @@ struct Fsk9600Modulator : Modulator { static constexpr int8_t DAC_BUFFER_LEN = 40; static constexpr int8_t BIT_LEN = DAC_BUFFER_LEN / 2; + static constexpr uint16_t VREF = 4095; static constexpr std::array cos_table{ 2047, 2020, 1937, 1801, 1616, 1387, 1120, 822, 502, 169, @@ -54,6 +55,8 @@ struct Fsk9600Modulator : Modulator { for (auto& x : buffer_) x = 2048; + (void) hw; // unused + state = State::STOPPED; level = Level::HIGH; @@ -64,12 +67,27 @@ struct Fsk9600Modulator : Modulator ERROR("htim7 init failed"); CxxErrorHandler(); } + + DAC_ChannelConfTypeDef sConfig; + + sConfig.DAC_SampleAndHold = DAC_SAMPLEANDHOLD_DISABLE; + sConfig.DAC_Trigger = DAC_TRIGGER_NONE; + sConfig.DAC_OutputBuffer = DAC_OUTPUTBUFFER_ENABLE; + sConfig.DAC_ConnectOnChipPeripheral = DAC_CHIPCONNECT_ENABLE; + sConfig.DAC_UserTrimming = DAC_TRIMMING_FACTORY; + if (HAL_DAC_ConfigChannel(&hdac1, &sConfig, DAC_CHANNEL_1) != HAL_OK) + { + CxxErrorHandler(); + } + + if (HAL_DAC_SetValue(&hdac1, DAC_CHANNEL_1, DAC_ALIGN_12B_R, 2048) != HAL_OK) CxxErrorHandler(); + if (HAL_DAC_Start(&hdac1, DAC_CHANNEL_1) != HAL_OK) CxxErrorHandler(); } void deinit() override { state = State::STOPPED; - HAL_DAC_Stop_DMA(&hdac1, DAC_CHANNEL_1); + HAL_DAC_Stop(&hdac1, DAC_CHANNEL_1); HAL_TIM_Base_Stop(&htim7); ptt_->off(); } @@ -100,18 +118,18 @@ struct Fsk9600Modulator : Modulator { case State::STOPPING: case State::STOPPED: + ptt_->on(); + #ifdef KISS_LOGGING + HAL_RCCEx_DisableLSCO(); + #endif + fill_first(scrambled); state = State::STARTING; break; case State::STARTING: fill_last(scrambled); state = State::RUNNING; - ptt_->on(); - HAL_TIM_Base_Start(&htim7); - HAL_DAC_Start_DMA( - &hdac1, DAC_CHANNEL_1, - reinterpret_cast(buffer_.data()), buffer_.size(), - DAC_ALIGN_12B_R); + start_conversion(); break; case State::RUNNING: osMessagePut(dacOutputQueueHandle_, scrambled, osWaitForever); @@ -142,24 +160,27 @@ struct Fsk9600Modulator : Modulator break; case State::STOPPING: state = State::STOPPED; - HAL_DAC_Stop_DMA(&hdac1, DAC_CHANNEL_1); - HAL_TIM_Base_Stop(&htim7); + stop_conversion(); ptt_->off(); + level = Level::HIGH; + #ifdef KISS_LOGGING + HAL_RCCEx_EnableLSCO(RCC_LSCOSOURCE_LSE); + #endif break; case State::STOPPED: break; } - state = State::STOPPING; - level = Level::HIGH; } void abort() override { state = State::STOPPED; - HAL_DAC_Stop_DMA(&hdac1, DAC_CHANNEL_1); - HAL_TIM_Base_Stop(&htim7); + stop_conversion(); ptt_->off(); - + level = Level::HIGH; + #ifdef KISS_LOGGING + HAL_RCCEx_EnableLSCO(RCC_LSCOSOURCE_LSE); + #endif // Drain the queue. while (osMessageGet(dacOutputQueueHandle_, 0).status == osEventMessage); } @@ -171,6 +192,31 @@ struct Fsk9600Modulator : Modulator private: + /** + * Configure the DAC for timer-based DMA conversion, start the timer, + * and start DMA to DAC. + */ + void start_conversion() + { + DAC_ChannelConfTypeDef sConfig; + + sConfig.DAC_SampleAndHold = DAC_SAMPLEANDHOLD_DISABLE; + sConfig.DAC_Trigger = DAC_TRIGGER_T7_TRGO; + sConfig.DAC_OutputBuffer = DAC_OUTPUTBUFFER_ENABLE; + sConfig.DAC_ConnectOnChipPeripheral = DAC_CHIPCONNECT_ENABLE; + sConfig.DAC_UserTrimming = DAC_TRIMMING_FACTORY; + if (HAL_DAC_ConfigChannel(&hdac1, &sConfig, DAC_CHANNEL_1) != HAL_OK) + { + CxxErrorHandler(); + } + + HAL_TIM_Base_Start(&htim7); + HAL_DAC_Start_DMA( + &hdac1, DAC_CHANNEL_1, + reinterpret_cast(buffer_.data()), buffer_.size(), + DAC_ALIGN_12B_R); + } + uint16_t adjust_level(int32_t sample) const { sample *= volume_; diff --git a/TNC/HDLCEncoder.hpp b/TNC/HDLCEncoder.hpp index 6cf190e..355f679 100644 --- a/TNC/HDLCEncoder.hpp +++ b/TNC/HDLCEncoder.hpp @@ -1,10 +1,10 @@ -// Copyright 2015 Mobilinkd LLC +// Copyright 2015-2020 Mobilinkd LLC // All rights reserved. -#ifndef INC_HDLCENCODER_HPP_ -#define INC_HDLCENCODER_HPP_ +#pragma once #include "Modulator.hpp" +#include "ModulatorTask.hpp" #include "HdlcFrame.hpp" #include "NRZI.hpp" #include "PTT.hpp" @@ -51,12 +51,12 @@ struct Encoder { volatile bool running_; bool send_delay_; // Avoid sending the preamble for back-to-back frames. - Encoder(osMessageQId input, Modulator* output) + Encoder(osMessageQId input) : tx_delay_(kiss::settings().txdelay), tx_tail_(kiss::settings().txtail) , p_persist_(kiss::settings().ppersist), slot_time_(kiss::settings().slot) , duplex_(kiss::settings().duplex), state_(state_type::STATE_IDLE) , ones_(0), nrzi_(), crc_() - , input_(input), modulator_(output) + , input_(input), modulator_(&getModulator()) , running_(false), send_delay_(true) {} @@ -67,11 +67,17 @@ struct Encoder { state_ = state_type::STATE_IDLE; osEvent evt = osMessageGet(input_, osWaitForever); if (evt.status == osEventMessage) { + tx_delay_ = kiss::settings().txdelay; + tx_tail_ = kiss::settings().txtail; + p_persist_ = kiss::settings().ppersist; + slot_time_ = kiss::settings().slot; + duplex_ = kiss::settings().duplex; auto frame = (IoFrame*) evt.value.p; process(frame); // See if we have back-to-back frames. evt = osMessagePeek(input_, 0); if (evt.status != osEventMessage) { + send_raw(IDLE); send_raw(IDLE); send_delay_ = true; if (!duplex_) { @@ -95,6 +101,11 @@ struct Encoder { int p_persist() const { return p_persist_; } void p_persist(int value) { p_persist_ = value; } + void updateModulator() + { + modulator_ = &(getModulator()); + } + state_type status() const {return state_; } void stop() { running_ = false; } @@ -192,7 +203,8 @@ struct Encoder { } void send_delay() { - const size_t tmp = tx_delay_ * modulator_->bits_per_ms(); + const size_t tmp = tx_delay_ * 1.25 * modulator_->bits_per_ms(); + INFO("Sending %u IDLE bytes", tmp); for (size_t i = 0; i != tmp; i++) { send_raw(IDLE); } @@ -239,5 +251,3 @@ struct Encoder { }; }}} // mobilinkd::tnc::hdlc - -#endif // INC_HDLCENCODER_HPP_ diff --git a/TNC/HdlcDecoder.cpp b/TNC/HdlcDecoder.cpp index 789a11f..bce1f31 100644 --- a/TNC/HdlcDecoder.cpp +++ b/TNC/HdlcDecoder.cpp @@ -7,48 +7,6 @@ namespace mobilinkd { namespace tnc { namespace hdlc { -Decoder::Decoder(bool pass_all) -: state_(SEARCH), ones_(0), buffer_(0), frame_(acquire()) -, bits_(0), passall_(pass_all), ready_(false) -{} - -IoFrame* Decoder::operator()(bool bit, bool pll) -{ - IoFrame* result = nullptr; - - // It appears that the ioFramePool may not get initialized in the proper - // order during some builds. - if (nullptr == frame_) frame_ = acquire(); - if (nullptr == frame_) return result; - - if (not pll) { - if ((state_ == FRAMING) and (frame_->size() >= 15) and passall_) { - frame_->parse_fcs(); - if (passall_ or frame_->ok()) { - result = frame_; - ready_ = false; - frame_ = acquire(); - return result; - } - } - reset(); - } else { - if (process(bit)) { - ready_ = false; - frame_->parse_fcs(); - if (passall_ or frame_->ok()) { - result = frame_; - frame_ = acquire(); - if (frame_) start_hunt(); - return result; - } - frame_->clear(); - } - } - - return result; -} - NewDecoder::optional_result_type NewDecoder::operator()(bool input, bool pll_lock) { optional_result_type result = nullptr; @@ -56,9 +14,8 @@ NewDecoder::optional_result_type NewDecoder::operator()(bool input, bool pll_loc auto status = process(input, pll_lock); if (status) { -// INFO("Frame Status = %02x, size = %d, CRC = %04x", -// int(status), int(packet->size()), int(packet->crc())); - if (((status & STATUS_OK) || passall) && packet->size() > 10) + INFO("HDLC decode status = 0x%02x, bits = %d", int(status), int(report_bits)); + if (can_pass(status) and packet->size() > 10) { result = packet; packet = nullptr; @@ -74,6 +31,8 @@ uint8_t NewDecoder::process(bool input, bool pll_lock) { uint8_t result_code = 0; + if (state == State::IDLE and not pll_lock) return result_code; + while (packet == nullptr) { packet = ioFramePool().acquire(); if (!packet) osThreadYield(); @@ -104,9 +63,24 @@ uint8_t NewDecoder::process(bool input, bool pll_lock) if (flag) { switch (buffer) { case 0x7E: - if (packet->size()) { + if (packet->size() > 0) { + // We have started decoding a packet. packet->parse_fcs(); - result_code = packet->ok() ? STATUS_OK : STATUS_CRC_ERROR; + report_bits = bits; + if (packet->ok()) { + // CRC is OK, assume a good packet, even if there are + // extraneous bits decoded. + result_code = STATUS_OK; + } else if (bits == 8) { + // Otherwise, if there is a CRC error but we are on + // an even byte boundary, flag a CRC error. This is + // used by the "pass all" rule. + result_code = STATUS_CRC_ERROR; + } else { + // Extraneous bits mean we have a framing error. + // We should not pass this frame up the stack. + result_code = STATUS_FRAME_ERROR; + } } state = State::SYNC; flag = 0; @@ -127,7 +101,8 @@ uint8_t NewDecoder::process(bool input, bool pll_lock) return result_code; } - switch (state) { + switch (state) + { case State::IDLE: break; @@ -149,10 +124,29 @@ uint8_t NewDecoder::process(bool input, bool pll_lock) } } else { // PLL unlocked. - if (packet->size()) { + // Note the rules here are the same as above. + report_bits = bits; + if (packet->size() > 2) + { packet->parse_fcs(); - result_code = packet->ok() ? STATUS_OK | STATUS_NO_CARRIER : STATUS_NO_CARRIER; + if (packet->ok()) + { + result_code = STATUS_OK; + } + else if (bits == 8) + { + result_code = STATUS_CRC_ERROR; + } + else + { + result_code = STATUS_NO_CARRIER; + } } + else + { + result_code = STATUS_NO_CARRIER; + } + if (state != State::IDLE) { buffer = 0; flag = 0; diff --git a/TNC/HdlcDecoder.hpp b/TNC/HdlcDecoder.hpp index ce6e746..82ac73e 100644 --- a/TNC/HdlcDecoder.hpp +++ b/TNC/HdlcDecoder.hpp @@ -1,8 +1,7 @@ -// Copyright 2015-2019 Mobilinkd LLC +// Copyright 2015-2020 Mobilinkd LLC // All rights reserved. -#ifndef MOBILINKD___HDLC_DECODER_HPP_ -#define MOBILINKD___HDLC_DECODER_HPP_ +#pragma once #include "HdlcFrame.hpp" @@ -10,320 +9,6 @@ namespace mobilinkd { namespace tnc { namespace hdlc { -struct Decoder -{ - static const uint16_t FLAG = 0x7E00; - static const uint16_t ABORT = 0x7F; - static const uint16_t IDLE = 0xFF; - - enum state {SEARCH, HUNT, FRAMING}; - - state state_; - int ones_; - uint16_t buffer_; - IoFrame* frame_; - int bits_; - bool passall_; - bool ready_; - - Decoder(bool pass_all = false); - - void reset() { - state_ = SEARCH; - ones_ = 0; - buffer_ = 0; - if (frame_) frame_->clear(); - ready_ = false; - } - - bool process(bool bit) - { - switch (state_) - { - case SEARCH: - search(bit); - break; - case HUNT: - hunt(bit); - break; - case FRAMING: - frame(bit); - break; - default: - reset(); - break; - } - - return ready_; - } - - /** - * Process a bit. If the bit results in a frame, a result set containing - * the frame, the FCS, and a flag indicating whether it is valid is - * returned. If HDLC was contructed with passall=false, the flag returned - * is always true as only valid frames are returned. - * - * When PLL is passed, when true it indicates that the bit should be - * processed, and when false indicates that any frame in progress should - * be dropped and the state reset to SEARCH. - */ - IoFrame* operator()(bool bit, bool pll); - - void add_bit(bool bit) - { - const uint16_t BIT = 0x8000; - - buffer_ >>= 1; - buffer_ |= (BIT * int(bit)); - bits_ += 1; - } - - char get_char() - { - char result = (buffer_ & 0xFF); - - return result; - } - - void consume_byte() - { - const uint16_t MASK = 0xFF00; - - buffer_ &= MASK; - bits_ -= 8; - } - - void consume_bit() - { - const uint16_t MASK = 0xFF00; - - uint16_t tmp = (buffer_ & 0x7F); - tmp <<= 1; - buffer_ &= MASK; - buffer_ |= tmp; - bits_ -= 1; - } - - void start_search() - { - state_ = SEARCH; - } - - bool have_flag() - { - const uint16_t MASK = 0xFF00; - - return (buffer_ & MASK) == FLAG; - } - - void start_hunt() - { - state_ = HUNT; - bits_ = 0; - buffer_ = 0; - } - - void search(bool bit) - { - add_bit(bit); - - if (have_flag()) - { - start_hunt(); - } - } - - bool have_frame_error() - { - switch (buffer_ & 0xFF00) - { - case 0xFF00: - case 0xFE00: - case 0xFC00: - case 0x7F00: - case 0x7E00: - case 0x3F00: - return true; - default: - return false; - } - } - - bool have_bogon() - { - const uint16_t MASK = 0xFF00; - - if (bits_ != 8) return false; - - const uint16_t test = (buffer_ & MASK); - - switch (test) - { - case 0xFF00: - case 0xFE00: - case 0x7F00: - return true; - default: - return false; - } - } - - void start_frame() - { - state_ = FRAMING; - frame_->clear(); - ones_ = 0; - buffer_ &= 0xFF00; - } - - void hunt(bool bit) - { - const uint16_t MASK = 0xFF00; - - add_bit(bit); - buffer_ &= MASK; - - if (bits_ != 8) return; - - if (have_flag()) { - start_hunt(); - return; - } - - if (have_bogon()) { - start_search(); - return; - } - - if (not have_frame_error()) { - start_frame(); - return; - } - - start_search(); - } - - void frame(bool bit) - { - add_bit(bit); - - if (ones_ < 5) { - if (buffer_ & 0x80) ones_ += 1; - else ones_ = 0; - - if (bits_ == 16) { - if (frame_->push_back(get_char())) { - consume_byte(); - } else { - // Allocation error. - frame_->clear(); - start_search(); - } - } - - if (have_flag()) { - if (frame_->size() >= 15) { - ready_ = true; - } - } - } else { - // 5 ones in a row means the next one should be 0 and be skipped. - - if ((buffer_ & 0x80) == 0) { - ones_ = 0; - consume_bit(); - return; - } else { - - // Framing error. Drop the frame. If there is a FLAG - // in the buffer, go into HUNT otherwise SEARCH. - if (frame_->size() > 15) { - ready_ = true; - return; - } - - if ((buffer_ >> (16 - bits_) & 0xFF) == 0x7E) { - // Cannot call start_hunt() here because we need - // to preserve buffer state. - bits_ -= 8; - state_ = HUNT; - frame_->clear(); - } else { - start_search(); - } - } - } - } - - bool frame_end() - { - uint16_t tmp = (buffer_ >> (16 - bits_)); - return (tmp & 0xFF) == FLAG; - } - - bool frame_abort() - { - uint16_t tmp = (buffer_ >> (16 - bits_)); - return (tmp & 0x7FFF) == 0x7FFF; - } - - void abort_frame() - { - bits_ = 8; - buffer_ &= 0xFF00; - frame_->clear(); - } - - bool ready() const - { - return ready_; - } -}; - -#if 0 - -Decoder::Decoder(bool pass_all) -: state_(SEARCH), ones_(0), buffer_(0), frame_(acquire()) -, bits_(0), passall_(pass_all), ready_(false) -{} - -IoFrame* Decoder::operator()(bool bit, bool pll) -{ - IoFrame* result = nullptr; - - // It appears that the ioFramePool may not get initialized in the proper - // order during some builds. - if (nullptr == frame_) frame_ = acquire(); - - if (not pll) { - if ((state_ == FRAMING) and (frame_->size() > 15) and passall_) { - frame_->parse_fcs(); - if (passall_ or frame_->ok()) { - result = frame_; - ready_ = false; - frame_ = acquire(); - return result; - } - } - reset(); - } else { - if (process(bit)) { - ready_ = false; - frame_->parse_fcs(); - if (passall_ or frame_->ok()) { - result = frame_; - frame_ = acquire(); - return result; - } - frame_->clear(); - } - } - - return result; -} -#endif - - struct NewDecoder { enum class State {IDLE, SYNC, RECEIVE}; @@ -338,15 +23,22 @@ struct NewDecoder static constexpr uint8_t STATUS_NO_CARRIER{0x10}; static constexpr uint8_t STATUS_CRC_ERROR{0x20}; - const uint16_t VALID_CRC = 0xf0b8; + static constexpr uint16_t VALID_CRC = 0xf0b8; State state{State::IDLE}; uint8_t buffer{0}; uint8_t bits{0}; + uint8_t report_bits{0}; uint8_t ones{0}; bool flag{0}; + + /** + * Tell the demodulator to return all "passable" HDLC frames. These + * are frames which consist of an even multiple of eight bits and are + * up to 330 bytes, but which do not have a valid checksum. + */ bool passall{false}; frame_type* packet{nullptr}; @@ -355,10 +47,13 @@ struct NewDecoder : passall(pass_all) {} + bool can_pass(uint8_t status) const + { + return status == STATUS_OK or (passall and status == STATUS_CRC_ERROR); + } + optional_result_type operator()(bool input, bool pll_lock); uint8_t process(bool input, bool pll_lock); }; }}} // mobilinkd::tnc::hdlc - -#endif // MOBILINKD___HDLC_DECODER_HPP_ diff --git a/TNC/IOEventTask.cpp b/TNC/IOEventTask.cpp index ad2792e..69fe5a7 100644 --- a/TNC/IOEventTask.cpp +++ b/TNC/IOEventTask.cpp @@ -12,6 +12,7 @@ #include "Kiss.hpp" #include "KissHardware.hpp" #include "ModulatorTask.hpp" +#include "Modulator.hpp" #include "UsbPort.hpp" #include "SerialPort.hpp" #include "NullPort.hpp" @@ -60,7 +61,6 @@ void startIOEventTask(void const*) hardware.init(); hardware.store(); } -// hardware.init(); osMutexRelease(hardwareInitMutexHandle); @@ -107,7 +107,6 @@ void startIOEventTask(void const*) DEBUG("USB connected -- negotiate"); HAL_GPIO_WritePin(BT_SLEEP_GPIO_Port, BT_SLEEP_Pin, GPIO_PIN_RESET); -// SysClock48(); osTimerStart(usbShutdownTimerHandle, 5000); } } diff --git a/TNC/KissHardware.cpp b/TNC/KissHardware.cpp index 13e29e8..b4669d4 100644 --- a/TNC/KissHardware.cpp +++ b/TNC/KissHardware.cpp @@ -6,7 +6,9 @@ #include "AudioInput.hpp" #include "AudioLevel.hpp" #include "IOEventTask.h" -#include +#include "ModulatorTask.hpp" +#include "Modulator.hpp" +#include "HDLCEncoder.hpp" #include #include @@ -27,17 +29,10 @@ int powerOffViaUSB(void) namespace mobilinkd { namespace tnc { namespace kiss { -const char FIRMWARE_VERSION[] = "2.0.0a1"; +const char FIRMWARE_VERSION[] = "2.0.0b1"; const char HARDWARE_VERSION[] = "Mobilinkd TNC3 2.1.1"; -const std::array modem_type_lookup = { - "NOT SET", - "AFSK1200", - "AFSK300", - "FSK9600", -}; - Hardware& settings() { static Hardware instance __attribute__((section(".bss3"))); @@ -107,9 +102,7 @@ void Hardware::set_duplex(uint8_t value) { update_crc(); } -#if 1 - - void reply8(uint8_t cmd, uint8_t result) { +void reply8(uint8_t cmd, uint8_t result) { uint8_t data[2] { cmd, result }; ioport->write(data, 2, 6, osWaitForever); } @@ -127,17 +120,33 @@ inline void reply(uint8_t cmd, const uint8_t* data, uint16_t len) { ioport->write(buffer, len + 1, 6, osWaitForever); } -inline void reply_ext(uint8_t ext, uint8_t cmd, const uint8_t* data, uint16_t len) { - auto buffer = static_cast(alloca(len + 2)); +template +void reply_ext(const std::array& cmd, const uint8_t* data, uint16_t len) { + auto buffer = static_cast(alloca(len + N)); if (buffer == nullptr) return; - buffer[0] = ext; - buffer[1] = cmd; + std::copy(std::begin(cmd), std::end(cmd), buffer); for (uint16_t i = 0; i != len and data[i] != 0; i++) - buffer[i + 2] = data[i]; + buffer[i + N] = data[i]; ioport->write(buffer, len + 2, 6, osWaitForever); free(buffer); } -#endif + + +template +inline void ext_reply(const std::array& cmd, uint8_t result) { + std::array buffer; + std::copy(std::begin(cmd), std::end(cmd), std::begin(buffer)); + buffer[N] = result; + ioport->write(buffer.data(), N + 1, 6, osWaitForever); +} + +template +void ext_reply(const std::array& cmd, const std::array& result) { + std::array data; + std::copy(std::begin(cmd), std::end(cmd), std::begin(data)); + std::copy(std::begin(result), std::end(result), std::begin(data) + M); + ioport->write(data.data(), M + N, 6, osWaitForever); +} void Hardware::get_alias(uint8_t alias) { uint8_t result[14]; @@ -149,7 +158,7 @@ void Hardware::get_alias(uint8_t alias) { result[11] = aliases[alias].insert_id; result[12] = aliases[alias].preempt; result[13] = aliases[alias].hops; - reply_ext(hardware::EXTENDED_CMD, hardware::EXT_GET_ALIASES, result, 14); + reply_ext(hardware::EXT_GET_ALIASES, result, 14); } void Hardware::set_alias(const hdlc::IoFrame* frame) { @@ -187,14 +196,13 @@ void Hardware::handle_request(hdlc::IoFrame* frame) { switch (command) { -#if 1 case hardware::SAVE: case hardware::SAVE_EEPROM_SETTINGS: update_crc(); store(); reply8(hardware::OK, hardware::SAVE_EEPROM_SETTINGS); break; -#endif + case hardware::POLL_INPUT_LEVEL: DEBUG("POLL_INPUT_VOLUME"); reply8(hardware::POLL_INPUT_LEVEL, 0); @@ -385,26 +393,6 @@ void Hardware::handle_request(hdlc::IoFrame* frame) { reply8(hardware::GET_DUPLEX, duplex); break; - case hardware::SET_MODEM_TYPE: - DEBUG("SET_MODEM_TYPE"); - if ((*it > 0) and (*it < 4)) - { - modem_type = *it; - DEBUG(modem_type_lookup[*it]); - update_crc(); - } - else - { - ERROR("Unknown modem type"); - } - osMessagePut(audioInputQueueHandle, audio::UPDATE_SETTINGS, - osWaitForever); - [[fallthrough]]; - case hardware::GET_MODEM_TYPE: - DEBUG("GET_MODEM_TYPE"); - reply8(hardware::GET_MODEM_TYPE, modem_type); - break; - case hardware::GET_FIRMWARE_VERSION: DEBUG("GET_FIRMWARE_VERSION"); reply(hardware::GET_FIRMWARE_VERSION, (uint8_t*) FIRMWARE_VERSION, @@ -440,6 +428,20 @@ void Hardware::handle_request(hdlc::IoFrame* frame) { options & KISS_OPTION_PTT_SIMPLEX ? 0 : 1); break; + case hardware::SET_PASSALL: + DEBUG("SET_PASSALL"); + if (!*it) { + options &= ~KISS_OPTION_PASSALL; + } else { + options |= KISS_OPTION_PASSALL; + } + update_crc(); + break; + case hardware::GET_PASSALL: + DEBUG("GET_PASSALL"); + reply8(hardware::GET_PASSALL, options & KISS_OPTION_PASSALL ? 1 : 0); + break; + case hardware::SET_USB_POWER_OFF: DEBUG("SET_USB_POWER_OFF"); if (*it) { @@ -491,8 +493,6 @@ void Hardware::handle_request(hdlc::IoFrame* frame) { reply16(hardware::GET_API_VERSION, hardware::KISS_API_VERSION); osMessagePut(audioInputQueueHandle, audio::POLL_BATTERY_LEVEL, osWaitForever); - osMessagePut(audioInputQueueHandle, audio::POLL_TWIST_LEVEL, - osWaitForever); osMessagePut(audioInputQueueHandle, audio::IDLE, osWaitForever); reply(hardware::GET_FIRMWARE_VERSION, (uint8_t*) FIRMWARE_VERSION, @@ -514,6 +514,7 @@ void Hardware::handle_request(hdlc::IoFrame* frame) { reply8(hardware::GET_DUPLEX, duplex); reply8(hardware::GET_PTT_CHANNEL, options & KISS_OPTION_PTT_SIMPLEX ? 0 : 1); + reply8(hardware::GET_PASSALL, options & KISS_OPTION_PASSALL ? 1 : 0); reply16(hardware::GET_CAPABILITIES, hardware::CAP_EEPROM_SAVE|hardware::CAP_BATTERY_LEVEL| hardware::CAP_ADJUST_INPUT|hardware::CAP_DFU_FIRMWARE); @@ -523,43 +524,60 @@ void Hardware::handle_request(hdlc::IoFrame* frame) { reply8(hardware::GET_MAX_INPUT_TWIST, 9); // Constants for this FW reply(hardware::GET_MAC_ADDRESS, mac_address, sizeof(mac_address)); reply(hardware::GET_DATETIME, get_rtc_datetime(), 7); + ext_reply(hardware::EXT_GET_MODEM_TYPE, modem_type); + ext_reply(hardware::EXT_GET_MODEM_TYPES, supported_modem_types); if (*error_message) { reply(hardware::GET_ERROR_MSG, (uint8_t*) error_message, sizeof(error_message)); } break; - case hardware::EXTENDED_CMD: - handle_ext_request(frame); - break; - default: - ERROR("Unknown hardware request"); + if (command > 0xC0) + { + handle_ext_request(frame); + } + else + { + ERROR("Unknown hardware request"); + } } } -inline void ext_reply(uint8_t cmd, uint8_t result) { - uint8_t data[3] { hardware::EXTENDED_CMD, cmd, result }; - ioport->write(data, 3, 6); -} - void Hardware::handle_ext_request(hdlc::IoFrame* frame) { auto it = frame->begin(); ++it; + // Currently only supports 2-byte extended commands. uint8_t ext_command = *it++; switch (ext_command) { - case hardware::EXT_GET_MODEM_TYPE: + + case hardware::EXT_SET_MODEM_TYPE[1]: + DEBUG("SET_MODEM_TYPE"); + if ((*it == hardware::MODEM_TYPE_1200) + or (*it == hardware::MODEM_TYPE_9600)) + { + modem_type = *it; + DEBUG(modem_type_lookup[modem_type]); + update_crc(); + } + else + { + ERROR("Unsupported modem type"); + } + osMessagePut(audioInputQueueHandle, audio::UPDATE_SETTINGS, + osWaitForever); + updateModulator(); + [[fallthrough]]; + case hardware::EXT_GET_MODEM_TYPE[1]: DEBUG("EXT_GET_MODEM_TYPE"); - ext_reply(hardware::EXT_GET_MODEM_TYPE, 1); + ext_reply(hardware::EXT_GET_MODEM_TYPE, modem_type); break; - case hardware::EXT_SET_MODEM_TYPE: - DEBUG("EXT_SET_MODEM_TYPE"); - ext_reply(hardware::EXT_OK, hardware::EXT_SET_MODEM_TYPE); - break; - case hardware::EXT_GET_MODEM_TYPES: + case hardware::EXT_GET_MODEM_TYPES[1]: DEBUG("EXT_GET_MODEM_TYPES"); - ext_reply(hardware::EXT_GET_MODEM_TYPES, 1); + ext_reply(hardware::EXT_GET_MODEM_TYPES, supported_modem_types); break; + default: + ERROR("Unknown extended hardware request"); } } diff --git a/TNC/KissHardware.hpp b/TNC/KissHardware.hpp index 7881a15..1dc3bbb 100644 --- a/TNC/KissHardware.hpp +++ b/TNC/KissHardware.hpp @@ -1,8 +1,7 @@ -// Copyright 2015 Mobilinkd LLC +// Copyright 2015-2020 Mobilinkd LLC // All rights reserved. -#ifndef MOBILINKD__TNC__KISS_HARDWARE_HPP_ -#define MOBILINKD__TNC__KISS_HARDWARE_HPP_ +#pragma once #include "KissHardware.h" #include "Log.h" @@ -35,7 +34,7 @@ namespace hardware { * The major version should be updated whenever non-backwards compatible * changes to the API are made. */ -constexpr const uint16_t KISS_API_VERSION = 0x0200; +constexpr const uint16_t KISS_API_VERSION = 0x0201; constexpr const uint16_t CAP_DCD = 0x0100; constexpr const uint16_t CAP_SQUELCH = 0x0200; @@ -94,9 +93,6 @@ constexpr const uint8_t GET_TIMESLOT = 35; constexpr const uint8_t GET_TXTAIL = 36; constexpr const uint8_t GET_DUPLEX = 37; -constexpr const uint8_t SET_MODEM_TYPE = 38; -constexpr const uint8_t GET_MODEM_TYPE = 39; - constexpr const uint8_t GET_FIRMWARE_VERSION = 40; constexpr const uint8_t GET_HARDWARE_VERSION = 41; constexpr const uint8_t SAVE_EEPROM_SETTINGS = 42; @@ -129,6 +125,9 @@ constexpr const uint8_t GET_BT_POWER_OFF = 78; constexpr const uint8_t SET_PTT_CHANNEL = 79; // Which PTT line to use (currently 0 or 1, constexpr const uint8_t GET_PTT_CHANNEL = 80; // multiplex or simplex) +constexpr const uint8_t SET_PASSALL = 81; // Allow invalid CRC through when +constexpr const uint8_t GET_PASSALL = 82; // true (1). + constexpr const uint8_t GET_MIN_OUTPUT_TWIST = 119; ///< int8_t (may be negative). constexpr const uint8_t GET_MAX_OUTPUT_TWIST = 120; ///< int8_t (may be negative). constexpr const uint8_t GET_MIN_INPUT_TWIST = 121; ///< int8_t (may be negative). @@ -140,34 +139,47 @@ constexpr const uint8_t GET_CAPABILITIES = 126; ///< Send all capabilities. constexpr const uint8_t GET_ALL_VALUES = 127; ///< Send all settings & versions. /** - * Extended commands are two+ bytes in length. They start at 80:00 - * and go through BF:FF (14 significant bits), then proceed to C0:00:00 - * through CF:FF:FF (20 more significant bits). + * Extended commands are two+ bytes in length. They follow the model used + * by UTF-8 to extend the command set. Extended commands start at C1:80 + * and go through DF:BF (11 significant bits), then proceed to E0:80:80 + * through EF:BF:BF (16 significant bits). * - * If needed, the commands can be extended to 9 nibbles (D0 - DF), - * 13 nibbles (E0-EF) and 17 nibbles (F0-FF). + * To avoid special KISS characters, we skip the following byte values in + * the first byte: + * + * - 0xC0 / FEND + * - 0xDB / FESC + * - 0xDC / TFEND + * - 0xDD / TFESC */ -constexpr const uint8_t EXTENDED_CMD = 128; +constexpr const uint8_t EXTENDED_CMD = 0xC1; -constexpr const uint8_t EXT_OK = 0; -constexpr const uint8_t EXT_GET_MODEM_TYPE = 1; -constexpr const uint8_t EXT_SET_MODEM_TYPE = 2; -constexpr const uint8_t EXT_GET_MODEM_TYPES = 3; ///< Return a list of supported modem types +constexpr const uint8_t EXT_OK = 0x80; -constexpr const uint8_t EXT_GET_ALIASES = 8; ///< Number of aliases supported -constexpr const uint8_t EXT_GET_ALIAS = 9; ///< Alias number (uint8_t), 8 characters, 5 bytes (set, use, insert_id, preempt, hops) -constexpr const uint8_t EXT_SET_ALIAS = 10; ///< Alias number (uint8_t), 8 characters, 5 bytes (set, use, insert_id, preempt, hops) +constexpr std::array EXT_GET_MODEM_TYPE = {0xC1, 0x81}; +constexpr std::array EXT_SET_MODEM_TYPE = {0xC1, 0x82}; +constexpr std::array EXT_GET_MODEM_TYPES = {0xC1, 0x83}; ///< Return a list of supported modem types -constexpr const uint8_t EXT_GET_BEACONS = 12; ///< Number of beacons supported -constexpr const uint8_t EXT_GET_BEACON = 13; ///< Beacon number (uint8_t), uint16_t interval in seconds, 3 NUL terminated strings (callsign, path, text) -constexpr const uint8_t EXT_SET_BEACON = 14; ///< Beacon number (uint8_t), uint16_t interval in seconds, 3 NUL terminated strings (callsign, path, text) +constexpr std::array EXT_GET_ALIASES = {0xC1, 0x88}; ///< Number of aliases supported +constexpr std::array EXT_GET_ALIAS = {0xC1, 0x89}; ///< Alias number (uint8_t), 8 characters, 5 bytes (set, use, insert_id, preempt, hops) +constexpr std::array EXT_SET_ALIAS = {0xC1, 0x0A}; ///< Alias number (uint8_t), 8 characters, 5 bytes (set, use, insert_id, preempt, hops) -constexpr const uint8_t MODEM_TYPE_1200 = 1; -constexpr const uint8_t MODEM_TYPE_300 = 2; -constexpr const uint8_t MODEM_TYPE_9600 = 3; -constexpr const uint8_t MODEM_TYPE_PSK31 = 4; -constexpr const uint8_t MODEM_TYPE_OFDM = 5; -constexpr const uint8_t MODEM_TYPE_MFSK16 = 6; +constexpr std::array EXT_GET_BEACON_SLOTS = {0xC1, 0x8C}; ///< Number of beacons supported +constexpr std::array EXT_GET_BEACON = {0xC1, 0x8D}; ///< Beacon number (uint8_t), uint16_t interval in seconds, 3 NUL terminated strings (callsign, path, text) +constexpr std::array EXT_SET_BEACON = {0xC1, 0x8E}; ///< Beacon number (uint8_t), uint16_t interval in seconds, 3 NUL terminated strings (callsign, path, text) + + +/* + * Modem type values 0x00 - 0x7F are single-byte types. Modem type values + * starting at 0xC0 are multi-byte values, and should follow the model used + * by UTF-8 to extend the modem types, should that ever be necessary. + */ +constexpr uint8_t MODEM_TYPE_1200 = 1; +constexpr uint8_t MODEM_TYPE_300 = 2; +constexpr uint8_t MODEM_TYPE_9600 = 3; +constexpr uint8_t MODEM_TYPE_PSK31 = 4; +constexpr uint8_t MODEM_TYPE_OFDM = 5; +constexpr uint8_t MODEM_TYPE_MFSK16 = 6; // Boolean options. #define KISS_OPTION_CONN_TRACK 0x01 @@ -175,6 +187,7 @@ constexpr const uint8_t MODEM_TYPE_MFSK16 = 6; #define KISS_OPTION_VIN_POWER_ON 0x04 // Power on when plugged into USB #define KISS_OPTION_VIN_POWER_OFF 0x08 // Power off when unplugged from USB #define KISS_OPTION_PTT_SIMPLEX 0x10 // Simplex PTT (the default) +#define KISS_OPTION_PASSALL 0x20 // Ignore invalid CRC. const char TOCALL[] = "APML30"; // Update for every feature change. @@ -210,11 +223,24 @@ const size_t NUMBER_OF_BEACONS = 4; // 680 bytes */ struct Hardware { + static constexpr std::array modem_type_lookup = { + "NOT SET", + "AFSK1200", + "AFSK300", + "FSK9600", + }; + + // This must match the constants defined above. enum ModemType { - AFSK1200 = 1, - AFSK300, - FSK9600, - PSK31 + AFSK1200 = hardware::MODEM_TYPE_1200, + AFSK300 = hardware::MODEM_TYPE_300, + FSK9600 = hardware::MODEM_TYPE_9600, + PSK31 = hardware::MODEM_TYPE_PSK31 + }; + + static constexpr std::array supported_modem_types = { + hardware::MODEM_TYPE_1200, + hardware::MODEM_TYPE_9600 }; uint8_t txdelay; ///< How long in 10mS units to wait for TX to settle before starting data @@ -272,7 +298,7 @@ struct Hardware slot = 10; txtail = 1; duplex = 0; - modem_type = ModemType::FSK9600; + modem_type = ModemType::AFSK1200; output_gain = 63; input_gain = 0; // 0-4 on TNC3 tx_twist = 50; @@ -299,13 +325,13 @@ struct Hardware DEBUG("Slot Time: %d", (int)slot); DEBUG("TX Tail: %d", (int)txtail); DEBUG("Duplex: %d", (int)duplex); - DEBUG("Modem Type: %d", (int)modem_type); + DEBUG("Modem Type: %s", modem_type_lookup[modem_type]); DEBUG("TX Gain: %d", (int)output_gain); DEBUG("RX Gain: %d", (int)input_gain); DEBUG("TX Twist: %d", (int)tx_twist); DEBUG("RX Twist: %d", (int)rx_twist); DEBUG("Log Level: %d", (int)log_level); - DEBUG("Options: %d", (int)options); + DEBUG("Options: %04hx", options); DEBUG("MYCALL: %s", (char*) mycall); DEBUG("Dedupe time (secs): %d", (int)dedupe_seconds); DEBUG("Aliases:"); @@ -325,7 +351,7 @@ struct Hardware DEBUG(" text: %s", (char*)b.text); DEBUG(" frequency (secs): %d", (int)b.seconds); } - DEBUG("Checksum: %04x", checksum); + DEBUG("Checksum: %04hx", checksum); } bool load(); @@ -350,7 +376,6 @@ struct Hardware extern Hardware& settings(); - struct I2C_Storage { constexpr static const uint16_t i2c_address{EEPROM_ADDRESS}; @@ -378,5 +403,3 @@ void reply8(uint8_t cmd, uint8_t result) __attribute__((noinline)); void reply16(uint8_t cmd, uint16_t result) __attribute__((noinline)); }}} // mobilinkd::tnc::kiss - -#endif // INMOBILINKD__TNC__KISS_HARDWARE_HPP_C_KISSHARDWARE_HPP_ diff --git a/TNC/Modulator.hpp b/TNC/Modulator.hpp index 9d3982c..417e4f8 100644 --- a/TNC/Modulator.hpp +++ b/TNC/Modulator.hpp @@ -6,6 +6,7 @@ #include "PTT.hpp" #include "KissHardware.hpp" +#include "stm32l4xx_hal.h" #include "cmsis_os.h" #include @@ -88,6 +89,44 @@ struct Modulator virtual void abort() = 0; virtual float bits_per_ms() const = 0; + +protected: + + /** + * Stop the DMA conversion and the timer. Configure DAC for no + * trigger and set the DAC level to exactly mid-level. + * + * @note The DAC is set to mid-level to ensure the audio coupling + * capacitor is kept biased to ensure that there is no DC level + * ramp when we start conversions. This is bad for FSK. + */ + void stop_conversion() + { + HAL_DAC_Stop_DMA(&hdac1, DAC_CHANNEL_1); + HAL_TIM_Base_Stop(&htim7); + + DAC_ChannelConfTypeDef sConfig; + + sConfig.DAC_SampleAndHold = DAC_SAMPLEANDHOLD_DISABLE; + sConfig.DAC_Trigger = DAC_TRIGGER_NONE; + sConfig.DAC_OutputBuffer = DAC_OUTPUTBUFFER_ENABLE; + sConfig.DAC_ConnectOnChipPeripheral = DAC_CHIPCONNECT_ENABLE; + sConfig.DAC_UserTrimming = DAC_TRIMMING_FACTORY; + if (HAL_DAC_ConfigChannel(&hdac1, &sConfig, DAC_CHANNEL_1) != HAL_OK) + { + CxxErrorHandler(); + } + + if (HAL_DAC_SetValue(&hdac1, DAC_CHANNEL_1, DAC_ALIGN_12B_R, 2048) != HAL_OK) + { + CxxErrorHandler(); + } + if (HAL_DAC_Start(&hdac1, DAC_CHANNEL_1) != HAL_OK) + { + CxxErrorHandler(); + } + } + }; }} // mobilinkd::tnc diff --git a/TNC/ModulatorTask.cpp b/TNC/ModulatorTask.cpp index 73d7136..6705a5e 100644 --- a/TNC/ModulatorTask.cpp +++ b/TNC/ModulatorTask.cpp @@ -2,6 +2,8 @@ // All rights reserved. #include "ModulatorTask.hpp" +#include "HDLCEncoder.hpp" +#include "Modulator.hpp" #include "Fsk9600Modulator.hpp" #include "AFSKModulator.hpp" #include "KissHardware.hpp" @@ -50,12 +52,12 @@ mobilinkd::tnc::Modulator& getModulator() case kiss::Hardware::ModemType::AFSK1200: return afsk1200modulator; default: - _Error_Handler(__FILE__, __LINE__); + CxxErrorHandler(); } } mobilinkd::tnc::hdlc::Encoder& getEncoder() { - static mobilinkd::tnc::hdlc::Encoder instance(hdlcOutputQueueHandle, &getModulator()); + static mobilinkd::tnc::hdlc::Encoder instance(hdlcOutputQueueHandle); return instance; } @@ -81,6 +83,16 @@ void updatePtt() getModulator().set_ptt(&multiplexPtt); } +void updateModulator() +{ + using namespace mobilinkd::tnc::kiss; + + modulator = &getModulator(); + modulator->init(settings()); + updatePtt(); + encoder->updateModulator(); +} + void startModulatorTask(void const*) { using namespace mobilinkd::tnc::kiss; diff --git a/TNC/ModulatorTask.hpp b/TNC/ModulatorTask.hpp index b52c98c..903602c 100644 --- a/TNC/ModulatorTask.hpp +++ b/TNC/ModulatorTask.hpp @@ -1,12 +1,8 @@ -// Copyright 2015 Mobilinkd LLC +// Copyright 2015-2020 Mobilinkd LLC // All rights reserved. +#pragma once -#ifndef MOBILINKD__MODULATOR_TASK_HPP_ -#define MOBILINKD__MODULATOR_TASK_HPP_ - -#include "HDLCEncoder.hpp" -#include "Modulator.hpp" #include "PTT.hpp" #include "cmsis_os.h" @@ -14,10 +10,21 @@ extern "C" { #endif +namespace mobilinkd { namespace tnc { + +class Modulator; + +namespace hdlc { +class Encoder; +} + +}} + extern mobilinkd::tnc::SimplexPTT simplexPtt; extern mobilinkd::tnc::MultiplexPTT multiplexPtt; mobilinkd::tnc::Modulator& getModulator(); +mobilinkd::tnc::hdlc::Encoder& getEncoder(); void startModulatorTask(void const * argument); @@ -26,9 +33,8 @@ enum class PTT {SIMPLEX, MULTIPLEX}; void setPtt(PTT ptt); void updatePtt(void); +void updateModulator(void); #ifdef __cplusplus } #endif - -#endif // MOBILINKD__MODULATOR_TASK_HPP_