kopia lustrzana https://github.com/F5OEO/rpitx
1654 wiersze
48 KiB
C
1654 wiersze
48 KiB
C
/*
|
|
|
|
<rpitx is a software which use the GPIO of Raspberry Pi to transmit HF>
|
|
|
|
Copyright (C) 2015 Evariste COURJAUD F5OEO (evaristec@gmail.com)
|
|
|
|
Transmitting on HF band is surely not permitted without license (Hamradio for example).
|
|
Usage of this software is not the responsability of the author.
|
|
|
|
This program is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation, either version 2 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
Thanks to first test of RF with Pifm by Oliver Mattos and Oskar Weigl
|
|
INSPIRED BY THE IMPLEMENTATION OF PIFMDMA by Richard Hirst <richardghirst@gmail.com> December 2012
|
|
Helped by a code fragment by PE1NNZ (http://pe1nnz.nl.eu.org/2013/05/direct-ssb-generation-on-pll.html)
|
|
*/
|
|
|
|
/* ================== TODO =====================
|
|
Optimize CPU on PWMFrequency
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <unistd.h>
|
|
#include <string.h>
|
|
#include <errno.h>
|
|
#include <stdarg.h>
|
|
#include <stdint.h>
|
|
#include <math.h>
|
|
#include <time.h>
|
|
#include <signal.h>
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <fcntl.h>
|
|
#include <sys/mman.h>
|
|
|
|
//#include "utils.h"
|
|
#include "mailbox.h"
|
|
#include <getopt.h>
|
|
#include <termios.h> //Used for UART
|
|
#include "RpiGpio.h"
|
|
#include "RpiDma.h"
|
|
#include <pthread.h>
|
|
|
|
#include "RpiTx.h"
|
|
|
|
#include <sys/prctl.h>
|
|
#include <getopt.h>
|
|
#include <sys/timex.h>
|
|
#include <sys/ioctl.h>
|
|
|
|
#define AMP_BYPAD
|
|
|
|
//Minimum Time in us to sleep
|
|
#define KERNEL_GRANULARITY 4000
|
|
|
|
#define SCHED_PRIORITY 30 //Linux scheduler priority. Higher = more realtime
|
|
|
|
|
|
#define PROGRAM_VERSION "0.3"
|
|
|
|
#define PLL_FREQ_500MHZ 500000000 // PLLD is running at 500MHz
|
|
#define PLL_500MHZ 0x6
|
|
|
|
//#define PLL_FREQ_1GHZ 1000000000 //PLLC = 1GHZ
|
|
//#define PLL_1GHZ 0x5
|
|
|
|
#define PLL_FREQ_1GHZ 1100000000 //PLL = 1GHZ
|
|
#define PLL_1GHZ 0x6 //PLLD = 1GHZ ONLY AFTER APLYINg DT-BLOB.BIN !!!! WARNING !!!
|
|
|
|
|
|
#define PLLFREQ_192 19200000 //PLLA = 19.2MHZ
|
|
#define PLL_192 0x1
|
|
|
|
#define HEADER_SIZE 44
|
|
|
|
// DMA TIMING : depends on Pi Model : Calibration is better
|
|
int FREQ_DELAY_TIME=0;
|
|
float FREQ_MINI_TIMING=157;
|
|
|
|
float DelayStep=157;
|
|
float DelayMini=1200;
|
|
|
|
int PWMF_MARGIN = 2496;//1120; //A Margin for now at 1us with PCM ->OK
|
|
double globalppmpll=0;
|
|
|
|
|
|
uint32_t *Shuffle[PWM_STEP_MAXI];
|
|
|
|
typedef unsigned char uchar; // 8 bit
|
|
typedef unsigned short uint16; // 16 bit
|
|
typedef unsigned int uint; // 32 bits
|
|
|
|
//F5OEO Variable
|
|
uint32_t PllFreq500MHZ;
|
|
uint32_t PllFreq1GHZ;
|
|
uint32_t PllFreq19MHZ;
|
|
|
|
uint32_t PllUsed;
|
|
char PllNumber;
|
|
double TuneFrequency=62500000;
|
|
unsigned char FreqDivider=2;
|
|
int DmaSampleBurstSize=1000;
|
|
int NUM_SAMPLES=NUM_SAMPLES_MAX;
|
|
int Randomize=1;
|
|
|
|
uint32_t GlobalTabPwmFrequency[50];
|
|
|
|
unsigned int CalibrationTab[200];
|
|
#include "calibrationpi2.h"
|
|
#include "calibrationpi3.h"
|
|
#include "calibrationpizero.h"
|
|
|
|
|
|
//End F5OEO
|
|
|
|
char EndOfApp=0;
|
|
unsigned char loop_mode_flag=0;
|
|
char *FileName = 0;
|
|
int FileInHandle = -1; //Handle in Transport Stream File
|
|
int useStdin = 0;
|
|
|
|
static void udelay(int us)
|
|
{
|
|
struct timespec ts = { 0, us * 1000 };
|
|
|
|
nanosleep(&ts, NULL);
|
|
}
|
|
|
|
static void stop_dma(void)
|
|
{
|
|
|
|
if (FileInHandle != -1) {
|
|
close(FileInHandle);
|
|
FileInHandle = -1;
|
|
}
|
|
if (dma_reg) {
|
|
//Stop Main DMA
|
|
//STop DMA
|
|
dma_reg[DMA_CS+DMA_CHANNEL*0x40] |= DMA_CS_ABORT;
|
|
udelay(100);
|
|
dma_reg[DMA_CS+DMA_CHANNEL*0x40]&= ~DMA_CS_ACTIVE;
|
|
dma_reg[DMA_CS+DMA_CHANNEL*0x40] |= DMA_CS_RESET;
|
|
udelay(100);
|
|
|
|
|
|
//printf("Reset DMA Done\n");
|
|
clk_reg[GPCLK_CNTL] = 0x5A << 24 | 0 << 9 | 1 << 4 | 6; //NO MASH !!!
|
|
udelay(500);
|
|
gpio_reg[GPFSEL0] = (gpio_reg[GPFSEL0] & ~(7 << 12)) | (0 << 12); //DISABLE CLOCK -
|
|
clk_reg[PWMCLK_CNTL] = 0x5A000006 | (0 << 9) ;
|
|
udelay(500);
|
|
clk_reg[PCMCLK_CNTL] = 0x5A000006;
|
|
udelay(500);
|
|
//printf("Resetpcm Done\n");
|
|
pwm_reg[PWM_DMAC] = 0;
|
|
udelay(100);
|
|
pwm_reg[PWM_CTL] = PWMCTL_CLRF;
|
|
udelay(100);
|
|
//printf("Reset pwm Done\n");
|
|
}
|
|
if (mbox.virt_addr != NULL) {
|
|
unmapmem(mbox.virt_addr, NUM_PAGES * PAGE_SIZE);
|
|
//printf("Unmapmem Done\n");
|
|
mem_unlock(mbox.handle, mbox.mem_ref);
|
|
//printf("Unmaplock Done\n");
|
|
mem_free(mbox.handle, mbox.mem_ref);
|
|
//printf("Unmapfree Done\n");
|
|
}
|
|
}
|
|
|
|
static void terminate(int dummy)
|
|
{
|
|
stop_dma();
|
|
//munmap(virtbase,NUM_PAGES * PAGE_SIZE);
|
|
printf("END OF PiTx\n");
|
|
exit(1);
|
|
}
|
|
|
|
static void fatal(char *fmt, ...)
|
|
{
|
|
va_list ap;
|
|
|
|
va_start(ap, fmt);
|
|
vfprintf(stderr, fmt, ap);
|
|
va_end(ap);
|
|
terminate(0);
|
|
}
|
|
|
|
void setSchedPriority(int priority)
|
|
{
|
|
//In order to get the best timing at a decent queue size, we want the kernel to avoid interrupting us for long durations.
|
|
//This is done by giving our process a high priority. Note, must run as super-user for this to work.
|
|
struct sched_param sp;
|
|
int ret;
|
|
|
|
sp.sched_priority=priority;
|
|
if ((ret = pthread_setschedparam(pthread_self(), SCHED_RR, &sp))) {
|
|
printf("Warning: pthread_setschedparam (increase thread priority) returned non-zero: %i\n", ret);
|
|
}
|
|
|
|
}
|
|
|
|
uint32_t DelayFromSampleRate=0;
|
|
int Instrumentation=0;
|
|
int UsePCMClk=1; ////GPIO CLK output is now default
|
|
uint32_t Originfsel=0;
|
|
uint32_t Originfsel2=0;
|
|
|
|
int SetupGpioClock(uint32_t SymbolRate,double TuningFrequency)
|
|
{
|
|
char MASH=1;
|
|
|
|
if(UsePCMClk==0) TuningFrequency=TuningFrequency*2;
|
|
printf("Gpioclokc Tuning=%f\n",TuningFrequency);
|
|
if((TuningFrequency>=250e6)&&(TuningFrequency<=400e6))
|
|
{
|
|
MASH=1;
|
|
}
|
|
if(TuningFrequency<250e6)
|
|
{
|
|
MASH=3;
|
|
}
|
|
|
|
printf("MASH %d Freq PLL# %d\n",MASH,PllNumber);
|
|
Originfsel=gpio_reg[GPFSEL0]; // Warning carefull if FSEL is used after !!!!!!!!!!!!!!!!!!!!
|
|
Originfsel2=gpio_reg[GPFSEL2]; // Warning carefull if FSEL is used after !!!!!!!!!!!!!!!!!!!!
|
|
|
|
// ------------------- MAKE MAX OUTPUT CURRENT FOR GPIO -----------------------
|
|
char OutputPower=7;
|
|
pad_gpios_reg[PADS_GPIO_0] = 0x5a000000 + (OutputPower&0x7) + (1<<4) + (0<<3); // Set output power for I/Q GPIO18/GPIO19
|
|
#define PULL_OFF 0
|
|
#define PULL_DOWN 1
|
|
#define PULL_UP 2
|
|
gpio_reg[GPPUD]=PULL_OFF;
|
|
udelay(100);
|
|
gpio_reg[GPPUDCLK0]=(1<<4); //GPIO CLK is GPIO 4
|
|
udelay(100);
|
|
gpio_reg[GPPUDCLK0]=(0); //GPIO CLK is GPIO 4
|
|
|
|
|
|
if(UsePCMClk==1)
|
|
{
|
|
gpio_reg[GPFSEL0] = (Originfsel & ~(7 << 12)) | (4 << 12); //ENABLE CLOCK ON GPIO CLK
|
|
//Could add on other GPIOCLK to transmit
|
|
//gpio_reg[GPFSEL2] = (Originfsel2 & ~(3 )) | ( 2); //ENABLE CLOCK ON GPIO CLK on GPIO 20 / ALT 5
|
|
}
|
|
|
|
#ifdef USE_PCM
|
|
//------------------- Init PCM ------------------
|
|
pcm_reg[PCM_CS_A] = 1; // Disable Rx+Tx, Enable PCM block
|
|
udelay(100);
|
|
clk_reg[PCMCLK_CNTL] = 0x5A000000|PLL_1GHZ; // Source=PLLC (1GHHz) STOP PLL
|
|
udelay(1000);
|
|
static uint32_t FreqDividerPCM;
|
|
static uint32_t FreqFractionnalPCM;
|
|
int NbStepPCM = 25; // Should not exceed 1000 :
|
|
|
|
FreqDividerPCM=(int) ((double)PllFreq1GHZ/(SymbolRate*NbStepPCM/**PwmNumberStep*/));
|
|
FreqFractionnalPCM=4096.0 * (((double)PllFreq1GHZ/(SymbolRate*NbStepPCM/**PwmNumberStep*/))-FreqDividerPCM);
|
|
|
|
printf("SampleRate=%d\n",SymbolRate);
|
|
if((FreqDividerPCM>4096)||(FreqDividerPCM<2)) printf("Warning : SampleRate is not valid\n");
|
|
clk_reg[PCMCLK_DIV] = 0x5A000000 | ((FreqDividerPCM)<<12) | FreqFractionnalPCM;
|
|
udelay(1000);
|
|
//printf("Div PCM %d FracPCM %d\n",FreqDividerPCM,FreqFractionnalPCM);
|
|
|
|
DelayFromSampleRate=(1e9/(SymbolRate));
|
|
|
|
pcm_reg[PCM_TXC_A] = 0<<31 | 1<<30 | 0<<20 | 0<<16; // 1 channel, 8 bits
|
|
udelay(100);
|
|
|
|
//printf("Nb PCM STEP (<1000):%d\n",NbStepPCM);
|
|
pcm_reg[PCM_MODE_A] = (NbStepPCM-1)<<10; // SHOULD NOT EXCEED 1000 !!!
|
|
udelay(100);
|
|
pcm_reg[PCM_CS_A] |= 1<<4 | 1<<3; // Clear FIFOs
|
|
udelay(100);
|
|
pcm_reg[PCM_DREQ_A] = 64<<24 | /*64<<8 |*/ 64<<8 ; //TX Fifo PCM=64 DMA Req when one slot is free?
|
|
udelay(100);
|
|
pcm_reg[PCM_CS_A] |= 1<<9; // Enable DMA
|
|
udelay(1000);
|
|
clk_reg[PCMCLK_CNTL] = 0x5A000010 |(1 << 9)| PLL_1GHZ /*PLL_1GHZ*/; // Source=PLLC and enable
|
|
udelay(100);
|
|
pcm_reg[PCM_CS_A] |= 1<<2; //START TX PCM
|
|
#endif
|
|
// FIN PCM
|
|
|
|
//INIT PWM in Serial Mode : WE USE PWM OUPUT
|
|
if(UsePCMClk==0)
|
|
{
|
|
gpioSetMode(18, 2); /* set to ALT5, PWM1 : RF On PIN */
|
|
|
|
pwm_reg[PWM_CTL] = 0;
|
|
clk_reg[PWMCLK_CNTL] = 0x5A000000 | (MASH << 9) |PllNumber/*PLL_1GHZ*/ ;
|
|
udelay(300);
|
|
clk_reg[PWMCLK_DIV] = 0x5A000000 | (2<<12); //WILL BE UPDATED BY DMA
|
|
udelay(300);
|
|
clk_reg[PWMCLK_CNTL] = 0x5A000010 | (MASH << 9) | PllNumber /*PLL_1GHZ*/; //MASH3 : A TESTER SI MIEUX en MASH1
|
|
//MASH 3 doesnt seem work above 80MHZ, back to MASH1
|
|
pwm_reg[PWM_RNG1] = 32;// 250 -> 8KHZ
|
|
udelay(100);
|
|
pwm_reg[PWM_RNG2] = 32;// 32 Mandatory for Serial Mode without gap
|
|
|
|
pwm_reg[PWM_FIFO]=0xAAAAAAAA;
|
|
pwm_reg[PWM_DMAC] = PWMDMAC_ENAB | PWMDMAC_THRSHLD;
|
|
udelay(100);
|
|
pwm_reg[PWM_CTL] = PWMCTL_CLRF;
|
|
udelay(100);
|
|
pwm_reg[PWM_CTL] = PWMCTL_USEF1| PWMCTL_MODE1| PWMCTL_PWEN1|PWMCTL_RPTL1; //PWM0 in Repeat mode
|
|
}
|
|
// FIN INIT PWM
|
|
|
|
//******************* INIT CLK MODE : WE OUTPUT CLK INSTEAD OF PWM OUTPUT
|
|
if(UsePCMClk==1)
|
|
{
|
|
clk_reg[GPCLK_CNTL] = 0x5A000000 | (MASH << 9) |PllNumber/*PLL_1GHZ*/ ;
|
|
udelay(300);
|
|
clk_reg[GPCLK_DIV] = 0x5A000000 | (2<<12); //WILL BE UPDATED BY DMA !! CAREFUL NOT DIVIDE BY 2 LIKE PWM
|
|
udelay(300);
|
|
clk_reg[GPCLK_CNTL] = 0x5A000010 | (MASH << 9) | PllNumber /*PLL_1GHZ*/; //MASH3 : A TESTER SI MIEUX en MASH1
|
|
}
|
|
|
|
|
|
ctl = (struct control_data_s *)virtbase; // Struct ctl is mapped to the memory allocated by RpiDMA (Mailbox)
|
|
dma_cb_t *cbp = ctl->cb;
|
|
|
|
uint32_t phys_pwm_fifo_addr = 0x7e20c000 + 0x18;//PWM Fifo
|
|
uint32_t phys_pwm_range_addr = 0x7e20c000 + 0x10;//PWM Range
|
|
uint32_t phys_clock_div_addr = 0x7e101074;//CLOCK Frequency Setting
|
|
uint32_t phys_pwm_clock_div_addr = 0x7e1010a4; //CLK PWM
|
|
uint32_t phys_gpio_set_addr = 0x7e20001c;
|
|
uint32_t phys_gpio_clear_addr = 0x7e200028;
|
|
uint32_t dummy_gpio = 0x7e20b000;
|
|
uint32_t phys_pcm_fifo_addr = 0x7e203004;
|
|
uint32_t phys_gpio_pads_addr =0x7e10002c;
|
|
uint32_t phys_pcm_clock = 0x7e10109c ;
|
|
uint32_t phys_pcm_clock_div_addr = 0x7e10109c;//CLOCK Frequency Setting
|
|
|
|
uint32_t phys_gpfsel = 0x7E200000 ;
|
|
int samplecnt;
|
|
|
|
|
|
for (samplecnt = 0; samplecnt < NUM_SAMPLES ; samplecnt++)
|
|
{
|
|
//At Same Time Init Samples
|
|
if(UsePCMClk==0)
|
|
{
|
|
ctl->sample[samplecnt].Amplitude2=0x0;
|
|
}
|
|
else
|
|
{
|
|
ctl->sample[samplecnt].Amplitude2=(Originfsel & ~(7 << 12)) | (0 << 12); //Pin is in
|
|
}
|
|
ctl->sample[samplecnt].Amplitude1=0x5a000000 + (0&0x7) + (1<<4) + (0<<3);
|
|
|
|
|
|
//@0
|
|
//Set Amplitude by writing to PWM_SERIAL via PADS
|
|
cbp->info = 0;//BCM2708_DMA_NO_WIDE_BURSTS | BCM2708_DMA_WAIT_RESP ;
|
|
cbp->src = mem_virt_to_phys(&ctl->sample[samplecnt].Amplitude1);
|
|
cbp->dst = phys_gpio_pads_addr;
|
|
cbp->length = 4;
|
|
cbp->stride = 0;
|
|
cbp->next = mem_virt_to_phys(cbp + 1);
|
|
cbp++;
|
|
//@1
|
|
//Set Amplitude by writing to PWM_SERIAL via Patern
|
|
cbp->info = 0;//BCM2708_DMA_NO_WIDE_BURSTS | BCM2708_DMA_WAIT_RESP ;
|
|
cbp->src = mem_virt_to_phys(&ctl->sample[samplecnt].Amplitude2);
|
|
if(UsePCMClk==0)
|
|
cbp->dst = phys_pwm_fifo_addr;
|
|
if(UsePCMClk==1)
|
|
cbp->dst = phys_gpfsel;
|
|
cbp->length = 4;
|
|
cbp->stride = 0;
|
|
cbp->next = mem_virt_to_phys(cbp + 1);
|
|
cbp++;
|
|
//2
|
|
//Set PWMFrequency
|
|
cbp->info =/*BCM2708_DMA_NO_WIDE_BURSTS*/ BCM2708_DMA_SRC_INC|BCM2708_DMA_NO_WIDE_BURSTS;
|
|
// BCM2708_DMA_WAIT_RESP : without 160ns, with 300ns
|
|
cbp->src = mem_virt_to_phys(&ctl->sample[samplecnt].FrequencyTab[0]);
|
|
if(UsePCMClk==0)
|
|
cbp->dst = phys_pwm_clock_div_addr;
|
|
if(UsePCMClk==1)
|
|
cbp->dst = phys_clock_div_addr;
|
|
cbp->length = 4; //Be updated by main DMA
|
|
cbp->stride = 0;
|
|
cbp->next = mem_virt_to_phys(cbp + 1);
|
|
cbp++;
|
|
}
|
|
|
|
cbp--;
|
|
cbp->next = mem_virt_to_phys((void*)virtbase);
|
|
|
|
|
|
// ------------------------------ END DMA INIT ---------------------------------
|
|
|
|
dma_reg[DMA_CS+DMA_CHANNEL*0x40] = BCM2708_DMA_RESET;
|
|
udelay(1000);
|
|
dma_reg[DMA_CS+DMA_CHANNEL*0x40] = BCM2708_DMA_INT | BCM2708_DMA_END;
|
|
udelay(100);
|
|
dma_reg[DMA_CONBLK_AD+DMA_CHANNEL*0x40]=mem_virt_to_phys((void *)virtbase );
|
|
udelay(100);
|
|
dma_reg[DMA_DEBUG+DMA_CHANNEL*0x40] = 7; // clear debug error flags
|
|
udelay(100);
|
|
|
|
return 1;
|
|
}
|
|
|
|
#define ln(x) (log(x)/log(2.718281828459045235f))
|
|
|
|
|
|
// Again some functions taken gracefully from F4GKR : https://github.com/f4gkr/RadiantBee
|
|
|
|
//Normalize to [-180,180):
|
|
inline double constrainAngle(double x){
|
|
x = fmod(x + M_PI,2*M_PI);
|
|
if (x < 0)
|
|
x += 2*M_PI;
|
|
return x - M_PI;
|
|
}
|
|
// convert to [-360,360]
|
|
inline double angleConv(double angle){
|
|
return fmod(constrainAngle(angle),2*M_PI);
|
|
}
|
|
inline double angleDiff(double a,double b){
|
|
double dif = fmod(b - a + M_PI,2*M_PI);
|
|
if (dif < 0)
|
|
dif += 2*M_PI;
|
|
return dif - M_PI;
|
|
}
|
|
|
|
inline double unwrap(double previousAngle,double newAngle){
|
|
return previousAngle - angleDiff(newAngle,angleConv(previousAngle));
|
|
}
|
|
|
|
|
|
int arctan2(int y, int x) // Should be replaced with fast_atan2 from rtl_fm
|
|
{
|
|
int abs_y = abs(y);
|
|
int angle;
|
|
if((x==0)&&(y==0)) return 0;
|
|
if(x >= 0){
|
|
angle = 45 - 45 * (x - abs_y) / ((x + abs_y)==0?1:(x + abs_y));
|
|
} else {
|
|
angle = 135 - 45 * (x + abs_y) / ((abs_y - x)==0?1:(abs_y - x));
|
|
}
|
|
return (y < 0) ? -angle : angle; // negate if in quad III or IV
|
|
}
|
|
|
|
void IQToFreqAmp(int I,int Q,double *Frequency,int *Amp,int SampleRate)
|
|
{
|
|
double phase;
|
|
static double prev_phase = 0;
|
|
|
|
*Amp=round(sqrt( I*I + Q*Q)/sqrt(2));
|
|
//*Amp=*Amp*3; // Amp*5 pour la voix !! To be tested more
|
|
if(*Amp>32767)
|
|
{
|
|
printf("!");
|
|
*Amp=32767; //Overload
|
|
}
|
|
|
|
//phase = M_PI + atan2(I,Q);//((double)arctan2(I,Q) * M_PI)/180.0f;
|
|
phase=atan2(I,Q);
|
|
phase=unwrap(prev_phase,phase);
|
|
|
|
double dp= phase-prev_phase;
|
|
|
|
/* double dp = phase - prev_phase;
|
|
if(dp < 0) dp = dp + 2*M_PI;
|
|
*/
|
|
*Frequency = (dp*(double)SampleRate)/(2.0f*M_PI);
|
|
//if(*Frequency<1000)
|
|
// printf("I=%d Q=%d phase= %f dp = %f Correctdp=%f Amp=%d Freq=%f\n",I,Q,phase,phase - prev_phase,dp,*Amp,*Frequency);
|
|
prev_phase = phase;
|
|
//if(*Frequency>SampleRate/2) printf("%f\n",*Frequency);
|
|
}
|
|
|
|
|
|
|
|
|
|
inline void shuffle_int(uint32_t *list, int len)
|
|
{
|
|
int j;
|
|
uint32_t tmp;
|
|
while(len)
|
|
{
|
|
j = rand() % (len+1);
|
|
if (j != len - 1)
|
|
{
|
|
tmp = list[j];
|
|
list[j] = list[len - 1];
|
|
list[len - 1] = tmp;
|
|
}
|
|
len--;
|
|
}
|
|
}
|
|
|
|
|
|
uint32_t FrequencyAmplitudeToRegister2(double TuneFrequency,uint32_t Amplitude,int NoSample,uint32_t WaitNanoSecond,uint32_t SampleRate,char NoUsePWMF,int debug)
|
|
{
|
|
static char ShowInfo=1;
|
|
|
|
ctl = (struct control_data_s *)virtbase; // Struct ctl is mapped to the memory allocated by RpiDMA (Mailbox)
|
|
dma_cb_t *cbp = ctl->cb+NoSample*CBS_SIZE_BY_SAMPLE;
|
|
|
|
|
|
if(WaitNanoSecond==0)
|
|
{
|
|
if(SampleRate!=0)
|
|
WaitNanoSecond = (1e9/SampleRate);
|
|
else
|
|
printf("No samplerate neither Wait..Quit\n");
|
|
}
|
|
|
|
// ********************************** PWM FREQUENCY PROCESSING *****************************
|
|
|
|
if(UsePCMClk==0)
|
|
TuneFrequency*=2.0; //Because of pattern 10101010
|
|
|
|
// F1 < TuneFrequency < F2
|
|
uint32_t FreqDividerf2=(int) ((double)PllUsed/TuneFrequency);
|
|
uint32_t FreqFractionnalf2=4096.0 * (((double)PllUsed/TuneFrequency)-FreqDividerf2);
|
|
|
|
uint32_t FreqDividerf1=(FreqFractionnalf2!=4095)?FreqDividerf2:FreqDividerf2+1;
|
|
uint32_t FreqFractionnalf1=(FreqFractionnalf2!=4095)?FreqFractionnalf2+1:0;
|
|
|
|
double f1=PllUsed/(FreqDividerf1+(double)FreqFractionnalf1/4096.0);
|
|
double f2=PllUsed/(FreqDividerf2+(double)FreqFractionnalf2/4096.0); // f2 is the higher frequency
|
|
double DeltaFreq=f2-f1; //Frequency granularity of PLL (without PWMF)
|
|
|
|
static int OverWaitNanoSecond=0; //To count how many nano we wait to much
|
|
//OverWaitNanoSecond=0;
|
|
if(WaitNanoSecond-OverWaitNanoSecond<0) {printf("Overwait issue\n");} //Fixme do something clean to avoid this
|
|
|
|
|
|
|
|
//Determine NbStepDMA which correpond to WaitNanosecond
|
|
//FixMe if WaitNanoSecond is too large !
|
|
int i;
|
|
int CompensateWait=(WaitNanoSecond-OverWaitNanoSecond);
|
|
for(i=1;i<PWM_STEP_MAXI;i++)
|
|
{
|
|
if(CalibrationTab[i]/*+DelayStep/2*/>=CompensateWait) //DelayStep on PI2 but not on other models ?
|
|
{
|
|
|
|
break;
|
|
}
|
|
}
|
|
OverWaitNanoSecond+=CalibrationTab[i]/*+DelayStep/2*/-WaitNanoSecond;
|
|
//printf("step %d Overwait=%d\n",i,OverWaitNanoSecond);
|
|
int PwmStepDMA=i; //Number step performs by DMA
|
|
|
|
/* int DelayStep=CalibrationTab[i+1]-CalibrationTab[i];
|
|
int DelayMini=CalibrationTab[i]-((CalibrationTab[i+1]-CalibrationTab[i])*i);
|
|
|
|
DelayStep=157;
|
|
DelayMini=1200;
|
|
*/
|
|
int DelayMiniStep=DelayMini/DelayStep;
|
|
int NbStepPWM=PwmStepDMA+DelayMiniStep; // Number step we use to calculate including delay of Minimum constant due to perform Amplitude and getting the AXI bus
|
|
double UnitFrequency=DeltaFreq/(double)NbStepPWM; // Frequency granularity resulting in PWM Frequency
|
|
|
|
|
|
int NbF1=0,NbF2=0;
|
|
double FTunePercentage=0;
|
|
// if((TuneFrequency-f1)<UnitFrequency) {NbF1=0;NbF2=NbStepPWM;}
|
|
// if((f2-TuneFrequency)<UnitFrequency){NbF1=NbStepPWM;NbF2=0;}
|
|
|
|
//if((NbF1==0)&&(NbF2==0)) //Normal case
|
|
{
|
|
FTunePercentage=(f2-TuneFrequency)/DeltaFreq;
|
|
NbF1=FTunePercentage*NbStepPWM;
|
|
NbF2=NbStepPWM-NbF1;
|
|
}
|
|
|
|
if(ShowInfo)
|
|
{
|
|
printf("FTune=%f f1=%f f2=%f PLL Granularity=%f NbStepDMA=%d NbStepExtended=%d PWMFGranularity=%f\n",TuneFrequency,f1,f2,DeltaFreq,PwmStepDMA,NbStepPWM,UnitFrequency);
|
|
ShowInfo=0;
|
|
}
|
|
|
|
uint32_t RegisterF1;
|
|
uint32_t RegisterF2;
|
|
|
|
int NbF1DMA;
|
|
if(NbF2>DelayMiniStep)//NbF2 is the upper frequency which will be play longer due to delay
|
|
{
|
|
RegisterF1=0x5A000000 | (FreqDividerf1<<12) | (FreqFractionnalf1);
|
|
RegisterF2=0x5A000000 | (FreqDividerf2<<12) | (FreqFractionnalf2);
|
|
NbF1DMA=NbF1;
|
|
//NbF2DMA=NbF2-DelayMiniStep;
|
|
|
|
}
|
|
else // F1 and F2 Swap : NbF1 is now the lowest frequency which will play longer due to delay
|
|
{
|
|
RegisterF1=0x5A000000 | (FreqDividerf2<<12) | (FreqFractionnalf2);
|
|
RegisterF2=0x5A000000 | (FreqDividerf1<<12) | (FreqFractionnalf1);
|
|
NbF1DMA=NbF2;;
|
|
//NbF2DMA=NbF1-DelayMiniStep;
|
|
}
|
|
|
|
|
|
if(NoUsePWMF==1)
|
|
{
|
|
RegisterF1=RegisterF2;
|
|
}
|
|
|
|
//printf("Ftune %f NbStepPWM=%d NbF1=%d NbF2=%d DelayMini=%d DelayStep=%d DelayMiniStep%d\n",FTunePercentage,NbStepPWM,NbF1,NbF2,DelayMini,DelayStep,DelayMiniStep);
|
|
|
|
//Fill DMA
|
|
if(PwmStepDMA>1)
|
|
{
|
|
int BeginShuffle=rand()%(PwmStepDMA-1); //-1 cause last value should always be f2
|
|
for(i=0;i<NbF1DMA;i++)
|
|
{
|
|
//ctl->sample[NoSample].FrequencyTab[i]=RegisterF1;
|
|
ctl->sample[NoSample].FrequencyTab[Shuffle[PwmStepDMA-1][(i+BeginShuffle)%(PwmStepDMA-1)]]=RegisterF1;
|
|
}
|
|
for(i=NbF1DMA;i<PwmStepDMA-1;i++)
|
|
{
|
|
//ctl->sample[NoSample].FrequencyTab[i]=RegisterF2;
|
|
ctl->sample[NoSample].FrequencyTab[Shuffle[PwmStepDMA-1][(i+BeginShuffle)%(PwmStepDMA-1)]]=RegisterF2;
|
|
}
|
|
}
|
|
ctl->sample[NoSample].FrequencyTab[PwmStepDMA-1]=RegisterF2; //Always finish by f2 to be played later
|
|
|
|
dma_cb_t *cbpwrite=cbp+2;
|
|
cbpwrite->length=PwmStepDMA*4;
|
|
|
|
// ****************************** AMPLITUDE PROCESSING **********************************************
|
|
|
|
|
|
Amplitude=(Amplitude>32767)?32767:Amplitude;
|
|
int IntAmplitude=Amplitude*7/32767-1;
|
|
|
|
float LogAmplitude=-(10.0*log10((Amplitude+1)/32767.0));
|
|
|
|
if(LogAmplitude<=0.1) IntAmplitude=7;
|
|
if((LogAmplitude>0.1)&&(LogAmplitude<=0.3)) IntAmplitude=6;
|
|
if((LogAmplitude>0.3)&&(LogAmplitude<=0.7)) IntAmplitude=5;
|
|
if((LogAmplitude>0.7)&&(LogAmplitude<=1.1)) IntAmplitude=4;
|
|
if((LogAmplitude>1.1)&&(LogAmplitude<=2.1)) IntAmplitude=3;
|
|
if((LogAmplitude>2.1)&&(LogAmplitude<=4.1)) IntAmplitude=2;
|
|
if((LogAmplitude>4.1)&&(LogAmplitude<=7.9)) IntAmplitude=1;
|
|
if((LogAmplitude>7.9)&&(LogAmplitude<18.0)) IntAmplitude=0;
|
|
if((LogAmplitude>18)) IntAmplitude=-1;
|
|
|
|
|
|
//printf("Ampli %d Log=%f Pad=%d\n",Amplitude,LogAmplitude,IntAmplitude);
|
|
if(UsePCMClk==0)
|
|
{
|
|
if(IntAmplitude==-1)
|
|
{
|
|
ctl->sample[NoSample].Amplitude2=0x0;
|
|
}
|
|
else
|
|
{
|
|
ctl->sample[NoSample].Amplitude2=0xAAAAAAAA;
|
|
}
|
|
}
|
|
if(UsePCMClk==1)
|
|
{
|
|
if(IntAmplitude==-1)
|
|
{
|
|
ctl->sample[NoSample].Amplitude2=(Originfsel & ~(7 << 12)) | (0 << 12); //Pin is in
|
|
}
|
|
else
|
|
{
|
|
ctl->sample[NoSample].Amplitude2=(Originfsel & ~(7 << 12)) | (4 << 12); //Alternate is CLK
|
|
}
|
|
}
|
|
|
|
|
|
if(IntAmplitude>7) IntAmplitude=7;
|
|
if(IntAmplitude<0) IntAmplitude=0;
|
|
ctl->sample[NoSample].Amplitude1=0x5a000000 + (IntAmplitude&0x7) + (1<<4) + (0<<3);
|
|
|
|
return CalibrationTab[PwmStepDMA];
|
|
|
|
}
|
|
|
|
|
|
|
|
//Get from http://stackoverflow.com/questions/5083465/fast-efficient-least-squares-fit-algorithm-in-c
|
|
|
|
#define REAL double
|
|
|
|
|
|
inline static REAL sqr(REAL x) {
|
|
return x*x;
|
|
}
|
|
|
|
|
|
int linreg(int n, const REAL x[], const REAL y[], REAL* m, REAL* b, REAL* r)
|
|
{
|
|
REAL sumx = 0.0; /* sum of x */
|
|
REAL sumx2 = 0.0; /* sum of x**2 */
|
|
REAL sumxy = 0.0; /* sum of x * y */
|
|
REAL sumy = 0.0; /* sum of y */
|
|
REAL sumy2 = 0.0; /* sum of y**2 */
|
|
int i;
|
|
for (i=0;i<n;i++)
|
|
{
|
|
sumx += x[i];
|
|
sumx2 += sqr(x[i]);
|
|
sumxy += x[i] * y[i];
|
|
sumy += y[i];
|
|
sumy2 += sqr(y[i]);
|
|
}
|
|
|
|
REAL denom = (n * sumx2 - sqr(sumx));
|
|
if (denom == 0) {
|
|
// singular matrix. can't solve the problem.
|
|
*m = 0;
|
|
*b = 0;
|
|
if (r) *r = 0;
|
|
return 1;
|
|
}
|
|
|
|
*m = (n * sumxy - sumx * sumy) / denom;
|
|
*b = (sumy * sumx2 - sumx * sumxy) / denom;
|
|
if (r!=NULL) {
|
|
*r = (sumxy - sumx * sumy / n) / /* compute correlation coeff */
|
|
sqrt((sumx2 - sqr(sumx)/n) *
|
|
(sumy2 - sqr(sumy)/n));
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
int GetDMADelay(int Step)
|
|
{
|
|
|
|
//Calibrate DMA Rate
|
|
// =====================================================
|
|
static volatile uint32_t cur_cb,last_cb;
|
|
struct timespec gettime_now;
|
|
int32_t start_time,time_difference;
|
|
int last_sample;
|
|
int this_sample;
|
|
int free_slots;
|
|
|
|
dma_cb_t *cbp = ctl->cb;
|
|
cur_cb = (uint32_t)virtbase; // DMA AT 1st CBS
|
|
dma_reg[DMA_CONBLK_AD+DMA_CHANNEL*0x40]=mem_virt_to_phys((void*)cur_cb);
|
|
//usleep(100);
|
|
int samplecnt;
|
|
|
|
|
|
|
|
for (samplecnt = 0; samplecnt < NUM_SAMPLES ; samplecnt++)
|
|
{
|
|
|
|
cbp+=2;
|
|
cbp->length = (uint32_t)4L*Step;
|
|
cbp++;
|
|
}
|
|
|
|
dma_reg[DMA_CS+DMA_CHANNEL*0x40] = DMA_CS_PRIORITY(7) | DMA_CS_PANIC_PRIORITY(7) | DMA_CS_DISDEBUG |DMA_CS_ACTIVE; // START DMA : go, mid priority, wait for outstanding writes :7 Seems Max Priority
|
|
usleep(100); //Wait to be sure DMA is running stable
|
|
int i;
|
|
int SumDelay=0;
|
|
int NbLoopToAverage=2;
|
|
for(i=0;i<NbLoopToAverage;i++)
|
|
{
|
|
|
|
|
|
|
|
last_cb = mem_phys_to_virt((uint32_t)(dma_reg[DMA_CONBLK_AD+DMA_CHANNEL*0x40]));
|
|
last_sample = (last_cb - (uint32_t)virtbase) / (sizeof(dma_cb_t) * CBS_SIZE_BY_SAMPLE);
|
|
|
|
clock_gettime(CLOCK_REALTIME, &gettime_now);
|
|
start_time = gettime_now.tv_nsec;
|
|
|
|
// ONE SAMPLE SHOULD BE MINIMUM AROUND Time = NBStep * 157 ns + 1360 ns +1360*4 ns = 6us and MAX 200*157+1360*5 = 35us
|
|
// Sleep at 80% de DELAY*NUM_SAMPLE
|
|
// BY removing 2 CBP = 300ns gain
|
|
do
|
|
{
|
|
usleep(10);
|
|
cur_cb = mem_phys_to_virt((uint32_t)(dma_reg[DMA_CONBLK_AD+DMA_CHANNEL*0x40]));
|
|
this_sample = (cur_cb - (uint32_t)virtbase) / (sizeof(dma_cb_t) * CBS_SIZE_BY_SAMPLE);
|
|
free_slots = this_sample - last_sample;
|
|
if (free_slots < 0) // WARNING : ORIGINAL CODE WAS < strictly
|
|
free_slots += NUM_SAMPLES;
|
|
clock_gettime(CLOCK_REALTIME, &gettime_now);
|
|
|
|
}
|
|
while(free_slots<=NUM_SAMPLES*0.1);
|
|
|
|
|
|
|
|
time_difference = gettime_now.tv_nsec - start_time;
|
|
if(time_difference<0) time_difference+=1E9;
|
|
|
|
//printf("Delay = %d (time_diff %ld freesolt %d \n",time_difference/free_slots,time_difference,free_slots);
|
|
|
|
SumDelay+=time_difference/free_slots;
|
|
}
|
|
|
|
|
|
//STop DMA
|
|
dma_reg[DMA_CS+DMA_CHANNEL*0x40] |= DMA_CS_ABORT;//BCM2708_DMA_INT | BCM2708_DMA_END;
|
|
udelay(100);
|
|
dma_reg[DMA_CS+DMA_CHANNEL*0x40]&= ~DMA_CS_ACTIVE;
|
|
dma_reg[DMA_CS+DMA_CHANNEL*0x40] |= DMA_CS_RESET; //BCM2708_DMA_ABORT|BCM2708_DMA_RESET;
|
|
udelay(100);
|
|
|
|
return SumDelay/NbLoopToAverage;
|
|
}
|
|
|
|
int CalibrateSystem(double *ppm,int *BaseDelayDMA,float *StepDelayDMA)
|
|
{
|
|
struct timex ntx;
|
|
int status;
|
|
//Calibrate Clock system (surely depends also on PLL PPM
|
|
// =====================================================
|
|
|
|
ntx.modes = 0; /* only read */
|
|
status = ntp_adjtime(&ntx);
|
|
double clockppm;
|
|
|
|
|
|
switch(info.model)
|
|
{
|
|
case RPI_MODEL_B_PI_2:InitCalibrationTabPi2();break;
|
|
case RPI_MODEL_ZERO:InitCalibrationTabPiZero();break;
|
|
case RPI_MODEL_B_PI_3:InitCalibrationTabPi3();break;
|
|
default:printf("Warning: No calibration for this pi model \n");InitCalibrationTabPiZero();break;
|
|
}
|
|
|
|
if (status != TIME_OK)
|
|
{
|
|
printf("Error: NTP\n");
|
|
*ppm=0;
|
|
//return 0;
|
|
}
|
|
else
|
|
{
|
|
clockppm = (double)ntx.freq/(double)(1 << 16);
|
|
if(abs(clockppm)<200)
|
|
*ppm=clockppm;
|
|
}
|
|
//printf("Clock PPM = %f\n",ppm);
|
|
int i;
|
|
int BaseDelay=1;
|
|
/*BaseDelay=GetDMADelay(0);
|
|
|
|
*BaseDelayDMA=BaseDelay;
|
|
*StepDelayDMA=(GetDMADelay(PWM_STEP_MAXI/2)-(*BaseDelayDMA))/(PWM_STEP_MAXI/2);*/
|
|
char csvline[255];
|
|
|
|
|
|
REAL x[PWM_STEP_MAXI];
|
|
REAL y[PWM_STEP_MAXI];
|
|
#define WRITE_CALIBRATION 1
|
|
#ifdef WRITE_CALIBRATION
|
|
int hFileCsv;
|
|
hFileCsv=open("calib.csv",O_CREAT | O_WRONLY);
|
|
#endif
|
|
printf("Performs calibration ");
|
|
for(i=1;i<PWM_STEP_MAXI;i+=1)
|
|
{
|
|
int Delay=GetDMADelay(i);
|
|
//printf("Step %d :%d \n",i,Delay);//,(GetDMADelay(i)-BaseDelay)/i);
|
|
|
|
CalibrationTab[i]=Delay;
|
|
if((i%10)==0){printf(".");fflush(stdout);}
|
|
#ifdef WRITE_CALIBRATION
|
|
sprintf(csvline,"CalibrationTab[%d]=%d;\n",i,Delay);
|
|
write(hFileCsv,csvline,strlen(csvline));
|
|
#endif
|
|
|
|
}
|
|
|
|
printf("\n");
|
|
REAL m,b,r;
|
|
|
|
for(i=1;i<PWM_STEP_MAXI;i++)
|
|
{
|
|
x[i]=i;
|
|
y[i]=CalibrationTab[i];
|
|
}
|
|
#define TangentWindow 10
|
|
|
|
float Sum[2]={0.0,0.0};
|
|
int NumberToAverage=0;
|
|
for(i=20;i<PWM_STEP_MAXI-20;i++)
|
|
{
|
|
int n=TangentWindow+i<PWM_STEP_MAXI?TangentWindow:PWM_STEP_MAXI-i;
|
|
int Begin=((i-TangentWindow)<0)?i:i-TangentWindow;
|
|
linreg(n*2,x+Begin,y+Begin,&m,&b,&r);
|
|
Sum[0]+=m;
|
|
Sum[1]+=b;
|
|
NumberToAverage++;
|
|
//printf("Timing step %d = %f step + %f\n",i,m,b);
|
|
}
|
|
|
|
DelayStep=Sum[0]/NumberToAverage;
|
|
DelayMini=Sum[1]/NumberToAverage;
|
|
|
|
//printf("DMA Delay Step =%f Delay =%f\n",DelayStep,DelayMini);
|
|
return 1;
|
|
}
|
|
|
|
void InitShuffle()
|
|
{
|
|
uint32_t i,j;
|
|
for(i=1;i<PWM_STEP_MAXI;i++)
|
|
{
|
|
Shuffle[i]=(uint32_t *)malloc(i*sizeof(uint32_t));
|
|
|
|
for(j=0;j<i;j++)
|
|
{
|
|
Shuffle[i][j]=j;
|
|
}
|
|
shuffle_int(Shuffle[i],i-1);
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
int pitx_init(int SampleRate, double TuningFrequency, int* skipSignals,int SetDma)
|
|
{
|
|
InitGpio();
|
|
InitDma(terminate, skipSignals);
|
|
if(SetDma) DMA_CHANNEL=SetDma;
|
|
|
|
SetupGpioClock(SampleRate,TuningFrequency);
|
|
InitShuffle();
|
|
//int FREQ_MINI_TIMING=157;
|
|
//int PWMF_MARGIN = 1120; //A Margin for now at 1us with PCM ->OK
|
|
|
|
if(CalibrateSystem(&globalppmpll,&PWMF_MARGIN,&FREQ_MINI_TIMING)) printf("Calibrate : ppm=%f DMA %fns:%fns\n",globalppmpll,DelayStep,DelayMini);
|
|
//printf("Timing : 1 cyle=%dns 1sample=%dns\n",NBSAMPLES_PWM_FREQ_MAX*400*3,(int)(1e9/(float)SampleRate));
|
|
return 1;
|
|
}
|
|
|
|
void print_usage(void)
|
|
{
|
|
|
|
fprintf(stderr,\
|
|
"\nrpitx -%s\n\
|
|
Usage:\nrpitx [-i File Input][-m ModeInput] [-f frequency output] [-s Samplerate] [-l] [-p ppm] [-h] \n\
|
|
-m {IQ(FileInput is a Stereo Wav contains I on left Channel, Q on right channel)}\n\
|
|
{IQFLOAT(FileInput is a Raw float interlaced I,Q)}\n\
|
|
{RF(FileInput is a (double)Frequency,Time in nanoseconds}\n\
|
|
{RFA(FileInput is a (double)Frequency,(int)Time in nanoseconds,(float)Amplitude}\n\
|
|
{VFO (constant frequency)}\n\
|
|
-i path to File Input \n\
|
|
-f float frequency to output on GPIO_18 pin 12 in khz : (130 kHz to 750 MHz),\n\
|
|
-l loop mode for file input\n\
|
|
-p float frequency correction in parts per million (ppm), positive or negative, for calibration, default 0.\n\
|
|
-d int DMABurstSize (default 1000) but for very short message, could be decrease\n\
|
|
-h help (this help).\n\
|
|
\n",\
|
|
PROGRAM_VERSION);
|
|
|
|
} /* end function print_usage */
|
|
|
|
|
|
|
|
double GlobalTuningFrequency;
|
|
int HarmonicNumber =1;
|
|
|
|
int pitx_SetTuneFrequency(double Frequency)
|
|
{
|
|
#define MAX_HARMONIC 41
|
|
int harmonic;
|
|
|
|
if(Frequency<PLL_FREQ_1GHZ/2048L) //2/4096-> For very Low Frequency we used 19.2 MHZ PLL
|
|
{
|
|
PllUsed=PllFreq19MHZ;
|
|
PllNumber=PLL_192;
|
|
}
|
|
else
|
|
{
|
|
PllUsed=PllFreq1GHZ;
|
|
PllNumber=PLL_1GHZ;
|
|
}
|
|
|
|
printf("Master PLL = %u Hz\n",PllUsed);
|
|
|
|
for(harmonic=1;harmonic<MAX_HARMONIC;harmonic+=2)
|
|
{
|
|
//printf("->%lf harmonic %d\n",(TuneFrequency/(double)harmonic),harmonic);
|
|
if((Frequency/(double)harmonic)<=(double)PllUsed/3.0) break;
|
|
}
|
|
HarmonicNumber=harmonic;
|
|
|
|
//HarmonicNumber=11; //TEST
|
|
|
|
if(HarmonicNumber>1) //Use Harmonic
|
|
{
|
|
GlobalTuningFrequency=Frequency/*/HarmonicNumber*/;
|
|
printf("\n Warning : Using harmonic %d -> Frequency fundamental on %f\n",HarmonicNumber,GlobalTuningFrequency/HarmonicNumber);
|
|
}
|
|
else
|
|
{
|
|
GlobalTuningFrequency=Frequency;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
/** Wrapper around read. */
|
|
static ssize_t readFile(void *buffer, const size_t count)
|
|
{
|
|
return read(FileInHandle, buffer, count);
|
|
}
|
|
static void resetFile(void)
|
|
{
|
|
lseek(FileInHandle, 0, SEEK_SET);
|
|
}
|
|
|
|
int main(int argc, char* argv[])
|
|
{
|
|
int a;
|
|
int anyargs = 0;
|
|
char Mode = MODE_IQ; // By default
|
|
int SampleRate=48000;
|
|
float SetFrequency=1e6;//1MHZ
|
|
float ppmpll=0.0;
|
|
char NoUsePwmFrequency=0;
|
|
int SetDma=0;
|
|
while(1)
|
|
{
|
|
a = getopt(argc, argv, "i:f:m:s:p:hld:w:c:r:a:");
|
|
|
|
if(a == -1)
|
|
{
|
|
if(anyargs) break;
|
|
else a='h'; //print usage and exit
|
|
}
|
|
anyargs = 1;
|
|
|
|
switch(a)
|
|
{
|
|
case 'i': // File name
|
|
FileName = optarg;
|
|
break;
|
|
case 'f': // Frequency
|
|
SetFrequency = atof(optarg);
|
|
break;
|
|
case 'm': // Mode (IQ,IQFLOAT,RF,RFA)
|
|
if(strcmp("IQ",optarg)==0) Mode=MODE_IQ;
|
|
if(strcmp("RF",optarg)==0) Mode=MODE_RF;
|
|
if(strcmp("RFA",optarg)==0) Mode=MODE_RFA;
|
|
if(strcmp("IQFLOAT",optarg)==0) Mode=MODE_IQ_FLOAT;
|
|
if(strcmp("VFO",optarg)==0) Mode=MODE_VFO;
|
|
break;
|
|
case 's': // SampleRate (Only needeed in IQ mode)
|
|
SampleRate = atoi(optarg);
|
|
break;
|
|
case 'p': // ppmcorrection
|
|
ppmpll = atof(optarg);
|
|
|
|
break;
|
|
case 'h': // help
|
|
print_usage();
|
|
exit(1);
|
|
break;
|
|
case 'l': // loop mode
|
|
loop_mode_flag = 1;
|
|
break;
|
|
case 'd': // Dma Sample Burst
|
|
DmaSampleBurstSize = atoi(optarg);
|
|
NUM_SAMPLES=4*DmaSampleBurstSize;
|
|
break;
|
|
case 'c': // Use clock instead of PWM pin
|
|
UsePCMClk = atoi(optarg);
|
|
|
|
break;
|
|
case 'w': // No use pwmfrequency
|
|
NoUsePwmFrequency = atoi(optarg);
|
|
|
|
break;
|
|
case 'r': // Randomize PWM frequency 1 by defaut
|
|
Randomize=atoi(optarg);
|
|
|
|
break;
|
|
case 'a': // DMA Channel 1-14
|
|
if((atoi(optarg)>0)&&(atoi(optarg)<15))
|
|
{
|
|
SetDma=atoi(optarg);
|
|
//DMA_CHANNEL=SetDma; Should be set after initdma
|
|
}
|
|
else
|
|
SetDma=0;
|
|
break;
|
|
case -1:
|
|
break;
|
|
case '?':
|
|
if (isprint(optopt) )
|
|
{
|
|
fprintf(stderr, "rpitx: unknown option `-%c'.\n", optopt);
|
|
}
|
|
else
|
|
{
|
|
fprintf(stderr, "rpitx: unknown option character `\\x%x'.\n", optopt);
|
|
}
|
|
print_usage();
|
|
|
|
exit(1);
|
|
break;
|
|
default:
|
|
print_usage();
|
|
exit(1);
|
|
break;
|
|
}/* end switch a */
|
|
}/* end while getopt() */
|
|
|
|
//Open File Input for modes which need it
|
|
if((Mode==MODE_IQ)||(Mode==MODE_IQ_FLOAT)||(Mode==MODE_RF)||(Mode==MODE_RFA))
|
|
{
|
|
if(FileName && strcmp(FileName,"-")==0)
|
|
{
|
|
FileInHandle = STDIN_FILENO;
|
|
useStdin = 1;
|
|
}
|
|
else
|
|
FileInHandle = open(FileName, O_RDONLY);
|
|
|
|
if (FileInHandle < 0)
|
|
{
|
|
fatal("Failed to read Filein %s\n",FileName);
|
|
}
|
|
}
|
|
if(UsePCMClk==1)
|
|
printf("Output to GPCLK Pin (Header No 7)\n");
|
|
else
|
|
printf("Output to PWM Pin (Header No 12)\n");
|
|
resetFile();
|
|
return pitx_run(Mode, SampleRate, SetFrequency, ppmpll, NoUsePwmFrequency, readFile, resetFile, NULL,SetDma);
|
|
}
|
|
|
|
int pitx_run(
|
|
const char Mode,
|
|
int SampleRate,
|
|
const float SetFrequency,
|
|
float ppmpll,
|
|
const char NoUsePwmFrequency,
|
|
ssize_t (*readWrapper)(void *buffer, size_t count),
|
|
void (*reset)(void),
|
|
int* skipSignals,
|
|
int SetDma)
|
|
{
|
|
int i;
|
|
//char pagemap_fn[64];
|
|
|
|
int OffsetModulation=1000;//TBR
|
|
int MicGain=100;
|
|
//unsigned char *data;
|
|
|
|
//Specific to ModeIQ
|
|
static signed short *IQArray=NULL;
|
|
|
|
//Specific to ModeIQ_FLOAT
|
|
static float *IQFloatArray=NULL;
|
|
|
|
//Specific to Mode RF
|
|
typedef struct {
|
|
double Frequency;
|
|
uint32_t WaitForThisSample;
|
|
} samplerf_t;
|
|
samplerf_t *TabRfSample=NULL;
|
|
|
|
fprintf(stdout,"rpitx Version %s compiled %s (F5OEO Evariste) \n",PROGRAM_VERSION,__DATE__);
|
|
|
|
PllFreq500MHZ=PLL_FREQ_500MHZ;
|
|
PllFreq1GHZ=PLL_FREQ_1GHZ;
|
|
PllFreq19MHZ=PLLFREQ_192;
|
|
|
|
|
|
//End of Init Plls
|
|
|
|
if(Mode==MODE_IQ)
|
|
{
|
|
IQArray=malloc(DmaSampleBurstSize*2*sizeof(signed short)); // TODO A FREE AT THE END OF SOFTWARE
|
|
reset();
|
|
}
|
|
if(Mode==MODE_IQ_FLOAT)
|
|
{
|
|
//NUM_SAMPLES=8*DmaSampleBurstSize;
|
|
IQFloatArray=malloc(DmaSampleBurstSize*2*sizeof(float)); // TODO A FREE AT THE END OF SOFTWARE
|
|
}
|
|
if((Mode==MODE_RF)||(Mode==MODE_RFA))
|
|
{
|
|
//TabRfSample=malloc(DmaSampleBurstSize*sizeof(samplerf_t));
|
|
SampleRate=50000L; //NOT USED BUT BY CALCULATING TIMETOSLEEP IN RF MODE
|
|
}
|
|
if(Mode==MODE_VFO)
|
|
SampleRate=50000L; //50000 BY EXPERIMENT
|
|
|
|
if(Mode==MODE_IQ)
|
|
{
|
|
printf(" Frequency=%f ",GlobalTuningFrequency);
|
|
printf(" SampleRate=%d ",SampleRate);
|
|
}
|
|
|
|
|
|
|
|
|
|
pitx_SetTuneFrequency(SetFrequency*1000.0);
|
|
pitx_init(SampleRate, GlobalTuningFrequency/HarmonicNumber, skipSignals,SetDma);
|
|
//Correct PLL Frequency
|
|
|
|
if(ppmpll==0) ppmpll=-globalppmpll; // Use calibrate only if not setting by user
|
|
|
|
PllUsed+=(PllUsed * ppmpll) / 1000000.0;
|
|
//printf("PLL ppm=%f -> PllUsed %u\n",ppmpll,PllUsed);
|
|
|
|
|
|
|
|
|
|
static volatile uint32_t cur_cb,last_cb;
|
|
int last_sample;
|
|
int this_sample;
|
|
int free_slots;
|
|
//int SumDelay=0;
|
|
|
|
long int start_time;
|
|
static long time_difference=0;
|
|
struct timespec gettime_now;
|
|
|
|
cur_cb = (uint32_t)virtbase+ (NUM_SAMPLES-DmaSampleBurstSize)* sizeof(dma_cb_t) *CBS_SIZE_BY_SAMPLE;
|
|
|
|
last_cb=(uint32_t)virtbase /*+ 965* sizeof(dma_cb_t) *CBS_SIZE_BY_SAMPLE*/ ;
|
|
|
|
|
|
dma_reg[DMA_CONBLK_AD+DMA_CHANNEL*0x40]=mem_virt_to_phys((void*)cur_cb);
|
|
|
|
unsigned char Init=1;
|
|
|
|
// -----------------------------------------------------------------
|
|
struct stat bufstat;
|
|
{
|
|
int ret;
|
|
fstat(FileInHandle,&bufstat);
|
|
if(S_ISFIFO(bufstat.st_mode)) printf("Using a Pipe\n");
|
|
}
|
|
for (;;)
|
|
{
|
|
int TimeToSleep;
|
|
static int StatusCompteur=0;
|
|
|
|
cur_cb = mem_phys_to_virt((uint32_t)(dma_reg[DMA_CONBLK_AD+DMA_CHANNEL*0x40]));
|
|
this_sample = (cur_cb - (uint32_t)virtbase) / (sizeof(dma_cb_t) * CBS_SIZE_BY_SAMPLE);
|
|
last_sample = (last_cb - (uint32_t)virtbase) / (sizeof(dma_cb_t) * CBS_SIZE_BY_SAMPLE);
|
|
free_slots = this_sample - last_sample;
|
|
if (free_slots < 0) // WARNING : ORIGINAL CODE WAS < strictly
|
|
free_slots += NUM_SAMPLES;
|
|
|
|
//printf("last_sample %lx cur_cb %lx FreeSlots = %d Time to sleep=%d\n",last_sample,cur_cb,free_slots,TimeToSleep);
|
|
|
|
if(Init==0)
|
|
{
|
|
if((Mode==MODE_RF)||(Mode==MODE_RFA))
|
|
{
|
|
TimeToSleep=50; //Max 200KHZ
|
|
}
|
|
else
|
|
{
|
|
|
|
TimeToSleep=(1e6*(NUM_SAMPLES-free_slots*2))/SampleRate; // Time to sleep in us
|
|
}
|
|
//printf("TimeToSleep%d\n",TimeToSleep);
|
|
}
|
|
else
|
|
TimeToSleep=1000;
|
|
|
|
//printf("Buffer Available=%d\n",BufferAvailable());
|
|
|
|
clock_gettime(CLOCK_REALTIME, &gettime_now);
|
|
start_time = gettime_now.tv_nsec;
|
|
if(TimeToSleep>=(KERNEL_GRANULARITY)) // 2ms : Time to process File/Canal Coding
|
|
{
|
|
udelay(TimeToSleep-(KERNEL_GRANULARITY));
|
|
TimeToSleep=0;
|
|
}
|
|
else
|
|
{
|
|
//udelay(TimeToSleep);
|
|
sched_yield();
|
|
//TimeToSleep=0;
|
|
//if(free_slots>(NUM_SAMPLES*9/10))
|
|
//printf("Buffer nearly empty...%d/%d\n",free_slots,NUM_SAMPLES);
|
|
}
|
|
|
|
static int free_slots_now;
|
|
cur_cb = mem_phys_to_virt(dma_reg[DMA_CONBLK_AD+DMA_CHANNEL*0x40]);
|
|
this_sample = (cur_cb - (uint32_t)virtbase) / (sizeof(dma_cb_t) * CBS_SIZE_BY_SAMPLE);
|
|
last_sample = (last_cb - (uint32_t)virtbase) / (sizeof(dma_cb_t) * CBS_SIZE_BY_SAMPLE);
|
|
free_slots_now = this_sample - last_sample;
|
|
if (free_slots_now < 0) // WARNING : ORIGINAL CODE WAS < strictly
|
|
free_slots_now += NUM_SAMPLES;
|
|
|
|
clock_gettime(CLOCK_REALTIME, &gettime_now);
|
|
time_difference = gettime_now.tv_nsec - start_time;
|
|
if(time_difference<0) time_difference+=1E9;
|
|
|
|
if(StatusCompteur%10==0)
|
|
{
|
|
//printf(" DiffTime = %ld FreeSlot %d FreeSlotDiff=%d Bitrate : %f\n",time_difference,free_slots_now,free_slots_now-free_slots,(1e9*(free_slots_now-free_slots))/(float)time_difference);
|
|
}
|
|
//if((1e9*(free_slots_now-free_slots))/(float)time_difference<40100.0) printf("Drift BAD\n"); else printf("Drift GOOD\n");
|
|
StatusCompteur++;
|
|
free_slots=free_slots_now;
|
|
// FIX IT : Max(freeslot et Numsample/8)
|
|
if((Init==1)&&(free_slots < DmaSampleBurstSize /*NUM_SAMPLES/8*/))
|
|
{
|
|
printf("****** STARTING TRANSMIT ********\n");
|
|
dma_reg[DMA_CONBLK_AD+DMA_CHANNEL*0x40]=mem_virt_to_phys((void*)virtbase );
|
|
usleep(100);
|
|
//Start DMA PWMFrequency
|
|
|
|
//dma_reg[DMA_CS+DMA_CHANNEL_PWMFREQUENCY*0x40] = 0x10880001;
|
|
|
|
//Start Main DMA
|
|
dma_reg[DMA_CS+DMA_CHANNEL*0x40] = DMA_CS_PRIORITY(7) | DMA_CS_PANIC_PRIORITY(7) | DMA_CS_DISDEBUG |DMA_CS_ACTIVE;
|
|
|
|
|
|
Init=0;
|
|
|
|
continue;
|
|
}
|
|
clock_gettime(CLOCK_REALTIME, &gettime_now);
|
|
start_time = gettime_now.tv_nsec;
|
|
|
|
int debug=0;
|
|
|
|
if ((free_slots>=DmaSampleBurstSize))
|
|
{
|
|
// *************************************** MODE IQ **************************************************
|
|
if(Mode==MODE_IQ)
|
|
{
|
|
int NbRead=0;
|
|
static int Max=0;
|
|
static int Min=32767;
|
|
static int CompteSample=0;
|
|
CompteSample++;
|
|
NbRead=readWrapper(IQArray,DmaSampleBurstSize*2*2/*SHORT I,SHORT Q*/);
|
|
|
|
if(NbRead!=DmaSampleBurstSize*2*2)
|
|
{
|
|
if(loop_mode_flag==1)
|
|
{
|
|
printf("Looping FileIn\n");
|
|
reset();
|
|
NbRead=readWrapper(IQArray,DmaSampleBurstSize*2*2);
|
|
}
|
|
else {
|
|
stop_dma();
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
for(i=0;i<DmaSampleBurstSize;i++)
|
|
{
|
|
//static float samplerate=48000;
|
|
static int amp;
|
|
static double df;
|
|
|
|
int CorrectionRpiFrequency=000; //TODO PPM / Offset=1KHZ at 144MHZ
|
|
|
|
CompteSample++;
|
|
//printf("i%d q%d\n",IQArray[2*i],IQArray[2*i+1]);
|
|
|
|
IQToFreqAmp(IQArray[2*i+1],IQArray[2*i],&df,&,SampleRate);
|
|
df+=CorrectionRpiFrequency;
|
|
|
|
|
|
//df=1440+rand()%2000;
|
|
// Compression have to be done in modulation (SSB not here)
|
|
|
|
double A = 87.7f; // compression parameter
|
|
double ampf=amp/32767.0;
|
|
ampf = (fabs(ampf) < 1.0f/A) ? A*fabs(ampf)/(1.0f+ln(A)) : (1.0f+ln(A*fabs(ampf)))/(1.0f+ln(A)); //compand
|
|
//amp= (int)(round(ampf * 32767.0f)) ;
|
|
|
|
if(amp>Max) Max=amp;
|
|
if(amp<Min) Min=amp;
|
|
|
|
/*
|
|
if((CompteSample%4800)==0)
|
|
{
|
|
//printf("%d\n",((CompteSample/48000)*1024)%32767);
|
|
//printf("Amp %d Freq %f MinAmp %d MaxAmp %d\n",amp,df,Min,Max);
|
|
printf("%d;%d;%d;%f\n",IQArray[2*i+1],IQArray[2*i],amp,df);
|
|
// printf(".");
|
|
fflush(stdout);
|
|
}
|
|
*/
|
|
// TEST - WARNING, REMOVE FOR RELEASE
|
|
//amp=((CompteSample/48000)*1024)%32767;
|
|
//df=0;
|
|
//amp=amp*10;
|
|
//amp=32767;
|
|
//if(df<OffsetModulation+100) amp=0;
|
|
//if(amp<32767/8) df= OffsetModulation;
|
|
//
|
|
|
|
// FIXME : df/harmonicNumber could alterate maybe modulations
|
|
FrequencyAmplitudeToRegister2((GlobalTuningFrequency-OffsetModulation+/*(CompteSample/480)*/+df/HarmonicNumber)/HarmonicNumber,amp,last_sample++,0,SampleRate,NoUsePwmFrequency,CompteSample%2);
|
|
// !!!!!!!!!!!!!!!!!!!! 680 is for 48KHZ , should be adpated !!!!!!!!!!!!!!!!!
|
|
|
|
free_slots--;
|
|
if (last_sample == NUM_SAMPLES) last_sample = 0;
|
|
}
|
|
}
|
|
// *************************************** MODE IQ FLOAT**************************************************
|
|
if(Mode==MODE_IQ_FLOAT)
|
|
{
|
|
int NbRead=0;
|
|
static int Max=0;
|
|
static int Min=32767;
|
|
static int CompteSample=0;
|
|
CompteSample++;
|
|
|
|
if(S_ISFIFO(bufstat.st_mode))
|
|
{
|
|
int n;
|
|
int res=ioctl(FileInHandle, FIONREAD, &n);
|
|
|
|
if(res<0) {stop_dma(); return 0;}
|
|
if(n<DmaSampleBurstSize*2*sizeof(float)) //not enough data
|
|
{
|
|
if(n>0)
|
|
{
|
|
NbRead=readWrapper(IQFloatArray,n);
|
|
}
|
|
else
|
|
NbRead=0;
|
|
memset(IQFloatArray+NbRead,0,DmaSampleBurstSize*2*sizeof(float)-NbRead); // Set all at zero
|
|
NbRead=DmaSampleBurstSize*2*sizeof(float);
|
|
//printf("#");
|
|
}
|
|
else
|
|
{
|
|
//printf("rpitx get data%d\n",n);
|
|
if(n>DmaSampleBurstSize*2*sizeof(float)) n=DmaSampleBurstSize*2*sizeof(float);
|
|
|
|
NbRead=readWrapper(IQFloatArray,n);
|
|
}
|
|
|
|
}
|
|
else
|
|
NbRead=readWrapper(IQFloatArray,DmaSampleBurstSize*2*sizeof(float));
|
|
|
|
if(NbRead!=DmaSampleBurstSize*2*sizeof(float))
|
|
{
|
|
printf("rpitx: NbRead %d/%d\n",NbRead,DmaSampleBurstSize*2*sizeof(float));
|
|
if(NbRead<=0)
|
|
{
|
|
if(loop_mode_flag==1)
|
|
{
|
|
printf("Looping FileIn\n");
|
|
reset();
|
|
}
|
|
else if (!useStdin) {
|
|
stop_dma();
|
|
return 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
for(i=0;i<NbRead/(2*sizeof(float));i++)
|
|
{
|
|
//static float samplerate=48000;
|
|
static int amp;
|
|
static double df;
|
|
|
|
int CorrectionRpiFrequency=000; //TODO PPM / Offset=1KHZ at 144MHZ
|
|
|
|
CompteSample++;
|
|
//printf("i%d q%d\n",IQArray[2*i],IQArray[2*i+1]);
|
|
|
|
IQToFreqAmp(IQFloatArray[2*i+1]*32767,IQFloatArray[2*i]*32767,&df,&,SampleRate);
|
|
//printf("df=%f\n",df);
|
|
if(amp>Max) Max=amp;
|
|
if(amp<Min) Min=amp;
|
|
/*
|
|
if((CompteSample%4800)==0)
|
|
{
|
|
//printf("%d\n",((CompteSample/48000)*1024)%32767);
|
|
printf("Amp %d Freq %f MinAmp %d MaxAmp %d\n",amp,df,Min,Max);
|
|
// printf(".");
|
|
fflush(stdout);
|
|
}
|
|
*/
|
|
// TEST - WARNING, REMOVE FOR RELEASE
|
|
//amp=((CompteSample/48000)*1024)%32767;
|
|
//df=0;
|
|
//amp=amp*10;
|
|
//amp=32767;
|
|
//if(df<OffsetModulation+100) amp=0;
|
|
//
|
|
//amp=32767;
|
|
//if(df>SampleRate/2) df=SampleRate/2-df;
|
|
FrequencyAmplitudeToRegister2((GlobalTuningFrequency/*-OffsetModulation*/+df/*/HarmonicNumber*/)/HarmonicNumber,amp,last_sample++,0,SampleRate,NoUsePwmFrequency,CompteSample%2);
|
|
|
|
free_slots--;
|
|
if (last_sample == NUM_SAMPLES) last_sample = 0;
|
|
}
|
|
}
|
|
// *************************************** MODE RF **************************************************
|
|
if((Mode==MODE_RF)||(Mode==MODE_RFA))
|
|
{
|
|
// SHOULD NOT EXEED 200 STEP*500ns; SAMPLERATE SHOULD BE MAX TO HAVE PRECISION FOR PCM
|
|
// BUT FIFO OF PCM IS 16 : SAMPLERATE MAYBE NOT EXCESS 16*80000 ! CAREFULL BUGS HERE
|
|
//#define MAX_DELAY_WAIT (PWM_STEP_MAXI/2*FREQ_MINI_TIMING-PWMF_MARGIN)
|
|
int MAX_DELAY_WAIT = 28000; //CalibrationTab[199];
|
|
static int CompteSample=0;
|
|
static uint32_t TimeRemaining=0;
|
|
static samplerf_t SampleRf;
|
|
static int NbRead;
|
|
CompteSample++;
|
|
int i;
|
|
for(i=0;i<DmaSampleBurstSize;i++)
|
|
{
|
|
|
|
if(TimeRemaining==0)
|
|
{
|
|
NbRead=readWrapper(&SampleRf,sizeof(samplerf_t));
|
|
|
|
if(NbRead!=sizeof(samplerf_t))
|
|
{
|
|
if(loop_mode_flag==1)
|
|
{
|
|
//printf("Looping FileIn\n");
|
|
reset();
|
|
NbRead=readWrapper(&SampleRf,sizeof(samplerf_t));
|
|
}
|
|
else if (!useStdin)
|
|
{
|
|
stop_dma();
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
TimeRemaining=SampleRf.WaitForThisSample;
|
|
//TimeRemaining=50000;//SampleRf.WaitForThisSample;
|
|
debug=1;
|
|
//printf("A=%f Time =%d \n",SampleRf.Frequency,SampleRf.WaitForThisSample);
|
|
}
|
|
else
|
|
debug=0;
|
|
|
|
static int amp=32767;
|
|
static int WaitSample=0;
|
|
|
|
if(TimeRemaining>MAX_DELAY_WAIT)
|
|
WaitSample=MAX_DELAY_WAIT;
|
|
else
|
|
WaitSample=TimeRemaining;
|
|
|
|
//printf("TimeRemaining %d WaitSample %d\n",TimeRemaining,WaitSample);
|
|
if(Mode==MODE_RF)
|
|
{
|
|
//Need to fix : in FM need a constant carrier, in SSTV need sometimes to pause
|
|
if(SampleRf.Frequency==0.0)
|
|
{
|
|
amp=0;
|
|
SampleRf.Frequency=00.0;// TODO change that ugly frequency
|
|
|
|
}
|
|
else
|
|
amp=32767;
|
|
|
|
int RealWait=FrequencyAmplitudeToRegister2((SampleRf.Frequency/*/HarmonicNumber*/+GlobalTuningFrequency)/HarmonicNumber,amp,last_sample++,WaitSample,0,NoUsePwmFrequency,debug);
|
|
//printf("Wait=%d RealWait=%d\n",WaitSample,RealWait);
|
|
}
|
|
if(Mode==MODE_RFA)
|
|
FrequencyAmplitudeToRegister2((GlobalTuningFrequency)/HarmonicNumber,SampleRf.Frequency,last_sample++,WaitSample,0,NoUsePwmFrequency,debug);
|
|
|
|
TimeRemaining-=WaitSample;
|
|
free_slots--;
|
|
if (last_sample == NUM_SAMPLES) last_sample = 0;
|
|
}
|
|
}
|
|
|
|
// *************************************** MODE VFO **************************************************
|
|
if(Mode==MODE_VFO)
|
|
{
|
|
static uint32_t CompteSample=0;
|
|
|
|
int i;
|
|
//printf("Begin free %d\n",free_slots);
|
|
static int Up=1;
|
|
for(i=0;i<DmaSampleBurstSize;i++)
|
|
{
|
|
//To be fine tuned !!!!
|
|
static int OutputPower=32767;
|
|
if(Up==1)
|
|
{
|
|
|
|
CompteSample++;
|
|
//if(CompteSample==327670) Up=0;
|
|
}
|
|
else
|
|
{
|
|
CompteSample--;
|
|
if(CompteSample==0) Up=1;
|
|
}
|
|
debug=1;//(debug+1)%2;
|
|
//OutputPower=((CompteSample/50)%2)*32767;
|
|
|
|
uint32_t RealWait=FrequencyAmplitudeToRegister2(GlobalTuningFrequency/HarmonicNumber+(CompteSample*0.00),OutputPower,last_sample++,20000,0,NoUsePwmFrequency,debug);
|
|
//printf("RealWait %d\n",(CompteSample/100)%15000+5000);
|
|
free_slots--;
|
|
//printf("%f \n",GlobalTuningFrequency+(((CompteSample/10)*1)%50000));
|
|
if (last_sample == NUM_SAMPLES) last_sample = 0;
|
|
if(CompteSample%40000==0)
|
|
{
|
|
//OutputPower=(OutputPower+1)%8;
|
|
//pad_gpios_reg[PADS_GPIO_0] = 0x5a000000 + (OutputPower&0x7) + (1<<4) + (0<<3); // Set output power for I/Q GPIO18/GPIO19
|
|
//printf("Freq %d Outputpower=%d\n",CompteSample/20000,OutputPower);
|
|
}
|
|
//usleep(1);
|
|
}
|
|
//printf("End free %d\n",free_slots);
|
|
}
|
|
|
|
}
|
|
|
|
clock_gettime(CLOCK_REALTIME, &gettime_now);
|
|
time_difference = gettime_now.tv_nsec - start_time;
|
|
if(time_difference<0) time_difference+=1E9;
|
|
|
|
last_cb = (uint32_t)virtbase + last_sample * sizeof(dma_cb_t) * CBS_SIZE_BY_SAMPLE;
|
|
}
|
|
|
|
stop_dma();
|
|
return(0);
|
|
}
|
|
|