kopia lustrzana https://github.com/F5OEO/rpitx
1380 wiersze
38 KiB
C
1380 wiersze
38 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>
|
|
|
|
#define AMP_BYPAD
|
|
|
|
//Minimum Time in us to sleep
|
|
#define KERNEL_GRANULARITY 20000
|
|
|
|
#define SCHED_PRIORITY 30 //Linux scheduler priority. Higher = more realtime
|
|
|
|
|
|
#define PROGRAM_VERSION "0.2"
|
|
|
|
#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 1000000000 //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
|
|
#define FREQ_DELAY_TIME 0
|
|
int FREQ_MINI_TIMING=157;
|
|
int PWMF_MARGIN = 1120; //A Margin for now at 1us with PCM ->OK
|
|
int globalppmpll=0;
|
|
|
|
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=0;
|
|
|
|
uint32_t GlobalTabPwmFrequency[50];
|
|
|
|
//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=0;
|
|
uint32_t Originfsel=0;
|
|
|
|
int SetupGpioClock(uint32_t SymbolRate,double TuningFrequency)
|
|
{
|
|
char MASH=1;
|
|
|
|
if(UsePCMClk==0) TuningFrequency=TuningFrequency*2;
|
|
if((TuningFrequency>=100e6)&&(TuningFrequency<=150e6))
|
|
{
|
|
MASH=2;
|
|
}
|
|
if(TuningFrequency<100e6)
|
|
{
|
|
MASH=3;
|
|
}
|
|
|
|
printf("MASH %d Freq PLL# %d\n",MASH,PllNumber);
|
|
Originfsel=gpio_reg[GPFSEL0]; // Warning carefull if FSEL is used after !!!!!!!!!!!!!!!!!!!!
|
|
if(UsePCMClk==1)
|
|
gpio_reg[GPFSEL0] = (Originfsel & ~(7 << 12)) | (4 << 12); //ENABLE CLOCK ON GPIO CLK
|
|
|
|
|
|
// ------------------- 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
|
|
|
|
#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++)
|
|
{
|
|
|
|
|
|
//@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))
|
|
|
|
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 + ((float)arctan2(I,Q)) * M_PI/180.0f;
|
|
double dp = phase - prev_phase;
|
|
if(dp < 0) dp = dp + 2*M_PI;
|
|
|
|
*Frequency = dp*SampleRate/(2.0f*M_PI);
|
|
prev_phase = phase;
|
|
//printf("I=%d Q=%d Amp=%d Freq=%d\n",I,Q,*Amp,*Frequency);
|
|
}
|
|
|
|
|
|
|
|
|
|
inline void shuffle_int(uint32_t list[], size_t 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--;
|
|
}
|
|
}
|
|
|
|
inline void FrequencyAmplitudeToRegister(double TuneFrequency,uint32_t Amplitude,int NoSample,uint32_t WaitNanoSecond,uint32_t SampleRate,char NoUsePWMF,int debug)
|
|
{
|
|
static char ShowInfo=1;
|
|
|
|
static uint32_t CompteurDebug=0;
|
|
#define DEBUG_RATE 20000
|
|
int PwmNumberStep;
|
|
CompteurDebug++;
|
|
static uint32_t TabPwmAmplitude[18]={0x00000000,
|
|
0x80000000,0xA0000000,0xA8000000,0xAA000000,
|
|
0xAA800000,0xAAA00000,0xAAA80000,0xAAAA0000,
|
|
0xAAAA8000,0xAAAAA000,0xAAAAA800,0xAAAAAA00,
|
|
0xAAAAAA80,0xAAAAAAA0,0xAAAAAAA8,0xAAAAAAAA,0xAAAAAAAA};
|
|
|
|
|
|
|
|
static char First=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;
|
|
|
|
static dma_cb_t *cbpwrite;
|
|
uint32_t TimeRemaining=0;
|
|
|
|
int SkipFrequency=0; //When WaitNano is too short, we don't set the frequency
|
|
|
|
// WITH DMA_CTL WITHOUT BCM2708_DMA_WAIT_RESP
|
|
// Time = NBStep * 157 ns + 1360 ns
|
|
|
|
|
|
|
|
|
|
if(WaitNanoSecond==0)
|
|
{
|
|
if(SampleRate!=0)
|
|
WaitNanoSecond = (1e9/SampleRate);
|
|
}
|
|
|
|
PwmNumberStep=WaitNanoSecond/FREQ_MINI_TIMING;
|
|
if(PwmNumberStep>PWM_STEP_MAXI) PwmNumberStep=PWM_STEP_MAXI;
|
|
|
|
|
|
// ********************************** PWM FREQUENCY PROCESSING *****************************
|
|
|
|
if(UsePCMClk==0)
|
|
TuneFrequency*=2.0; //Because of pattern 10
|
|
|
|
// 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 FreqTuningUp=PllUsed/(FreqDividerf2+(double)FreqFractionnalf2/4096.0);
|
|
|
|
double FreqStep=f2-f1;
|
|
static uint32_t RegisterF1;
|
|
static uint32_t RegisterF2;
|
|
|
|
if(ShowInfo==1)
|
|
{
|
|
printf("WaitNano=%d F1=%f TuneFrequency %f F2=%f Initial Resolution(Hz)=%f ResolutionPWMF %f NbStep=%d DELAYStep=%d\n",WaitNanoSecond,f1,TuneFrequency,f2,FreqStep,FreqStep/(PwmNumberStep),PwmNumberStep,(PWMF_MARGIN+FREQ_DELAY_TIME)/FREQ_MINI_TIMING);
|
|
ShowInfo=0;
|
|
}
|
|
|
|
static int DebugStep=71;
|
|
double fPWMFrequency=((FreqTuningUp-TuneFrequency)*1.0*(double)(PwmNumberStep)/FreqStep); // Give NbStep of F2
|
|
int PWMFrequency=round(fPWMFrequency);
|
|
|
|
//printf("PWMF =%d PWMSTEP=%d\n",PWMFrequency,PwmNumberStep);
|
|
/*if((CompteurDebug%DEBUG_RATE)==0)
|
|
{
|
|
DebugStep=(DebugStep+1)%PwmNumberStep;
|
|
//printf("PwmNumberStep %d Step %d\n",PwmNumberStep,DebugStep);
|
|
}
|
|
PWMFrequency=(DebugStep);*/
|
|
|
|
//if((CompteurDebug%200)==0) printf("PwmNumberStep =%d TuneFrequency %f : FreqTuning %f FreqStep %f PwmFreqStep %f fPWMFrequency %f PWMFrequency %d f1 %f f2 %f %x %x\n",PwmNumberStep,TuneFrequency,FreqTuning,FreqStep,FreqStep/PwmNumberStep,fPWMFrequency,PWMFrequency,f1,f2,RegisterF1,RegisterF2);
|
|
|
|
int i;
|
|
|
|
static int NbF1,NbF2,NbF1F2;
|
|
NbF1=0;
|
|
NbF2=0;
|
|
NbF1F2=0;
|
|
|
|
|
|
int AdaptPWMFrequency;
|
|
if((PwmNumberStep-PWMFrequency-(PWMF_MARGIN+FREQ_DELAY_TIME)/FREQ_MINI_TIMING)>PwmNumberStep/2)
|
|
{
|
|
RegisterF1=0x5A000000 | (FreqDividerf1<<12) | (FreqFractionnalf1);
|
|
RegisterF2=0x5A000000 | (FreqDividerf2<<12) | (FreqFractionnalf2);
|
|
AdaptPWMFrequency=PWMFrequency;
|
|
NbF1=0;
|
|
NbF2=(PWMF_MARGIN+FREQ_DELAY_TIME)/FREQ_MINI_TIMING;
|
|
|
|
}
|
|
else // SWAP F1 AND F2
|
|
{
|
|
//if((CompteurDebug%DEBUG_RATE)==0) printf("-");
|
|
RegisterF2=0x5A000000 | (FreqDividerf1<<12) | (FreqFractionnalf1);
|
|
RegisterF1=0x5A000000 | (FreqDividerf2<<12) | (FreqFractionnalf2);
|
|
AdaptPWMFrequency=PwmNumberStep-PWMFrequency;
|
|
NbF1=0;
|
|
NbF2=(PWMF_MARGIN+FREQ_DELAY_TIME)/FREQ_MINI_TIMING;
|
|
}
|
|
|
|
i=0;
|
|
NbF1F2=NbF1+NbF2;
|
|
|
|
if(NoUsePWMF==1)
|
|
{
|
|
RegisterF1=0x5A000000 | (FreqDividerf1<<12) | (FreqFractionnalf1);
|
|
RegisterF2=0x5A000000 | (FreqDividerf2<<12) | (FreqFractionnalf2);
|
|
i=0;
|
|
ctl->sample[NoSample].FrequencyTab[i++]=RegisterF2;
|
|
}
|
|
else
|
|
{
|
|
while(NbF1F2<PwmNumberStep-1)
|
|
{
|
|
if(NbF1<AdaptPWMFrequency)
|
|
{
|
|
ctl->sample[NoSample].FrequencyTab[i++]=RegisterF1;
|
|
NbF1++;
|
|
NbF1F2++;
|
|
}
|
|
if(NbF2<PwmNumberStep-AdaptPWMFrequency-1)
|
|
{
|
|
ctl->sample[NoSample].FrequencyTab[i++]=RegisterF2;
|
|
NbF2++;
|
|
NbF1F2++;
|
|
}
|
|
}
|
|
if (Randomize)
|
|
shuffle_int(ctl->sample[NoSample].FrequencyTab,i);
|
|
|
|
//SHould finished by F2
|
|
ctl->sample[NoSample].FrequencyTab[i++]=RegisterF2;
|
|
NbF2++;
|
|
NbF1F2++;
|
|
}
|
|
|
|
cbpwrite=cbp+2;
|
|
cbpwrite->length=i*4;
|
|
|
|
|
|
|
|
// ****************************** AMPLITUDE PROCESSING **********************************************
|
|
|
|
|
|
Amplitude=(Amplitude>32767)?32767:Amplitude;
|
|
int IntAmplitude=(Amplitude*7.0)/32767.0; // Convert to 8 amplitude step
|
|
|
|
if(UsePCMClk==0)
|
|
{
|
|
if(IntAmplitude==0)
|
|
{
|
|
ctl->sample[NoSample].Amplitude2=0x0;
|
|
}
|
|
else
|
|
{
|
|
ctl->sample[NoSample].Amplitude2=0xAAAAAAAA;
|
|
}
|
|
}
|
|
if(UsePCMClk==1)
|
|
{
|
|
if(IntAmplitude==0)
|
|
{
|
|
ctl->sample[NoSample].Amplitude2=(Originfsel & ~(7 << 12)) | (0 << 12);
|
|
}
|
|
else
|
|
{
|
|
ctl->sample[NoSample].Amplitude2=(Originfsel & ~(7 << 12)) | (4 << 12);
|
|
}
|
|
}
|
|
|
|
static int OldIntAmplitude=0;
|
|
|
|
|
|
if(IntAmplitude>7) IntAmplitude=7;
|
|
|
|
ctl->sample[NoSample].Amplitude1=0x5a000000 + (IntAmplitude&0x7) + (1<<4) + (0<<3);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
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(5000); //Wait to be sure DMA is running stable
|
|
int i;
|
|
int SumDelay=0;
|
|
for(i=0;i<10;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.6);
|
|
|
|
|
|
|
|
time_difference = gettime_now.tv_nsec - start_time;
|
|
if(time_difference<0) time_difference+=1E9;
|
|
|
|
//printf("Delay = %d\n",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/10;
|
|
}
|
|
|
|
int CalibrateSystem(int *ppm,int *BaseDelayDMA,int *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;
|
|
|
|
if (status != TIME_OK)
|
|
{
|
|
printf("Error: NTP\n");
|
|
return 0;
|
|
}
|
|
clockppm = (double)ntx.freq/(double)(1 << 16);
|
|
if(abs(clockppm)<200)
|
|
*ppm=clockppm;
|
|
//printf("Clock PPM = %f\n",ppm);
|
|
int i;
|
|
int BaseDelay=0;
|
|
BaseDelay=GetDMADelay(0);
|
|
|
|
*BaseDelayDMA=BaseDelay;
|
|
*StepDelayDMA=(GetDMADelay(PWM_STEP_MAXI/2)-(*BaseDelayDMA))/(PWM_STEP_MAXI/2);
|
|
//for(i=1;i<200;i+=10)
|
|
//printf("Step %d =%d\n",i,(GetDMADelay(i)-BaseDelay)/i);
|
|
return 1;
|
|
}
|
|
|
|
int pitx_init(int SampleRate, double TuningFrequency, int* skipSignals,int SetDma)
|
|
{
|
|
InitGpio();
|
|
InitDma(terminate, skipSignals);
|
|
if(SetDma) DMA_CHANNEL=SetDma;
|
|
SetupGpioClock(SampleRate,TuningFrequency);
|
|
//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=%d DMA %dns:%dns\n",globalppmpll,FREQ_MINI_TIMING,PWMF_MARGIN);
|
|
//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 = %d\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/4.0) break;
|
|
}
|
|
HarmonicNumber=harmonic;
|
|
|
|
//HarmonicNumber=11; //TEST
|
|
|
|
if(HarmonicNumber>1) //Use Harmonic
|
|
{
|
|
GlobalTuningFrequency=Frequency/HarmonicNumber;
|
|
printf("\n Warning : Using harmonic %d\n",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:ra:");
|
|
|
|
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);
|
|
if(UsePCMClk==1) printf("Use GPCLK Pin instead of PWM\n");
|
|
break;
|
|
case 'w': // No use pwmfrequency
|
|
NoUsePwmFrequency = atoi(optarg);
|
|
|
|
break;
|
|
case 'r': // Randomize PWM frequency
|
|
Randomize=1;
|
|
|
|
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);
|
|
}
|
|
}
|
|
|
|
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) running on ",PROGRAM_VERSION,__DATE__);
|
|
|
|
// Init Plls Frequency using ppm (or default)
|
|
if(ppmpll!=0) ppmpll=(float)globalppmpll; // Use calibrate only if not setting by user
|
|
PllFreq500MHZ=PLL_FREQ_500MHZ;
|
|
PllFreq500MHZ+=PllFreq500MHZ * (ppmpll / 1000000.0);
|
|
|
|
PllFreq1GHZ=PLL_FREQ_1GHZ;
|
|
PllFreq1GHZ+=PllFreq1GHZ * (ppmpll / 1000000.0);
|
|
|
|
PllFreq19MHZ=PLLFREQ_192;
|
|
PllFreq19MHZ+=PllFreq19MHZ * (ppmpll / 1000000.0);
|
|
|
|
//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)
|
|
{
|
|
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, skipSignals,SetDma);
|
|
|
|
|
|
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;
|
|
|
|
// -----------------------------------------------------------------
|
|
|
|
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=100; //Max 100KHZ
|
|
}
|
|
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>=(2200+KERNEL_GRANULARITY)) // 2ms : Time to process File/Canal Coding
|
|
{
|
|
udelay(TimeToSleep-(2200+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=-1000; //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);
|
|
|
|
// 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
|
|
FrequencyAmplitudeToRegister((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++;
|
|
NbRead=readWrapper(IQFloatArray,DmaSampleBurstSize*2*sizeof(float));
|
|
|
|
if(NbRead!=DmaSampleBurstSize*2*sizeof(float))
|
|
{
|
|
if(loop_mode_flag==1)
|
|
{
|
|
printf("Looping FileIn\n");
|
|
reset();
|
|
}
|
|
else if (!useStdin) {
|
|
stop_dma();
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
for(i=0;i<DmaSampleBurstSize;i++)
|
|
{
|
|
//static float samplerate=48000;
|
|
static int amp;
|
|
static double df;
|
|
|
|
int CorrectionRpiFrequency=-1000; //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);
|
|
|
|
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;
|
|
FrequencyAmplitudeToRegister((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)
|
|
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)
|
|
{
|
|
if(SampleRf.Frequency==0.0)
|
|
{
|
|
amp=0;
|
|
SampleRf.Frequency=00.0;// TODO change that ugly frequency
|
|
}
|
|
else
|
|
amp=32767;
|
|
FrequencyAmplitudeToRegister((SampleRf.Frequency/HarmonicNumber+GlobalTuningFrequency)/HarmonicNumber,amp,last_sample++,WaitSample,0,NoUsePwmFrequency,debug);
|
|
}
|
|
if(Mode==MODE_RFA)
|
|
FrequencyAmplitudeToRegister((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);
|
|
for(i=0;i<DmaSampleBurstSize;i++)
|
|
{
|
|
//To be fine tuned !!!!
|
|
static int OutputPower=32767;
|
|
CompteSample++;
|
|
debug=1;//(debug+1)%2;
|
|
//OutputPower=(CompteSample/10)%32768;
|
|
|
|
FrequencyAmplitudeToRegister(GlobalTuningFrequency/HarmonicNumber/*+(CompteSample*0.1)*/,OutputPower,last_sample++,25000,0,NoUsePwmFrequency,debug);
|
|
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);
|
|
}
|
|
|