From e4f659d9dadb69ef9f9457cdcf5a9bb63538ff08 Mon Sep 17 00:00:00 2001 From: sq8vps Date: Tue, 22 Aug 2023 13:32:07 +0200 Subject: [PATCH 01/29] working on more modems --- Inc/drivers/modem.h | 40 +++-- Src/ax25.c | 10 +- Src/drivers/modem.c | 382 ++++++++++++++++++++++++++++++-------------- Src/main.c | 14 +- 4 files changed, 301 insertions(+), 145 deletions(-) diff --git a/Inc/drivers/modem.h b/Inc/drivers/modem.h index 19bb73a..2530f33 100644 --- a/Inc/drivers/modem.h +++ b/Inc/drivers/modem.h @@ -20,13 +20,18 @@ along with VP-Digi. If not, see . #include -//number of parallel demodulators +//number of maximum parallel demodulators //each demodulator must be explicitly configured in code -#define MODEM_DEMODULATOR_COUNT 2 +//currently used only for 1200 Bd modem +#define MODEM_MAX_DEMODULATOR_COUNT 2 -#define MODEM_BAUDRATE 1200.f -#define MODEM_MARK_FREQUENCY 1200.f -#define MODEM_SPACE_FREQUENCY 2200.f +enum ModemType +{ + MODEM_1200 = 0, + MODEM_1200_V23, + MODEM_300, + MODEM_9600, +}; enum ModemTxTestMode { @@ -39,26 +44,39 @@ enum ModemTxTestMode struct ModemDemodConfig { + enum ModemType modem; uint8_t usePWM : 1; //0 - use R2R, 1 - use PWM uint8_t flatAudioIn : 1; //0 - normal (deemphasized) audio input, 1 - flat audio (unfiltered) input }; extern struct ModemDemodConfig ModemConfig; -enum ModemEmphasis +enum ModemPrefilter { - PREEMPHASIS, - DEEMPHASIS, - EMPHASIS_NONE + PREFILTER_PREEMPHASIS, + PREFILTER_DEEMPHASIS, + PREFILTER_FLAT, + PREFILTER_NONE, }; +/** + * @brief Get current modem baudrate + * @return Baudrate + */ +float ModemGetBaudrate(void); + +/** + * @brief Get count of demodulators running in parallel + * @return Count of demodulators + */ +uint8_t ModemGetDemodulatorCount(void); /** - * @brief Get filter type (preemphasis, deemphasis etc.) for given modem + * @brief Get prefilter type (preemphasis, deemphasis etc.) for given modem * @param modem Modem number * @return Filter type */ -enum ModemEmphasis ModemGetFilterType(uint8_t modem); +enum ModemPrefilter ModemGetFilterType(uint8_t modem); /** * @brief Get current DCD state diff --git a/Src/ax25.c b/Src/ax25.c index 4dd5e10..83fe662 100644 --- a/Src/ax25.c +++ b/Src/ax25.c @@ -105,7 +105,7 @@ struct RxState uint8_t frameReceived; //frame received flag }; -static volatile struct RxState rxState[MODEM_DEMODULATOR_COUNT]; +static volatile struct RxState rxState[MODEM_MAX_DEMODULATOR_COUNT]; static uint16_t lastCrc = 0; //CRC of the last received frame. If not 0, a frame was successfully received static uint16_t rxMultiplexDelay = 0; //simple delay for decoder multiplexer to avoid receiving the same frame twice @@ -229,11 +229,11 @@ void Ax25BitParse(uint8_t bit, uint8_t modem) if(lastCrc != 0) //there was a frame received { rxMultiplexDelay++; - if(rxMultiplexDelay > (4 * MODEM_DEMODULATOR_COUNT)) //hold it for a while and wait for other decoders to receive the frame + if(rxMultiplexDelay > (4 * MODEM_MAX_DEMODULATOR_COUNT)) //hold it for a while and wait for other decoders to receive the frame { lastCrc = 0; rxMultiplexDelay = 0; - for(uint8_t i = 0; i < MODEM_DEMODULATOR_COUNT; i++) + for(uint8_t i = 0; i < MODEM_MAX_DEMODULATOR_COUNT; i++) { frameReceived |= ((rxState[i].frameReceived > 0) << i); rxState[i].frameReceived = 0; @@ -588,6 +588,6 @@ void Ax25Init(void) for(uint8_t i = 0; i < (sizeof(rxState) / sizeof(rxState[0])); i++) rxState[i].crc = 0xFFFF; - txDelay = ((float)Ax25Config.txDelayLength / (8.f * 1000.f / (float)MODEM_BAUDRATE)); //change milliseconds to byte count - txTail = ((float)Ax25Config.txTailLength / (8.f * 1000.f / (float)MODEM_BAUDRATE)); + txDelay = ((float)Ax25Config.txDelayLength / (8.f * 1000.f / ModemGetBaudrate())); //change milliseconds to byte count + txTail = ((float)Ax25Config.txTailLength / (8.f * 1000.f / ModemGetBaudrate())); } diff --git a/Src/drivers/modem.c b/Src/drivers/modem.c index ba419af..ec9497b 100644 --- a/Src/drivers/modem.c +++ b/Src/drivers/modem.c @@ -35,17 +35,42 @@ along with VP-Digi. If not, see . * The DCD mechanism is described in afsk_demod(). * All values were selected by trial and error */ -#define DCD_MAXPULSE 100 -#define DCD_THRES 30 -#define DCD_DEC 1 -#define DCD_INC 7 -#define DCD_PLLTUNE 0 +#define DCD1200_MAXPULSE 100 +#define DCD1200_THRES 30 +#define DCD1200_DEC 1 +#define DCD1200_INC 7 +#define DCD1200_PLLTUNE 0 + +#define DCD9600_MAXPULSE 200 +#define DCD9600_THRES 80 +#define DCD9600_DEC 3 +#define DCD9600_INC 6 +#define DCD9600_PLLTUNE 0 + +#define DCD300_MAXPULSE 50 +#define DCD300_THRES 6 +#define DCD300_DEC 1 +#define DCD300_INC 5 +#define DCD300_PLLTUNE 0 + +#define N1200 8 //samples per symbol +#define N9600 4 +#define N300 16 +#define NMAX 16 //keep this value equal to the biggest Nx + +#define PLL1200_STEP (((uint64_t)1 << 32) / N1200) //PLL tick increment value +#define PLL9600_STEP (((uint64_t)1 << 32) / N9600) +#define PLL300_STEP (((uint64_t)1 << 32) / N300) + +#define PLL1200_LOCKED_TUNE 0.74f +#define PLL1200_NOT_LOCKED_TUNE 0.50f +#define PLL9600_LOCKED_TUNE 0.89f +#define PLL9600_NOT_LOCKED_TUNE 0.50f //I have 0.67 noted somewhere as possibly better value +#define PLL300_LOCKED_TUNE 0.74f +#define PLL300_NOT_LOCKED_TUNE 0.50f -#define N 8 //samples per symbol #define DAC_SINE_SIZE 32 //DAC sine table size -#define PLLINC 536870912 //PLL tick increment value -#define PLLLOCKED 0.74 //PLL adjustment value when locked -#define PLLNOTLOCKED 0.50 //PLL adjustment value when not locked + #define PTT_ON GPIOB->BSRR = GPIO_BSRR_BS7 #define PTT_OFF GPIOB->BSRR = GPIO_BSRR_BR7 @@ -54,22 +79,27 @@ along with VP-Digi. If not, see . struct ModemDemodConfig ModemConfig; +static uint8_t N; //samples per symbol static enum ModemTxTestMode txTestState; //current TX test mode +static uint8_t demodCount; //actual number of parallel demodulators static uint16_t dacSine[DAC_SINE_SIZE]; //sine samples for DAC static uint8_t dacSineIdx; //current sine sample index static uint16_t samples[4]; //very raw received samples, filled directly by DMA static uint8_t currentSymbol; //current symbol for NRZI encoding -static uint8_t markFreq; //mark frequency (inter-sample interval) -static uint8_t spaceFreq; //space frequency (inter-sample interval) -static uint16_t baudRate; //baudrate -static int32_t coeffHiI[N], coeffLoI[N], coeffHiQ[N], coeffLoQ[N]; //correlator IQ coefficients +static float markFreq; //mark frequency +static float spaceFreq; //space frequency +static float baudRate; //baudrate +static uint8_t markStep; //mark timer step +static uint8_t spaceStep; //space timer step +static uint16_t baudRateStep; //baudrate timer step +static int32_t coeffHiI[NMAX], coeffLoI[NMAX], coeffHiQ[NMAX], coeffLoQ[NMAX]; //correlator IQ coefficients static uint8_t dcd = 0; //multiplexed DCD state from both demodulators /** * @brief BPF filter with 2200 Hz tone 6 dB preemphasis (it actually attenuates 1200 Hz tone by 6 dB) */ -static const int16_t bpfCoeffs[8] = +static int16_t bpf1200[8] = { 728, -13418, @@ -84,7 +114,7 @@ static const int16_t bpfCoeffs[8] = /** * @brief BPF filter with 2200 Hz tone 6 dB deemphasis */ -static const int16_t invBpfCoeffs[8] = +static int16_t bpf1200Inv[8] = { -10513, -10854, @@ -96,14 +126,29 @@ static const int16_t invBpfCoeffs[8] = -879 }; -#define BPF_TAPS (sizeof(bpfCoeffs) / sizeof(*bpfCoeffs) > sizeof(invBpfCoeffs) / sizeof(*invBpfCoeffs) ? \ - sizeof(bpfCoeffs) / sizeof(*bpfCoeffs) : sizeof(invBpfCoeffs) / sizeof(*invBpfCoeffs)) +//fs=4800, rectangular, fc1=1400, fc2=2000, 0 dB @ 1600 Hz and 1800 Hz, N = 15, gain 65536 +static int16_t bpf300[15] = +{ + -2327, 3557, 1048, -9284, 12210, -3989, -9849, 17268, -9849, -3989, 12210, -9284, 1048, 3557, -2327, +}; -/** - * @brief Output LPF filter to remove data faster than 1200 baud - * It actually is a 600 Hz filter: symbols can change at 1200 Hz, but it takes 2 "ticks" to return to the same symbol - that's why it's 600 Hz - */ -static const int16_t lpfCoeffs[15] = +#define BPF_MAX_TAPS 15 + +//fs=4800 Hz, raised cosine, fc=300 Hz (BR=600 Bd), beta=0.8, N=14, gain=65536 +static int16_t lpf300[14] = +{ + 741, 1834, 3216, 4756, 6268, 7547, 8402, 8402, 7547, 6268, 4756, 3216, 1834, 741, +}; + +#ifdef EXPERIMENTAL_LPF1200 +//fs=9600 Hz, raised cosine, fc=600Hz (BR=1200 Bd), beta=0.8, N=14, gain=65536 +static const int16_t lpf1200[14] = +{ + 741, 1834, 3216, 4756, 6268, 7547, 8402, 8402, 7547, 6268, 4756, 3216, 1834, 741, +}; +#define LPF_MAX_TAPS 14 +#else +static int16_t lpf1200[15] = { -6128, -5974, @@ -122,34 +167,86 @@ static const int16_t lpfCoeffs[15] = -6128 }; -#define LPF_TAPS (sizeof(lpfCoeffs) / sizeof(*lpfCoeffs)) +#define LPF_MAX_TAPS 15 + +#endif + +//fs=38400 Hz, Gaussian, fc=4800 Hz (9600 Bd), N=9, gain=65536 +//seems like there is no difference between N=9 and any higher order +static int16_t lpf9600[9] = {497, 2360, 7178, 13992, 17478, 13992, 7178, 2360, 497}; + + +#define FILTER_MAX_TAPS ((LPF_MAX_TAPS > BPF_MAX_TAPS) ? LPF_MAX_TAPS : BPF_MAX_TAPS) +struct Filter +{ + int16_t *coeffs; + uint8_t taps; + int32_t samples[FILTER_MAX_TAPS]; + uint8_t gainShift; +}; struct DemodState { - enum ModemEmphasis emphasis; //preemphasis/deemphasis uint8_t rawSymbols; //raw, unsynchronized symbols uint8_t syncSymbols; //synchronized symbols - int16_t rawSample[BPF_TAPS]; //input (raw) samples - int32_t rxSample[BPF_TAPS]; //rx samples after pre/deemphasis filter - uint8_t rxSampleIdx; //index for the array above - int64_t lpfSample[LPF_TAPS]; //rx samples after final filtering + + enum ModemPrefilter prefilter; + struct Filter bpf; + int32_t correlatorSamples[NMAX]; + uint8_t correlatorSamplesIdx; + struct Filter lpf; + uint8_t dcd : 1; //DCD state uint64_t RMSenergy; //frame energy counter (sum of samples squared) uint32_t RMSsampleCount; //number of samples for RMS + int32_t pll; //bit recovery PLL counter - int32_t lastPll; //last bit recovery PLL counter value + int32_t pllStep; + float pllLockedAdjust; + float pllNotLockedAdjust; + int32_t dcdPll; //DCD PLL main counter uint8_t dcdLastSymbol; //last symbol for DCD uint8_t dcdCounter; //DCD "pulse" counter (incremented when RX signal is correct) + int32_t dcdMax; + int32_t dcdThres; + int32_t dcdInc; + int32_t dcdDec; + float dcdAdjust; }; -static volatile struct DemodState demodState[MODEM_DEMODULATOR_COUNT]; +static struct DemodState demodState[MODEM_MAX_DEMODULATOR_COUNT]; static void decode(uint8_t symbol, uint8_t demod); static int32_t demodulate(int16_t sample, struct DemodState *dem); static void setPtt(uint8_t state); +static int32_t filter(struct Filter *filter, int32_t input) +{ + int32_t out = 0; + + for(uint8_t i = filter->taps - 1; i > 0; i--) + filter->samples[i] = filter->samples[i - 1]; //shift old samples + + filter->samples[0] = input; //store new sample + for(uint8_t i = 0; i < filter->taps; i++) + { + out += filter->coeffs[i] * filter->samples[i]; + } + return out >> filter->gainShift; +} + +float ModemGetBaudrate(void) +{ + return baudRate; +} + +uint8_t ModemGetDemodulatorCount(void) +{ + return demodCount; +} + uint8_t ModemDcdState(void) { return dcd; @@ -176,9 +273,9 @@ uint16_t ModemGetRMS(uint8_t modem) return sqrtf((float)demodState[modem].RMSenergy / (float)demodState[modem].RMSsampleCount); } -enum ModemEmphasis ModemGetFilterType(uint8_t modem) +enum ModemPrefilter ModemGetFilterType(uint8_t modem) { - return demodState[modem].emphasis; + return demodState[modem].prefilter; } /** @@ -213,14 +310,14 @@ void DMA1_Channel2_IRQHandler(void) int32_t sample = ((samples[0] + samples[1] + samples[2] + samples[3]) >> 1) - 4095; //calculate input sample (decimation) - uint8_t partialDcd = 0; + bool partialDcd = false; - for(uint8_t i = 0; i < MODEM_DEMODULATOR_COUNT; i++) + for(uint8_t i = 0; i < demodCount; i++) { uint8_t symbol = (demodulate(sample, (struct DemodState*)&demodState[i]) > 0); //demodulate sample decode(symbol, i); //recover bits, decode NRZI and call higher level function if(demodState[i].dcd) - partialDcd |= 1; + partialDcd = true; } if(partialDcd) //DCD on any of the demodulators @@ -285,9 +382,9 @@ void TIM3_IRQHandler(void) TIM1->CNT = 0; if(currentSymbol) //current symbol is space - TIM1->ARR = spaceFreq; + TIM1->ARR = spaceStep; else //mark - TIM1->ARR = markFreq; + TIM1->ARR = markStep; } @@ -300,48 +397,37 @@ void TIM3_IRQHandler(void) */ static int32_t demodulate(int16_t sample, struct DemodState *dem) { - dem->RMSenergy += ((sample >> 1) * (sample >> 1)); //square the sample and add it to the sum dem->RMSsampleCount++; //increment number of samples - if(dem->emphasis != EMPHASIS_NONE) //preemphasis/deemphasis is used + if(dem->prefilter != PREFILTER_NONE) //filter is used { - int32_t out = 0; //filtered output - - for(uint8_t i = BPF_TAPS - 1; i > 0; i--) - dem->rawSample[i] = dem->rawSample[i - 1]; //shift old samples - - dem->rawSample[0] = sample; //store new sample - for(uint8_t i = 0; i < BPF_TAPS; i++) - { - if(dem->emphasis == PREEMPHASIS) - out += bpfCoeffs[i] * dem->rawSample[i]; //use preemphasis - else - out += invBpfCoeffs[i] * dem->rawSample[i]; //use deemphasis - } - dem->rxSample[dem->rxSampleIdx] = (out >> 15); //store filtered sample + dem->correlatorSamples[dem->correlatorSamplesIdx++] = filter(&dem->bpf, sample); } else //no pre/deemphasis { - dem->rxSample[dem->rxSampleIdx] = sample; //store incoming sample + dem->correlatorSamples[dem->correlatorSamplesIdx++] = sample; } - dem->rxSampleIdx = (dem->rxSampleIdx + 1) % BPF_TAPS; //increment sample pointer and wrap around if needed + dem->correlatorSamplesIdx %= N; int64_t outLoI = 0, outLoQ = 0, outHiI = 0, outHiQ = 0; //output values after correlating - for(uint8_t i = 0; i < N; i++) { - int32_t t = dem->rxSample[(dem->rxSampleIdx + i) % BPF_TAPS]; //read sample + for(uint8_t i = 0; i < N; i++) + { + int32_t t = dem->correlatorSamples[(dem->correlatorSamplesIdx + i) % N]; //read sample outLoI += t * coeffLoI[i]; //correlate sample outLoQ += t * coeffLoQ[i]; outHiI += t * coeffHiI[i]; outHiQ += t * coeffHiQ[i]; } - uint64_t hi = 0, lo = 0; - - hi = ((outHiI >> 12) * (outHiI >> 12)) + ((outHiQ >> 12) * (outHiQ >> 12)); //calculate output tone levels - lo = ((outLoI >> 12) * (outLoI >> 12)) + ((outLoQ >> 12) * (outLoQ >> 12)); + outHiI >>= 12; + outHiQ >>= 12; + outLoI >>= 12; + outLoQ >>= 12; + uint64_t hi = (outHiI * outHiI) + (outHiQ * outHiQ); //calculate output tone levels + uint64_t lo = (outLoI * outLoI) + (outLoQ * outLoQ); //DCD using PLL //PLL is running nominally at 1200 Hz (= baudrate) @@ -355,45 +441,33 @@ static int32_t demodulate(int16_t sample, struct DemodState *dem) //when configured properly, it's generally immune to noise, as the detected tone changes much faster than 1200 baud //it's also important to set some maximum value for DCD counter, otherwise the DCD is "sticky" - dem->dcdPll = (signed)((unsigned)(dem->dcdPll) + ((unsigned)PLLINC)); //keep PLL ticking at the frequency equal to baudrate + dem->dcdPll = (signed)((unsigned)(dem->dcdPll) + ((unsigned)dem->pll)); //keep PLL ticking at the frequency equal to baudrate uint8_t dcdSymbol = (hi > lo); //get current symbol if(dcdSymbol != dem->dcdLastSymbol) //tone changed { - if(abs(dem->dcdPll) < PLLINC) //tone change occurred near zero - dem->dcdCounter += DCD_INC; //increase DCD counter + if(abs(dem->dcdPll) < dem->dcdInc) //tone change occurred near zero + dem->dcdCounter += dem->dcdInc; //increase DCD counter else //tone change occurred far from zero { - if(dem->dcdCounter >= DCD_DEC) //avoid overflow - dem->dcdCounter -= DCD_DEC; //decrease DCD counter + if(dem->dcdCounter >= dem->dcdDec) //avoid overflow + dem->dcdCounter -= dem->dcdDec; //decrease DCD counter } - dem->dcdPll = (int)(dem->dcdPll * DCD_PLLTUNE); //adjust PLL + dem->dcdPll = (int)(dem->dcdPll * dem->dcdAdjust); //adjust PLL } dem->dcdLastSymbol = dcdSymbol; //store last symbol for symbol change detection - if(dem->dcdCounter > DCD_MAXPULSE) //maximum DCD counter value reached - dem->dcdCounter = DCD_MAXPULSE; //avoid "sticky" DCD and counter overflow + if(dem->dcdCounter > dem->dcdMax) //maximum DCD counter value reached + dem->dcdCounter = dem->dcdMax; //avoid "sticky" DCD and counter overflow - if(dem->dcdCounter > DCD_THRES) //DCD threshold reached + if(dem->dcdCounter > dem->dcdThres) //DCD threshold reached dem->dcd = 1; //DCD! else //below DCD threshold dem->dcd = 0; //no DCD - //filter out signal faster than 1200 baud - int64_t out = 0; - - for(uint8_t i = LPF_TAPS - 1; i > 0; i--) - dem->lpfSample[i] = dem->lpfSample[i - 1]; - - dem->lpfSample[0] = (int64_t)hi - (int64_t)lo; - for(uint8_t i = 0; i < LPF_TAPS; i++) - { - out += lpfCoeffs[i] * dem->lpfSample[i]; - } - - return out > 0; + return filter(&dem->lpf, hi - lo) > 0; } @@ -411,15 +485,15 @@ static void decode(uint8_t symbol, uint8_t demod) //This function provides bit/clock recovery and NRZI decoding //Bit recovery is based on PLL which is described in the function above (DCD PLL) //Current symbol is sampled at PLL counter overflow, so symbol transition should occur at PLL counter zero - dem->lastPll = dem->pll; //store last clock state + int32_t previous = dem->pll; //store last clock state - dem->pll = (signed)((unsigned)(dem->pll) + (unsigned)PLLINC); //keep PLL running + dem->pll = (signed)((unsigned)(dem->pll) + (unsigned)dem->pllStep); //keep PLL running dem->rawSymbols <<= 1; //store received unsynchronized symbol dem->rawSymbols |= (symbol & 1); - if ((dem->pll < 0) && (dem->lastPll > 0)) //PLL counter overflow, sample symbol, decode NRZI and process in higher layer + if ((dem->pll < 0) && (previous > 0)) //PLL counter overflow, sample symbol, decode NRZI and process in higher layer { dem->syncSymbols <<= 1; //shift recovered (received, synchronized) bit register @@ -446,11 +520,11 @@ static void decode(uint8_t symbol, uint8_t demod) if(Ax25GetRxStage(demod) != RX_STAGE_FRAME) //not in a frame { - dem->pll = (int)(dem->pll * PLLNOTLOCKED); //adjust PLL faster + dem->pll = (int)(dem->pll * dem->pllNotLockedAdjust); //adjust PLL faster } else //in a frame { - dem->pll = (int)(dem->pll * PLLLOCKED); //adjust PLL slower + dem->pll = (int)(dem->pll * dem->pllLockedAdjust); //adjust PLL slower } } @@ -478,17 +552,17 @@ void ModemTxTestStart(enum ModemTxTestMode type) if(type == TEST_MARK) { - TIM1->ARR = markFreq; + TIM1->ARR = markStep; } else if(type == TEST_SPACE) { - TIM1->ARR = spaceFreq; + TIM1->ARR = spaceStep; } else //alternating tones { //enable baudrate generator TIM3->PSC = 71; //72/72=1 MHz TIM3->DIER = TIM_DIER_UIE; //enable interrupt - TIM3->ARR = baudRate; //set timer interval + TIM3->ARR = baudRateStep; //set timer interval TIM3->CR1 = TIM_CR1_CEN; //enable timer NVIC_EnableIRQ(TIM3_IRQn); //enable interrupt in NVIC } @@ -521,7 +595,7 @@ void ModemTransmitStart(void) TIM3->PSC = 71; TIM3->DIER |= TIM_DIER_UIE; - TIM3->ARR = baudRate; + TIM3->ARR = baudRateStep; TIM3->CR1 = TIM_CR1_CEN; TIM1->CR1 = TIM_CR1_CEN; @@ -565,12 +639,102 @@ static void setPtt(uint8_t state) } - /** * @brief Initialize AFSK module */ void ModemInit(void) { + memset(demodState, 0, sizeof(demodState)); + + if((ModemConfig.modem == MODEM_1200) || (ModemConfig.modem == MODEM_1200_V23)) + { + demodCount = 2; + N = N1200; + baudRate = 1200.f; + + demodState[0].pllStep = PLL1200_STEP; + demodState[0].pllLockedAdjust = PLL1200_LOCKED_TUNE; + demodState[0].pllNotLockedAdjust = PLL1200_NOT_LOCKED_TUNE; + demodState[0].dcdMax = DCD1200_MAXPULSE; + demodState[0].dcdThres = DCD1200_THRES; + demodState[0].dcdInc = DCD1200_INC; + demodState[0].dcdDec = DCD1200_DEC; + demodState[0].dcdAdjust = DCD1200_PLLTUNE; + + demodState[1].pllStep = PLL1200_STEP; + demodState[1].pllLockedAdjust = PLL1200_LOCKED_TUNE; + demodState[1].pllNotLockedAdjust = PLL1200_NOT_LOCKED_TUNE; + demodState[1].dcdMax = DCD1200_MAXPULSE; + demodState[1].dcdThres = DCD1200_THRES; + demodState[1].dcdInc = DCD1200_INC; + demodState[1].dcdDec = DCD1200_DEC; + demodState[1].dcdAdjust = DCD1200_PLLTUNE; + + demodState[0].prefilter = PREFILTER_NONE; + demodState[0].lpf.coeffs = lpf1200; + demodState[0].lpf.taps = sizeof(lpf1200) / sizeof(*lpf1200); + demodState[0].lpf.gainShift = 0; //not important, output is always compared with 0 + demodState[1].lpf.coeffs = lpf1200; + demodState[1].lpf.taps = sizeof(lpf1200) / sizeof(*lpf1200); + demodState[1].lpf.gainShift = 0; //not important, output is always compared with 0 + + if(ModemConfig.flatAudioIn) //when used with flat audio input, use deemphasis and flat modems + { + demodState[1].prefilter = PREFILTER_DEEMPHASIS; + demodState[1].bpf.coeffs = bpf1200Inv; + demodState[1].bpf.taps = sizeof(bpf1200Inv) / sizeof(*bpf1200Inv); + demodState[1].bpf.gainShift = 15; + } + else //when used with normal (filtered) audio input, use flat and preemphasis modems + { + demodState[1].prefilter = PREFILTER_PREEMPHASIS; + demodState[1].bpf.coeffs = bpf1200; + demodState[1].bpf.taps = sizeof(bpf1200) / sizeof(*bpf1200); + demodState[1].bpf.gainShift = 15; + } + + if(ModemConfig.modem == MODEM_1200) //Bell 202 + { + markFreq = 1200.f; + spaceFreq = 2200.f; + } + else //V.23 + { + markFreq = 1300.f; + spaceFreq = 2100.f; + } + } + else if(ModemConfig.modem == MODEM_300) + { + demodCount = 1; + N = N300; + baudRate = 300.f; + markFreq = 1600.f; + markFreq = 1800.f; + + demodState[0].pllStep = PLL300_STEP; + demodState[0].pllLockedAdjust = PLL300_LOCKED_TUNE; + demodState[0].pllNotLockedAdjust = PLL300_NOT_LOCKED_TUNE; + demodState[0].dcdMax = DCD300_MAXPULSE; + demodState[0].dcdThres = DCD300_THRES; + demodState[0].dcdInc = DCD300_INC; + demodState[0].dcdDec = DCD300_DEC; + demodState[0].dcdAdjust = DCD300_PLLTUNE; + + demodState[0].prefilter = PREFILTER_FLAT; + demodState[0].bpf.coeffs = bpf300; + demodState[0].bpf.taps = sizeof(bpf300) / sizeof(*bpf300); + demodState[0].bpf.gainShift = 16; + demodState[0].lpf.coeffs = lpf300; + demodState[0].lpf.taps = sizeof(lpf300) / sizeof(*lpf300); + demodState[0].lpf.gainShift = 0; //not important, output is always compared with 0 + } + + markStep = 4000000 / (DAC_SINE_SIZE * markFreq) - 1; + spaceStep = 4000000 / (DAC_SINE_SIZE * spaceFreq) - 1; + baudRateStep = 1000000 / baudRate - 1; + + /** * TIM1 is used for pushing samples to DAC (R2R or PWM) at 4 MHz * TIM3 is the baudrate generator for TX running at 1 MHz @@ -647,19 +811,14 @@ void ModemInit(void) TIM2->ARR = 103; //4MHz / 104 =~38400 Hz (4*9600 Hz for 4x oversampling) TIM2->CR1 |= TIM_CR1_CEN; //enable timer - markFreq = 4000000 / (DAC_SINE_SIZE * (uint32_t)MODEM_MARK_FREQUENCY) - 1; //set mark frequency - spaceFreq = 4000000 / (DAC_SINE_SIZE * (uint32_t)MODEM_SPACE_FREQUENCY) - 1; //set space frequency - baudRate = 1000000 / (uint32_t)MODEM_BAUDRATE - 1; //set baudrate - for(uint8_t i = 0; i < N; i++) //calculate correlator coefficients { - coeffLoI[i] = 4095.f * cosf(2.f * 3.1416f * (float)i / (float)N * MODEM_MARK_FREQUENCY / MODEM_BAUDRATE); - coeffLoQ[i] = 4095.f * sinf(2.f * 3.1416f * (float)i / (float)N * MODEM_MARK_FREQUENCY / MODEM_BAUDRATE); - coeffHiI[i] = 4095.f * cosf(2.f * 3.1416f * (float)i / (float)N * MODEM_SPACE_FREQUENCY / MODEM_BAUDRATE); - coeffHiQ[i] = 4095.f * sinf(2.f * 3.1416f * (float)i / (float)N * MODEM_SPACE_FREQUENCY / MODEM_BAUDRATE); + coeffLoI[i] = 4095.f * cosf(2.f * 3.1416f * (float)i / (float)N * markFreq / baudRate); + coeffLoQ[i] = 4095.f * sinf(2.f * 3.1416f * (float)i / (float)N * markFreq / baudRate); + coeffHiI[i] = 4095.f * cosf(2.f * 3.1416f * (float)i / (float)N * spaceFreq / baudRate); + coeffHiQ[i] = 4095.f * sinf(2.f * 3.1416f * (float)i / (float)N * spaceFreq / baudRate); } - for(uint8_t i = 0; i < DAC_SINE_SIZE; i++) //calculate DAC sine samples { if(ModemConfig.usePWM) @@ -668,17 +827,6 @@ void ModemInit(void) dacSine[i] = ((7.f * sinf(2.f * 3.1416f * (float)i / (float)DAC_SINE_SIZE)) + 8.f); } - if(ModemConfig.flatAudioIn) //when used with flat audio input, use deemphasis and flat modems - { - demodState[0].emphasis = EMPHASIS_NONE; - demodState[1].emphasis = DEEMPHASIS; - } - else //when used with normal (filtered) audio input, use flat and preemphasis modems - { - demodState[0].emphasis = EMPHASIS_NONE; - demodState[1].emphasis = PREEMPHASIS; - } - if(ModemConfig.usePWM) { RCC->APB1ENR |= RCC_APB1ENR_TIM4EN; //configure timer @@ -699,11 +847,3 @@ void ModemInit(void) } } - - - - - -/** - * @} - */ diff --git a/Src/main.c b/Src/main.c index 53c891c..d0ce825 100644 --- a/Src/main.c +++ b/Src/main.c @@ -127,24 +127,22 @@ static void handleFrame(void) TermSendToAll(MODE_MONITOR, (uint8_t*)"(AX.25) Frame received [", 0); //show which modem received the frame: [FP] (flat and preemphasized), [FD] (flat and deemphasized - in flat audio input mode) //[F_] (only flat), [_P] (only preemphasized) or [_D] (only deemphasized - in flat audio input mode) - for(uint8_t i = 0; i < MODEM_DEMODULATOR_COUNT; i++) + for(uint8_t i = 0; i < ModemGetDemodulatorCount(); i++) { if(modemBitmap & (1 << i)) { - enum ModemEmphasis m = ModemGetFilterType(i); + enum ModemPrefilter m = ModemGetFilterType(i); switch(m) { - case PREEMPHASIS: + case PREFILTER_PREEMPHASIS: TermSendToAll(MODE_MONITOR, (uint8_t*)"P", 1); break; - case DEEMPHASIS: + case PREFILTER_DEEMPHASIS: TermSendToAll(MODE_MONITOR, (uint8_t*)"D", 1); break; - case EMPHASIS_NONE: - TermSendToAll(MODE_MONITOR, (uint8_t*)"F", 1); - break; + case PREFILTER_FLAT: default: - TermSendToAll(MODE_MONITOR, (uint8_t*)"*", 1); + TermSendToAll(MODE_MONITOR, (uint8_t*)"F", 1); break; } } From bb6797b29368408eeebfb285315ea0683bf8f965 Mon Sep 17 00:00:00 2001 From: sq8vps Date: Thu, 24 Aug 2023 07:47:22 +0200 Subject: [PATCH 02/29] 9600 untested --- Inc/common.h | 1 + Src/drivers/modem.c | 394 +++++++++++++++++++++++++++----------------- 2 files changed, 246 insertions(+), 149 deletions(-) diff --git a/Inc/common.h b/Inc/common.h index fb2262c..8f309ef 100644 --- a/Inc/common.h +++ b/Inc/common.h @@ -22,6 +22,7 @@ along with VP-Digi. If not, see . #define IS_UPPERCASE_ALPHANUMERIC(x) ((((x) >= '0') && ((x) <= '9')) || (((x) >= 'A') && ((x) <= 'Z'))) #define IS_NUMBER(x) (((x) >= '0') && ((x) <= '9')) +#define ABS(x) (((x) > 0) ? (x) : (-x)) #define CRC32_INIT 0xFFFFFFFF diff --git a/Src/drivers/modem.c b/Src/drivers/modem.c index ec9497b..006b7a3 100644 --- a/Src/drivers/modem.c +++ b/Src/drivers/modem.c @@ -69,13 +69,13 @@ along with VP-Digi. If not, see . #define PLL300_LOCKED_TUNE 0.74f #define PLL300_NOT_LOCKED_TUNE 0.50f +#define AGC9600_ATTACK 0.08f +#define AGC9600_DECAY 0.0008f + #define DAC_SINE_SIZE 32 //DAC sine table size -#define PTT_ON GPIOB->BSRR = GPIO_BSRR_BS7 -#define PTT_OFF GPIOB->BSRR = GPIO_BSRR_BR7 -#define DCD_ON (GPIOC->BSRR = GPIO_BSRR_BR13) -#define DCD_OFF (GPIOC->BSRR = GPIO_BSRR_BS13) + struct ModemDemodConfig ModemConfig; @@ -92,9 +92,9 @@ static float baudRate; //baudrate static uint8_t markStep; //mark timer step static uint8_t spaceStep; //space timer step static uint16_t baudRateStep; //baudrate timer step -static int32_t coeffHiI[NMAX], coeffLoI[NMAX], coeffHiQ[NMAX], coeffLoQ[NMAX]; //correlator IQ coefficients +static int16_t coeffHiI[NMAX], coeffLoI[NMAX], coeffHiQ[NMAX], coeffLoQ[NMAX]; //correlator IQ coefficients static uint8_t dcd = 0; //multiplexed DCD state from both demodulators - +static uint32_t lfsr = 0; //LFSR for 9600 Bd /** * @brief BPF filter with 2200 Hz tone 6 dB preemphasis (it actually attenuates 1200 Hz tone by 6 dB) @@ -193,7 +193,7 @@ struct DemodState enum ModemPrefilter prefilter; struct Filter bpf; - int32_t correlatorSamples[NMAX]; + int16_t correlatorSamples[NMAX]; uint8_t correlatorSamplesIdx; struct Filter lpf; @@ -214,6 +214,9 @@ struct DemodState int32_t dcdInc; int32_t dcdDec; float dcdAdjust; + + int16_t peak; + int16_t valley; }; static struct DemodState demodState[MODEM_MAX_DEMODULATOR_COUNT]; @@ -296,6 +299,15 @@ static void setDcd(uint8_t state) } } +static inline uint8_t scramble(uint8_t in) +{ + //G3RUH scrambling (x^17+x^12+1) + uint8_t bit = ((lfsr & 0x10000) > 0) ^ ((lfsr & 0x800) > 0) ^ (in > 0); + + lfsr <<= 1; + lfsr |= bit; + return bit; +} /** * @brief ISR for demodulator @@ -343,18 +355,34 @@ void TIM1_UP_IRQHandler(void) { TIM1->SR &= ~TIM_SR_UIF; + int32_t sample = 0; + + if(ModemConfig.modem == MODEM_9600) + { + if(ModemConfig.usePWM) + sample = currentSymbol ? 90 : 0; + else + sample = currentSymbol ? 15 : 1; + + sample = filter(&demodState[0].lpf, sample); + } + else + { + sample = dacSine[dacSineIdx]; + dacSineIdx++; + dacSineIdx &= (DAC_SINE_SIZE - 1); + } + if(ModemConfig.usePWM) { - TIM4->CCR1 = dacSine[dacSineIdx]; + TIM4->CCR1 = sample; } else { GPIOB->ODR &= ~0xF000; //zero 4 oldest bits - GPIOB->ODR |= (dacSine[dacSineIdx] << 12); //write sample to 4 oldest bits + GPIOB->ODR |= (sample << 12); //write sample to 4 oldest bits } - dacSineIdx++; - dacSineIdx &= (DAC_SINE_SIZE - 1); } @@ -381,10 +409,17 @@ void TIM3_IRQHandler(void) TIM1->CNT = 0; - if(currentSymbol) //current symbol is space - TIM1->ARR = spaceStep; - else //mark - TIM1->ARR = markStep; + if(ModemConfig.modem == MODEM_9600) + { + currentSymbol = scramble(currentSymbol); + } + else + { + if(currentSymbol) //current symbol is space + TIM1->ARR = spaceStep; + else //mark + TIM1->ARR = markStep; + } } @@ -400,34 +435,38 @@ static int32_t demodulate(int16_t sample, struct DemodState *dem) dem->RMSenergy += ((sample >> 1) * (sample >> 1)); //square the sample and add it to the sum dem->RMSsampleCount++; //increment number of samples - if(dem->prefilter != PREFILTER_NONE) //filter is used - { - dem->correlatorSamples[dem->correlatorSamplesIdx++] = filter(&dem->bpf, sample); - } - else //no pre/deemphasis + if(ModemConfig.modem != MODEM_9600) { - dem->correlatorSamples[dem->correlatorSamplesIdx++] = sample; - } + if(dem->prefilter != PREFILTER_NONE) //filter is used + { + dem->correlatorSamples[dem->correlatorSamplesIdx++] = filter(&dem->bpf, sample); + } + else //no pre/deemphasis + { + dem->correlatorSamples[dem->correlatorSamplesIdx++] = sample; + } - dem->correlatorSamplesIdx %= N; + dem->correlatorSamplesIdx %= N; - int64_t outLoI = 0, outLoQ = 0, outHiI = 0, outHiQ = 0; //output values after correlating + int32_t outLoI = 0, outLoQ = 0, outHiI = 0, outHiQ = 0; //output values after correlating - for(uint8_t i = 0; i < N; i++) - { - int32_t t = dem->correlatorSamples[(dem->correlatorSamplesIdx + i) % N]; //read sample - outLoI += t * coeffLoI[i]; //correlate sample - outLoQ += t * coeffLoQ[i]; - outHiI += t * coeffHiI[i]; - outHiQ += t * coeffHiQ[i]; + for(uint8_t i = 0; i < N; i++) + { + int16_t t = dem->correlatorSamples[(dem->correlatorSamplesIdx + i) % N]; //read sample + outLoI += t * coeffLoI[i]; //correlate sample + outLoQ += t * coeffLoQ[i]; + outHiI += t * coeffHiI[i]; + outHiQ += t * coeffHiQ[i]; + } + + outHiI >>= 12; + outHiQ >>= 12; + outLoI >>= 12; + outLoQ >>= 12; + + sample = ABS(outHiI) + ABS(outHiQ) - ABS(outLoI) - ABS(outLoQ); } - outHiI >>= 12; - outHiQ >>= 12; - outLoI >>= 12; - outLoQ >>= 12; - uint64_t hi = (outHiI * outHiI) + (outHiQ * outHiQ); //calculate output tone levels - uint64_t lo = (outLoI * outLoI) + (outLoQ * outLoQ); //DCD using PLL //PLL is running nominally at 1200 Hz (= baudrate) @@ -443,9 +482,7 @@ static int32_t demodulate(int16_t sample, struct DemodState *dem) dem->dcdPll = (signed)((unsigned)(dem->dcdPll) + ((unsigned)dem->pll)); //keep PLL ticking at the frequency equal to baudrate - uint8_t dcdSymbol = (hi > lo); //get current symbol - - if(dcdSymbol != dem->dcdLastSymbol) //tone changed + if((sample > 0) != dem->dcdLastSymbol) //tone changed { if(abs(dem->dcdPll) < dem->dcdInc) //tone change occurred near zero dem->dcdCounter += dem->dcdInc; //increase DCD counter @@ -457,7 +494,7 @@ static int32_t demodulate(int16_t sample, struct DemodState *dem) dem->dcdPll = (int)(dem->dcdPll * dem->dcdAdjust); //adjust PLL } - dem->dcdLastSymbol = dcdSymbol; //store last symbol for symbol change detection + dem->dcdLastSymbol = sample > 0; //store last symbol for symbol change detection if(dem->dcdCounter > dem->dcdMax) //maximum DCD counter value reached dem->dcdCounter = dem->dcdMax; //avoid "sticky" DCD and counter overflow @@ -467,10 +504,41 @@ static int32_t demodulate(int16_t sample, struct DemodState *dem) else //below DCD threshold dem->dcd = 0; //no DCD - return filter(&dem->lpf, hi - lo) > 0; -} + //TODO: check if demodulator works well after all changes + sample = filter(&dem->lpf, sample); + + if(ModemConfig.modem == MODEM_9600) + { + //AGC + if(sample >= dem->peak) + { + dem->peak += (((int32_t)(AGC9600_ATTACK * (float)32768) * (int32_t)(sample - dem->peak)) >> 15); + } + else + { + dem->peak += (((int32_t)(AGC9600_DECAY * (float)32768) * (int32_t)(sample - dem->peak)) >> 15); + } + + if(sample <= dem->valley) + { + dem->valley += (((int32_t)(AGC9600_ATTACK * (float)32768) * (int32_t)(sample - dem->valley)) >> 15); + } + else + { + dem->valley += (((int32_t)(AGC9600_DECAY * (float)32768) * (int32_t)(sample - dem->valley)) >> 15); + } + //remove DC component (subtract average value of peaks) + //and normalize to 32768 peak-to-peak (-16384:16384) + //32768 is equal to 1 << 15 + if(dem->peak > dem->valley) + { + sample = ((((int32_t)(sample) - ((int32_t)(dem->peak + dem->valley) >> 1)) << 15) / (int32_t)(dem->peak - dem->valley)); + } + } + return sample > 0; +} /** @@ -497,12 +565,16 @@ static void decode(uint8_t symbol, uint8_t demod) { dem->syncSymbols <<= 1; //shift recovered (received, synchronized) bit register - uint8_t t = dem->rawSymbols & 0x07; //take last three symbols for sampling. Seems that 1 symbol is not enough, but 3 symbols work well - if(t == 0b111 || t == 0b110 || t == 0b101 || t == 0b011) //if there are 2 or 3 ones, then the received symbol is 1 - { - dem->syncSymbols |= 1; //push to recovered symbols register - } - //if there 2 or 3 zeros, no need to add anything to the register + uint8_t sym = dem->rawSymbols & 0x07; //take last three symbols for sampling. Seems that 1 symbol is not enough, but 3 symbols work well + if(sym == 0b111 || sym == 0b110 || sym == 0b101 || sym == 0b011) //if there are 2 or 3 ones, then the received symbol is 1 + sym = 1; + else + sym = 0; + + if(ModemConfig.modem == MODEM_9600) + sym = scramble(sym); //descramble + + dem->syncSymbols |= sym; //NRZI decoding if (((dem->syncSymbols & 0x03) == 0b11) || ((dem->syncSymbols & 0x03) == 0b00)) //two last symbols are the same - no symbol transition - decoded bit 1 @@ -540,29 +612,23 @@ void ModemTxTestStart(enum ModemTxTestMode type) setPtt(1); //PTT on txTestState = type; - //DAC timer - TIM1->PSC = 17; //72/18=4 MHz - TIM1->DIER = TIM_DIER_UIE; //enable interrupt - TIM1->CR1 |= TIM_CR1_CEN; //enable timer - TIM2->CR1 &= ~TIM_CR1_CEN; //disable RX timer + TIM1->CR1 |= TIM_CR1_CEN; //enable DAC timer NVIC_DisableIRQ(DMA1_Channel2_IRQn); //disable RX DMA interrupt - NVIC_EnableIRQ(TIM1_UP_IRQn); //enable timer 1 for PWM + NVIC_EnableIRQ(TIM1_UP_IRQn); //enable DAC interrupt if(type == TEST_MARK) { TIM1->ARR = markStep; - } else if(type == TEST_SPACE) + } + else if(type == TEST_SPACE) { TIM1->ARR = spaceStep; } else //alternating tones { //enable baudrate generator - TIM3->PSC = 71; //72/72=1 MHz - TIM3->DIER = TIM_DIER_UIE; //enable interrupt - TIM3->ARR = baudRateStep; //set timer interval TIM3->CR1 = TIM_CR1_CEN; //enable timer NVIC_EnableIRQ(TIM3_IRQn); //enable interrupt in NVIC } @@ -573,8 +639,8 @@ void ModemTxTestStop(void) { txTestState = TEST_DISABLED; - TIM3->CR1 &= ~TIM_CR1_CEN; //turn off timers - TIM1->CR1 &= ~TIM_CR1_CEN; + TIM3->CR1 &= ~TIM_CR1_CEN; //disable baudrate timer + TIM1->CR1 &= ~TIM_CR1_CEN; //disable DAC timer TIM2->CR1 |= TIM_CR1_CEN; //enable RX timer NVIC_DisableIRQ(TIM3_IRQn); @@ -589,14 +655,6 @@ void ModemTransmitStart(void) { setPtt(1); //PTT on - TIM1->PSC = 17; - TIM1->DIER |= TIM_DIER_UIE; - - - TIM3->PSC = 71; - TIM3->DIER |= TIM_DIER_UIE; - TIM3->ARR = baudRateStep; - TIM3->CR1 = TIM_CR1_CEN; TIM1->CR1 = TIM_CR1_CEN; TIM2->CR1 &= ~TIM_CR1_CEN; @@ -612,7 +670,6 @@ void ModemTransmitStart(void) */ void ModemTransmitStop(void) { - TIM2->CR1 |= TIM_CR1_CEN; TIM3->CR1 &= ~TIM_CR1_CEN; TIM1->CR1 &= ~TIM_CR1_CEN; @@ -628,14 +685,14 @@ void ModemTransmitStop(void) /** * @brief Controls PTT output - * @param[in] state 0 - PTT off, 1 - PTT on + * @param state 0 - PTT off, 1 - PTT on */ static void setPtt(uint8_t state) { if(state) - PTT_ON; + GPIOB->BSRR = GPIO_BSRR_BS7; else - PTT_OFF; + GPIOB->BSRR = GPIO_BSRR_BR7; } @@ -646,6 +703,90 @@ void ModemInit(void) { memset(demodState, 0, sizeof(demodState)); + /** + * TIM1 is used for pushing samples to DAC (R2R or PWM) (clocked at 4 MHz) + * TIM3 is the baudrate generator for TX (clocked at 1 MHz) + * TIM4 is the PWM generator with no software interrupt + * TIM2 is the RX sampling timer with no software interrupt, but it directly calls DMA + */ + + RCC->APB2ENR |= RCC_APB2ENR_IOPBEN; + RCC->APB2ENR |= RCC_APB2ENR_IOPCEN; + RCC->APB2ENR |= RCC_APB2ENR_IOPAEN; + RCC->APB1ENR |= RCC_APB1ENR_TIM2EN; + RCC->APB1ENR |= RCC_APB1ENR_TIM3EN; + RCC->APB2ENR |= RCC_APB2ENR_TIM1EN; + RCC->APB2ENR |= RCC_APB2ENR_ADC1EN; + RCC->AHBENR |= RCC_AHBENR_DMA1EN; + + + GPIOC->CRH |= GPIO_CRH_MODE13_1; //DCD LED on PC13 + GPIOC->CRH &= ~GPIO_CRH_MODE13_0; + GPIOC->CRH &= ~GPIO_CRH_CNF13; + + GPIOB->CRH &= ~0xFFFF0000; //R2R output on PB12-PB15 + GPIOB->CRH |= 0x22220000; + + GPIOA->CRL &= ~GPIO_CRL_CNF0; //ADC input on PA0 + GPIOA->CRL &= ~GPIO_CRL_MODE0; + + + GPIOB->CRL |= GPIO_CRL_MODE7_1; //PTT output on PB7 + GPIOB->CRL &= ~GPIO_CRL_MODE7_0; + GPIOB->CRL &= ~GPIO_CRL_CNF7; + + GPIOB->CRL |= GPIO_CRL_MODE5_1; //2nd DCD LED on PB5 + GPIOB->CRL &= ~GPIO_CRL_MODE5_0; + GPIOB->CRL &= ~GPIO_CRL_CNF5; + + + RCC->CFGR |= RCC_CFGR_ADCPRE_1; //ADC prescaler /6 + RCC->CFGR &= ~RCC_CFGR_ADCPRE_0; + + ADC1->CR2 |= ADC_CR2_CONT; //continuous conversion + ADC1->CR2 |= ADC_CR2_EXTSEL; + ADC1->SQR1 &= ~ADC_SQR1_L; //1 conversion + ADC1->SMPR2 |= ADC_SMPR2_SMP0_2; //41.5 cycle sampling + ADC1->SQR3 &= ~ADC_SQR3_SQ1; //channel 0 is first in the sequence + ADC1->CR2 |= ADC_CR2_ADON; //ADC on + + ADC1->CR2 |= ADC_CR2_RSTCAL; //calibrate ADC + while(ADC1->CR2 & ADC_CR2_RSTCAL) + ; + ADC1->CR2 |= ADC_CR2_CAL; + while(ADC1->CR2 & ADC_CR2_CAL) + ; + + ADC1->CR2 |= ADC_CR2_EXTTRIG; + ADC1->CR2 |= ADC_CR2_SWSTART; //start ADC conversion + + //prepare DMA + DMA1_Channel2->CCR |= DMA_CCR_MSIZE_0; //16 bit memory region + DMA1_Channel2->CCR &= ~DMA_CCR_MSIZE_1; + DMA1_Channel2->CCR |= DMA_CCR_PSIZE_0; + DMA1_Channel2->CCR &= ~DMA_CCR_PSIZE_1; + + DMA1_Channel2->CCR |= DMA_CCR_MINC | DMA_CCR_CIRC| DMA_CCR_TCIE; //circular mode, memory increment and interrupt + DMA1_Channel2->CNDTR = 4; //4 samples + DMA1_Channel2->CPAR = (uint32_t)&(ADC1->DR); //ADC data register address + DMA1_Channel2->CMAR = (uint32_t)samples; //sample buffer address + DMA1_Channel2->CCR |= DMA_CCR_EN; //enable DMA + + NVIC_EnableIRQ(DMA1_Channel2_IRQn); + + + //RX sampling timer + TIM2->PSC = 8; //72/9=8 MHz + TIM2->DIER |= TIM_DIER_UDE; //enable calling DMA on timer tick + + //TX DAC timer + TIM1->PSC = 17; //72/18=4 MHz + TIM1->DIER |= TIM_DIER_UIE; + + //baudrate timer + TIM3->PSC = 71; //72/72=1 MHz + TIM3->DIER |= TIM_DIER_UIE; + if((ModemConfig.modem == MODEM_1200) || (ModemConfig.modem == MODEM_1200_V23)) { demodCount = 2; @@ -703,6 +844,8 @@ void ModemInit(void) markFreq = 1300.f; spaceFreq = 2100.f; } + + TIM2->ARR = 207; //8MHz / 208 =~38400 Hz (4*9600 Hz for 4x oversampling) } else if(ModemConfig.modem == MODEM_300) { @@ -710,7 +853,7 @@ void ModemInit(void) N = N300; baudRate = 300.f; markFreq = 1600.f; - markFreq = 1800.f; + spaceFreq = 1800.f; demodState[0].pllStep = PLL300_STEP; demodState[0].pllLockedAdjust = PLL300_LOCKED_TUNE; @@ -728,88 +871,41 @@ void ModemInit(void) demodState[0].lpf.coeffs = lpf300; demodState[0].lpf.taps = sizeof(lpf300) / sizeof(*lpf300); demodState[0].lpf.gainShift = 0; //not important, output is always compared with 0 - } - - markStep = 4000000 / (DAC_SINE_SIZE * markFreq) - 1; - spaceStep = 4000000 / (DAC_SINE_SIZE * spaceFreq) - 1; - baudRateStep = 1000000 / baudRate - 1; - - - /** - * TIM1 is used for pushing samples to DAC (R2R or PWM) at 4 MHz - * TIM3 is the baudrate generator for TX running at 1 MHz - * TIM4 is the PWM generator with no software interrupt - * TIM2 is the RX sampling timer with no software interrupt, but it directly calls DMA - */ - - RCC->APB2ENR |= RCC_APB2ENR_IOPBEN; - RCC->APB2ENR |= RCC_APB2ENR_IOPCEN; - RCC->APB2ENR |= RCC_APB2ENR_IOPAEN; - RCC->APB1ENR |= RCC_APB1ENR_TIM2EN; - RCC->APB1ENR |= RCC_APB1ENR_TIM3EN; - RCC->APB2ENR |= RCC_APB2ENR_TIM1EN; - RCC->APB2ENR |= RCC_APB2ENR_ADC1EN; - RCC->AHBENR |= RCC_AHBENR_DMA1EN; - - - GPIOC->CRH |= GPIO_CRH_MODE13_1; //DCD LED on PC13 - GPIOC->CRH &= ~GPIO_CRH_MODE13_0; - GPIOC->CRH &= ~GPIO_CRH_CNF13; - - GPIOB->CRH &= ~0xFFFF0000; //R2R output on PB12-PB15 - GPIOB->CRH |= 0x22220000; - GPIOA->CRL &= ~GPIO_CRL_CNF0; //ADC input on PA0 - GPIOA->CRL &= ~GPIO_CRL_MODE0; - - - GPIOB->CRL |= GPIO_CRL_MODE7_1; //PTT output on PB7 - GPIOB->CRL &= ~GPIO_CRL_MODE7_0; - GPIOB->CRL &= ~GPIO_CRL_CNF7; - - GPIOB->CRL |= GPIO_CRL_MODE5_1; //2nd DCD LED on PB5 - GPIOB->CRL &= ~GPIO_CRL_MODE5_0; - GPIOB->CRL &= ~GPIO_CRL_CNF5; - - - RCC->CFGR |= RCC_CFGR_ADCPRE_1; //ADC prescaler /6 - RCC->CFGR &= ~RCC_CFGR_ADCPRE_0; - - ADC1->CR2 |= ADC_CR2_CONT; //continuous conversion - ADC1->CR2 |= ADC_CR2_EXTSEL; - ADC1->SQR1 &= ~ADC_SQR1_L; //1 conversion - ADC1->SMPR2 |= ADC_SMPR2_SMP0_2; //41.5 cycle sampling - ADC1->SQR3 &= ~ADC_SQR3_SQ1; //channel 0 is first in the sequence - ADC1->CR2 |= ADC_CR2_ADON; //ADC on + TIM2->ARR = 416; //8MHz / 416 =~19200 Hz (4*4800 Hz for 4x oversampling) + } + else if(ModemConfig.modem == MODEM_9600) + { + demodCount = 1; + N = N9600; + baudRate = 9600.f; + + demodState[0].pllStep = PLL9600_STEP; + demodState[0].pllLockedAdjust = PLL9600_LOCKED_TUNE; + demodState[0].pllNotLockedAdjust = PLL9600_NOT_LOCKED_TUNE; + demodState[0].dcdMax = DCD9600_MAXPULSE; + demodState[0].dcdThres = DCD9600_THRES; + demodState[0].dcdInc = DCD9600_INC; + demodState[0].dcdDec = DCD9600_DEC; + demodState[0].dcdAdjust = DCD9600_PLLTUNE; - ADC1->CR2 |= ADC_CR2_RSTCAL; //calibrate ADC - while(ADC1->CR2 & ADC_CR2_RSTCAL) - ; - ADC1->CR2 |= ADC_CR2_CAL; - while(ADC1->CR2 & ADC_CR2_CAL) - ; + demodState[0].prefilter = PREFILTER_NONE; + //this filter will be used for RX and TX + demodState[0].lpf.coeffs = lpf9600; + demodState[0].lpf.taps = sizeof(lpf9600) / sizeof(*lpf9600); + demodState[0].lpf.gainShift = 16; - ADC1->CR2 |= ADC_CR2_EXTTRIG; - ADC1->CR2 |= ADC_CR2_SWSTART; //start ADC conversion + TIM2->ARR = 51; //8MHz / 52 =~153600 Hz (4*38400 Hz for 4x oversampling) + } - //prepare DMA - DMA1_Channel2->CCR |= DMA_CCR_MSIZE_0; //16 bit memory region - DMA1_Channel2->CCR &= ~DMA_CCR_MSIZE_1; - DMA1_Channel2->CCR |= DMA_CCR_PSIZE_0; - DMA1_Channel2->CCR &= ~DMA_CCR_PSIZE_1; + TIM2->CR1 |= TIM_CR1_CEN; //enable DMA timer - DMA1_Channel2->CCR |= DMA_CCR_MINC | DMA_CCR_CIRC| DMA_CCR_TCIE; //circular mode, memory increment and interrupt - DMA1_Channel2->CNDTR = 4; //4 samples - DMA1_Channel2->CPAR = (uint32_t)&(ADC1->DR); //ADC data register address - DMA1_Channel2->CMAR = (uint32_t)samples; //sample buffer address - DMA1_Channel2->CCR |= DMA_CCR_EN; //enable DMA + markStep = 4000000 / (DAC_SINE_SIZE * markFreq) - 1; + spaceStep = 4000000 / (DAC_SINE_SIZE * spaceFreq) - 1; + baudRateStep = 1000000 / baudRate - 1; - NVIC_EnableIRQ(DMA1_Channel2_IRQn); - TIM2->PSC = 17; //72/18=4 MHz - TIM2->DIER |= TIM_DIER_UDE; //enable calling DMA on timer tick - TIM2->ARR = 103; //4MHz / 104 =~38400 Hz (4*9600 Hz for 4x oversampling) - TIM2->CR1 |= TIM_CR1_CEN; //enable timer + TIM3->ARR = baudRateStep; for(uint8_t i = 0; i < N; i++) //calculate correlator coefficients { From 80093c96ea332b2366f108117946762fd865669b Mon Sep 17 00:00:00 2001 From: sq8vps Date: Fri, 25 Aug 2023 11:56:06 +0200 Subject: [PATCH 03/29] working on modems --- .cproject | 100 +++++++++++---------- .gitmodules | 3 + Inc/ax25.h | 6 +- Inc/fx25.h | 32 +++++++ Src/ax25.c | 246 ++++++++++++++++++++++++++++++++++++++++++++++++--- Src/fx25.c | 83 +++++++++++++++++ Src/main.c | 11 ++- bpf1200.c | 42 +++++++++ bpf1200inv.c | 42 +++++++++ 9 files changed, 497 insertions(+), 68 deletions(-) create mode 100644 .gitmodules create mode 100644 Inc/fx25.h create mode 100644 Src/fx25.c create mode 100644 bpf1200.c create mode 100644 bpf1200inv.c diff --git a/.cproject b/.cproject index 632be6e..b0d130b 100644 --- a/.cproject +++ b/.cproject @@ -17,16 +17,16 @@ - - - - - - - - + + + + + @@ -157,12 +160,14 @@ + + - -