kopia lustrzana https://github.com/F5OEO/rpidatv
2035 wiersze
57 KiB
C
2035 wiersze
57 KiB
C
/*
|
|
|
|
<rpidatv is a software which use the GPIO of Raspberry Pi to transmit Digital Television over 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
|
|
|
|
Thanks to Brian Jordan G4EWJ for Channel modulation implementation (dvbsenco)
|
|
*/
|
|
|
|
|
|
#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 "fec100.h"
|
|
#include "mailbox.h"
|
|
#include <getopt.h>
|
|
#include <termios.h> //Used for UART
|
|
#include "rpigpio.h"
|
|
#include "rpidma.h"
|
|
#include <pthread.h>
|
|
|
|
#include <sys/prctl.h>
|
|
#include <sys/timex.h>
|
|
#include <sys/ioctl.h>
|
|
|
|
extern void dvbsenco_init (void) ;
|
|
extern uchar* dvbsenco (uchar*) ;
|
|
|
|
extern void energy (uchar* input,uchar *output) ;
|
|
extern void reed (uchar *input188) ;
|
|
extern uchar* interleave (uchar* packetin) ;
|
|
|
|
#define PROGRAM_VERSION "2.0.0"
|
|
|
|
//Minimum Time in us to sleep
|
|
#define KERNEL_GRANULARITY 10000
|
|
|
|
#define SCHED_PRIORITY 30 //Linux scheduler priority. Higher = more realtime
|
|
|
|
#define WITH_MEMORY_BUFFER
|
|
|
|
|
|
|
|
|
|
#define PLLFREQ_PCM 1000000000 // PLLD is running at 500MHz
|
|
#define PLL_PCM 0x6
|
|
|
|
//#define PLLFREQ_PWM 1000000000 //PLLC = 1GHZ , 1.2GHZ ON PIZERO ! But Unstable -> Go back to PLL_D
|
|
//#define PLL_PWM 0x5
|
|
|
|
#define PLLFREQ_PWM 1000000000 //PLLC = 1GHZ , 1.2GHZ ON PIZERO ! But Unstable -> Go back to PLL_D
|
|
#define PLL_PWM 0x6
|
|
|
|
#define PLLFREQ_192 19200000 //PLLA = 19.2MHZ
|
|
#define PLL_192 0x1
|
|
|
|
#define CARRIERFREQ 100000000 // Carrier frequency is 100MHz
|
|
|
|
|
|
//F5OEO Variable
|
|
uint32_t TabIQ[4]={0xCCCCCCCC,0x66666666,0x99999999,0x33333333};//0,-pi/2,pi/2,pi
|
|
uint32_t TabIQTest[4]={0xCCCCCCCC,0xCCCCCCCC,0xCCCCCCCC,0xCCCCCCCC};//0,-pi/2,pi/2,pi
|
|
uint32_t TabIQTestI[4]={0x00110011,0x11111111,0x11111111,0x11111111};
|
|
uint32_t TabIQTestQ[4]={0x01010101,0x00000000,0x00000000,0x00000000};
|
|
int PinOutput[2]={18,19}; //Output signal I/Q on GPIO pin number
|
|
|
|
//uint32_t TabIQ[4]={0x00000000,0xFFFFFFFF,0x00000000,0xFFFFFFFF};
|
|
int SymbolRate=0;
|
|
int FEC=1;
|
|
double TuneFrequency=62500000;
|
|
unsigned char FreqDivider=2;
|
|
//End F5OEO
|
|
|
|
|
|
int uart0_filestream = -1; // Handle to Serial Port for Digithin
|
|
char DigithinCommand[]="*A";
|
|
|
|
pthread_t th1; // Thread filling BigBUffer
|
|
char EndOfApp=0;
|
|
unsigned char Loop=0;
|
|
char *FileName;
|
|
int fdts; //Handle in Transport Stream File
|
|
static void
|
|
udelay(int us)
|
|
{
|
|
struct timespec ts = { 0, us * 1000 };
|
|
|
|
nanosleep(&ts, NULL);
|
|
}
|
|
|
|
static void
|
|
terminate(int dummy)
|
|
{
|
|
#ifdef WITH_MEMORY_BUFFER
|
|
//pthread_cancel(th1);
|
|
EndOfApp=1;
|
|
pthread_join(th1, NULL);
|
|
#endif
|
|
close(fdts);
|
|
// SET PTT OFF
|
|
|
|
gpio_reg[0x28/4]=1<<21; // Set PTT OFF
|
|
|
|
if (dma_reg)
|
|
{
|
|
dma_reg[DMA_CS+DMA_CHANNEL*0x40] = BCM2708_DMA_INT | BCM2708_DMA_END;
|
|
udelay(100);
|
|
dma_reg[DMA_CS+DMA_CHANNEL*0x40] = BCM2708_DMA_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 - In case used by digilite
|
|
//clk_reg[PWMCLK_CNTL] = 0x5A000006 | (0 << 9) ;
|
|
clk_reg[PWMCLK_CNTL] = 0x5A000000|PLL_PWM;
|
|
udelay(500);
|
|
clk_reg[PCMCLK_CNTL] = 0x5A000000|PLL_PWM;
|
|
udelay(500);
|
|
//printf("Resetpcm Done\n");
|
|
pwm_reg[PWM_DMAC] = 0;
|
|
udelay(100);
|
|
pwm_reg[PWM_CTL] = PWMCTL_CLRF;
|
|
udelay(100);
|
|
pwm_reg[PWM_FIFO]=0L;
|
|
//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");
|
|
}
|
|
|
|
//munmap(virtbase,NUM_PAGES * PAGE_SIZE);
|
|
printf("END OF RPIDATV\n");
|
|
exit(1);
|
|
}
|
|
|
|
static void
|
|
fatal(char *fmt, ...)
|
|
{
|
|
va_list ap;
|
|
|
|
va_start(ap, fmt);
|
|
vfprintf(stderr, fmt, ap);
|
|
va_end(ap);
|
|
terminate(0);
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#define DATA_FILE_SIZE 4080 // Not used anymore
|
|
|
|
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;
|
|
sp.sched_priority=priority;
|
|
int ret;
|
|
if ((ret = pthread_setschedparam(pthread_self(), SCHED_RR, &sp))) {
|
|
printf("Warning: pthread_setschedparam (increase thread priority) returned non-zero: %i\n", ret);
|
|
}
|
|
/*
|
|
if ((ret = pthread_setschedparam(th1, SCHED_RR, &sp))) {
|
|
printf("Warning: pthread_setschedparam (increase thread priority) returned non-zero: %i\n", ret);
|
|
}
|
|
*/
|
|
}
|
|
|
|
/*
|
|
void shift(char* window, int len) {
|
|
int i;
|
|
for (i = 1; i < len; ++i) {
|
|
window[i-1] = window[i];
|
|
}
|
|
window[len-1] = 0;
|
|
}
|
|
|
|
*/
|
|
void SetUglyFrequency(double Frequency)
|
|
{
|
|
int harmonic;
|
|
double FreqFound;
|
|
uint16_t FreqFractionnal=0;
|
|
|
|
|
|
for(harmonic=1;(harmonic<41);harmonic+=2)
|
|
{
|
|
//printf("->%lf harmonic %d\n",(Frequency/(double)harmonic),harmonic);
|
|
if((Frequency/(double)harmonic)<=(double)PLLFREQ_PWM/8.0) break;
|
|
}
|
|
|
|
harmonic-=2;
|
|
//do
|
|
{
|
|
harmonic+=2;
|
|
FreqFound = (double) (Frequency*4.0/(double)harmonic);
|
|
FreqDivider=(int) ((double)PLLFREQ_PWM/FreqFound);
|
|
FreqFractionnal=4096.0 * (((double)PLLFREQ_PWM/FreqFound)-FreqDivider);
|
|
//printf("Ecart = %lf\n", (PLLFREQ/(double)FreqDivider)-(PLLFREQ/(double)(FreqDivider+1.0)));
|
|
}
|
|
//while((PLLFREQ/(double)FreqDivider)-(PLLFREQ/(double)(FreqDivider+1.0))>25e6); // To Avoir 25MHZ of MASH : seems not a limit, removed
|
|
clk_reg[PWMCLK_DIV] = 0x5A000000 | (FreqDivider<<12) | FreqFractionnal;
|
|
printf("Tuning on %lf MHZ (harmonic %d):DIV%d/FRAC%d\n",1e-6*(double)PLLFREQ_PWM*(double)harmonic/(4.0*(FreqDivider+(double) FreqFractionnal/4096.0)),harmonic,FreqDivider,FreqFractionnal);
|
|
|
|
}
|
|
//************************************ INIT MODE UGLY ***********************************************
|
|
|
|
int InitUgly()
|
|
{
|
|
char MASH=1;
|
|
|
|
// SET PTT
|
|
gpioSetMode(21,1); // GPIO 21 - PIN 40 is output for PTT
|
|
gpio_reg[0x1C/4]=1<<21; // Set PTT ON
|
|
|
|
SetUglyFrequency(TuneFrequency);
|
|
//gpioSetMode(18, 2); /* set to ALT5, PWM1 : RF */
|
|
if(PinOutput[0]==18) {gpioSetMode(18, 2);printf("\n Using GPIO 18\n");}; //ALT 5
|
|
if(PinOutput[0]==12) {gpioSetMode(12, 4);printf("\n Using GPIO 12\n");} //ALT 0
|
|
if(PinOutput[0]==40) gpioSetMode(40, 4); //ALT 0
|
|
|
|
pwm_reg[PWM_CTL] = 0;
|
|
//ALWAYS USE MASH
|
|
{
|
|
//printf("MASH ENABLE\n");
|
|
clk_reg[PWMCLK_CNTL] = 0x5A000000 | (MASH << 9)|PLL_PWM ; //1<<9
|
|
//clk_reg[PWMCLK_CNTL] = 0x5A000005 | (1 << 9) ;
|
|
}
|
|
|
|
udelay(300);
|
|
|
|
|
|
clk_reg[PWMCLK_CNTL]= 0x5A000010 | (MASH << 9) | PLL_PWM;
|
|
|
|
pwm_reg[PWM_RNG1] = 32;// 32 Mandatory for Serial Mode without gap
|
|
udelay(100);
|
|
pwm_reg[PWM_RNG2] = 32;// 32 Mandatory for Serial Mode without gap
|
|
udelay(100);
|
|
pwm_reg[PWM_DMAC] = PWMDMAC_ENAB | PWMDMAC_THRSHLD;
|
|
udelay(100);
|
|
pwm_reg[PWM_CTL] = PWMCTL_CLRF;
|
|
udelay(100);
|
|
|
|
//------------------- Init PCM ------------------
|
|
pcm_reg[PCM_CS_A] = 1; // Disable Rx+Tx, Enable PCM block
|
|
udelay(100);
|
|
clk_reg[PCMCLK_CNTL] = 0x5A000000|PLL_PWM; // Source=PLLD (500MHz) //Seems Both should be on same PLL
|
|
udelay(1000);
|
|
|
|
int NbStep;
|
|
int prescale=2;
|
|
if(SymbolRate>=250)
|
|
{
|
|
clk_reg[PCMCLK_DIV] = 0x5A000000 | (8<<12); // Set pcm div to 2, giving 250MHz step
|
|
NbStep= PLLFREQ_PCM/(8*SymbolRate*1000) -1;
|
|
}
|
|
else
|
|
{
|
|
prescale=4*(250/SymbolRate);
|
|
clk_reg[PCMCLK_DIV] = 0x5A000000 | ((prescale*2*2)<<12); // Set pcm div to 2, giving 250MHz step
|
|
NbStep= PLLFREQ_PCM/(4*prescale*SymbolRate*1000) -1;
|
|
printf("Low SymbolRate\n");
|
|
}
|
|
|
|
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",NbStep);
|
|
pcm_reg[PCM_MODE_A] = NbStep<<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 |PLL_PWM; // Source=PLLD and enable
|
|
|
|
|
|
printf("RealSR= %lu Symbol/s \n",PLLFREQ_PCM/((NbStep+1)*4L*prescale));
|
|
|
|
// ========================== INIT DMA ================================================
|
|
ctl = (struct control_data_s *)virtbase;
|
|
dma_cb_t *cbp = ctl->cb;
|
|
|
|
|
|
|
|
|
|
uint32_t phys_pwm_fifo_addr = 0x7e20c000 + 0x18;//PWM
|
|
uint32_t phys_fifo_addr = (0x00203000 | 0x7e000000) + 0x04; //PCM
|
|
//uint32_t dummy_gpio = 0x7e20b000;
|
|
int samplecnt;
|
|
|
|
for (samplecnt = 0; samplecnt < NUM_SAMPLES; samplecnt++) {
|
|
|
|
|
|
// Write a frequency sample
|
|
|
|
cbp->info = BCM2708_DMA_NO_WIDE_BURSTS /* BCM2708_DMA_WAIT_RESP |BCM2708_DMA_D_DREQ | BCM2708_DMA_PER_MAP(5)*/;
|
|
cbp->src = mem_virt_to_phys(ctl->sample + samplecnt);
|
|
cbp->dst = phys_pwm_fifo_addr;
|
|
cbp->length = 4;
|
|
cbp->stride = 0;
|
|
cbp->next = mem_virt_to_phys(cbp + 1);
|
|
//printf("cbp : sample %x src %x dest %x next %x\n",ctl->sample + i,cbp->src,cbp->dst,cbp->next);
|
|
cbp++;
|
|
|
|
|
|
// Delay
|
|
|
|
cbp->info = BCM2708_DMA_SRC_IGNOR |/* BCM2708_DMA_NO_WIDE_BURSTS | BCM2708_DMA_WAIT_RESP |*/ BCM2708_DMA_D_DREQ | BCM2708_DMA_PER_MAP(2);
|
|
cbp->src = mem_virt_to_phys(virtbase);
|
|
cbp->dst = phys_fifo_addr;//Delay with PCM
|
|
cbp->length = 4;
|
|
cbp->stride = 0;
|
|
cbp->next = mem_virt_to_phys(cbp + 1);
|
|
cbp++;
|
|
|
|
|
|
}
|
|
|
|
cbp--;
|
|
cbp->next = mem_virt_to_phys(virtbase);
|
|
|
|
// ----------------------------- END DMA ------------------------------------
|
|
pwm_reg[PWM_CTL] = PWMCTL_USEF1| PWMCTL_MODE1| PWMCTL_PWEN1|PWMCTL_RPTL1 ; //PWM0
|
|
usleep(100);
|
|
pcm_reg[PCM_CS_A] |= 1<<2; //START TX PCM
|
|
|
|
return 1;
|
|
}
|
|
|
|
|
|
//************************************ INIT MODE IQ ***********************************************
|
|
|
|
int InitIQ(int DigithinMode)
|
|
{
|
|
//PIN FOR I
|
|
if(PinOutput[0]==18) gpioSetMode(18, 2); //ALT 5
|
|
if(PinOutput[0]==12) gpioSetMode(12, 4); //ALT 0
|
|
if(PinOutput[0]==40) gpioSetMode(40, 4); //ALT 0
|
|
|
|
//PIN FOR Q
|
|
if(PinOutput[1]==13) gpioSetMode(13, 4); //ALT 0
|
|
if(PinOutput[1]==19) gpioSetMode(19, 2); //ALT 5
|
|
if(PinOutput[1]==41) gpioSetMode(41, 4); //ALT 0
|
|
if(PinOutput[1]==45) gpioSetMode(45, 4); //ALT 0
|
|
|
|
/* CAM-GPIO IS ON 41 on B+ : DON'T USE*/
|
|
|
|
// REMOVE AUTODETECTION PIN FROM PI MODEL - USE PIN MAPPING
|
|
/*
|
|
if (model<3) //ONLY ON B : FOR SOLDERING on PCB near audio output, ON B+ IT CRASH CAM
|
|
{
|
|
gpioSetMode(40, 4); // set to ALT0, PWM1 DIGILITE Model B
|
|
gpioSetMode(41, 4); // set to ALT0, PWM2 DIGILITE Model B !!! CAM-GPIO IS ON 41 on B+
|
|
}
|
|
*/
|
|
//unsigned int SRClock=PLLFREQ_PCM/(SymbolRate*1000);
|
|
|
|
// SET PTT
|
|
gpioSetMode(21,1); // GPIO 21 - PIN 40 is output for PTT
|
|
gpio_reg[0x1C/4]=1<<21; // Set PTT ON
|
|
|
|
unsigned int SRClock;
|
|
//unsigned int SRClockPCM=(PLLFREQ_PCM/(SymbolRate*1000*64))*64;
|
|
|
|
//SymbolRate = PLLFREQ/(SRClockPCM*1000);
|
|
|
|
|
|
|
|
// CLK_DIGITHIN 500MHZ(PLLD)/4MHZ = 125
|
|
int CLK_DIGITHIN=PLLFREQ_PCM/4E6;
|
|
|
|
//#define CLK_4MHZ 125
|
|
if(DigithinMode==1)
|
|
{
|
|
uint32_t DigiThin_ClockBySymbol;
|
|
|
|
// GPIO4 needs to be ALT FUNC 0 to otuput the clock
|
|
gpio_reg[GPFSEL0] = (gpio_reg[GPFSEL0] & ~(7 << 12)) | (4 << 12); //ENABLE CLOCK - In case used by digilite
|
|
usleep(1000);
|
|
clk_reg[GPCLK_CNTL] = 0x5A << 24 |0<<4 | PLL_PCM;
|
|
usleep(1000);
|
|
clk_reg[GPCLK_DIV] = 0x5A << 24 | (CLK_DIGITHIN<<12) ; //CLK FREQ = 4MHZ: Fixed for Digithin
|
|
usleep(100);
|
|
DigiThin_ClockBySymbol=( PLLFREQ_PCM/(CLK_DIGITHIN));
|
|
//SRClock=DigiThin_ClockBySymbol*CLK_4MHZ;
|
|
printf("Digithin Clock at 4MHZ:%d clock by Symbol (SR=%ld)\n",DigiThin_ClockBySymbol,4000000/(SymbolRate*1000L));
|
|
udelay(500);
|
|
clk_reg[GPCLK_CNTL] = 0x5A << 24 | 0 << 9 | 1 << 4 | PLL_PCM; //NO MASH !!!
|
|
udelay(500);
|
|
|
|
|
|
uart0_filestream = open("/dev/ttyAMA0", O_RDWR | O_NOCTTY | O_NDELAY); //Open in non blocking read/write mode
|
|
if (uart0_filestream == -1)
|
|
{
|
|
//ERROR - CAN'T OPEN SERIAL PORT
|
|
printf("Error - Unable to open UART. Ensure it is not in use by another application\n");
|
|
}
|
|
|
|
//CONFIGURE THE UART
|
|
//The flags (defined in /usr/include/termios.h - see http://pubs.opengroup.org/onlinepubs/007908799/xsh/termios.h.html):
|
|
|
|
struct termios options;
|
|
tcgetattr(uart0_filestream, &options);
|
|
options.c_cflag = B57600 | CS8 | CLOCAL | CREAD; //<Set baud rate
|
|
options.c_iflag = IGNPAR;
|
|
options.c_oflag = 0;
|
|
options.c_lflag = 0;
|
|
tcflush(uart0_filestream, TCIFLUSH);
|
|
tcsetattr(uart0_filestream, TCSANOW, &options);
|
|
|
|
//GPIO7 (pin 26) is connected to the reset pin of the dsPIC, so it should always be high. It may never be necessary to reset the dsPIC, but it gives the option.
|
|
|
|
// DigiThin UART commands (CRLF not required)
|
|
// *0 reboot
|
|
// *1 test mode, no mod
|
|
// *2 test mode, LSB
|
|
// *3 test mode, USB
|
|
// *4 test mode, in phase
|
|
// *5 test mode, ALT1
|
|
// *6 test mode, ALT2
|
|
// *7 test mode, ALT4
|
|
//
|
|
// 4MHz reference clock
|
|
//
|
|
// *A running, 12 cycles per bit, nominal 333kS
|
|
// *B running, 15 cycles per bit, nominal 266kS
|
|
// *C running, 18 cycles per bit, nominal 222kS
|
|
// *D running, 21 cycles per bit, nominal 190kS
|
|
// *E running, 24 cycles per bit, nominal 166kS
|
|
// *F running, 27 cycles per bit, nominal 148kS
|
|
// *G running, 30 cycles per bit, nominal 133kS
|
|
// *H running, 33 cycles per bit, nominal 121kS
|
|
//
|
|
// *? request status
|
|
|
|
switch(SymbolRate)
|
|
{
|
|
case 333:DigithinCommand[1]='A';SymbolRate=333333;break;
|
|
case 266:DigithinCommand[1]='B';SymbolRate=266666;break;
|
|
case 222:DigithinCommand[1]='C';SymbolRate=222222;break;
|
|
case 190:DigithinCommand[1]='D';SymbolRate=190476;break;
|
|
case 166:DigithinCommand[1]='E';SymbolRate=166666;break;
|
|
case 148:DigithinCommand[1]='F';SymbolRate=148148;break;
|
|
case 133:DigithinCommand[1]='G';SymbolRate=133333;break;
|
|
case 121:DigithinCommand[1]='H';SymbolRate=121121;break;
|
|
default:DigithinCommand[1]='A';SymbolRate=333333;break;
|
|
|
|
}
|
|
SRClock=PLLFREQ_PCM/(SymbolRate);
|
|
SymbolRate=SymbolRate/1000;
|
|
|
|
}
|
|
else
|
|
{
|
|
//#define VCO_MODE
|
|
|
|
|
|
|
|
SRClock=PLLFREQ_PCM/(1000*SymbolRate);
|
|
#ifdef DIGILITE_CLOCK_MODE
|
|
printf("\n ******** DIGILITE CLOCK MODE*********** \n");
|
|
printf("SRClok=%d SYmbolRate=%dKSymb\n",SRClock,500000/SRClock);
|
|
gpio_reg[GPFSEL0] = (gpio_reg[GPFSEL0] & ~(7 << 12)) | (4 << 12); //ENABLE CLOCK - In case used by digilite
|
|
clk_reg[GPCLK_CNTL] = 0x5A << 24 |0<<4 | PLL_PCM;
|
|
udelay(2000);
|
|
//clk_reg[GPCLK_DIV] = 0x5A << 24 | ((SRClock)<<12) ; //CLK FREQ = SR for Digilite
|
|
clk_reg[GPCLK_DIV] = 0x5A << 24 | ((SRClock>>2)<<12) ; //CLK FREQ = SR for F5LGJ
|
|
udelay(500);
|
|
clk_reg[GPCLK_CNTL] = 0x5A << 24 | 1 << 9 | 1 << 4 | PLL_PCM; //NO MASH !!!
|
|
udelay(500);
|
|
#endif
|
|
#ifdef VCO_MODE
|
|
printf("\n ******** VCO CLOCK MODE*********** \n");
|
|
|
|
uint32_t FreqVCO=437000000;
|
|
uint32_t FreqDivider,FreqFractionnal;
|
|
|
|
FreqDivider=(int) ((double)PLLFREQ_PWM/FreqVCO);
|
|
FreqFractionnal=4096.0 * (((double)PLLFREQ_PWM/FreqVCO)-FreqDivider);
|
|
FreqDivider=FreqDivider%4096;
|
|
FreqFractionnal=FreqFractionnal%4096;
|
|
printf("Freq Divider %d Freq Frac %d\n",FreqDivider,FreqFractionnal);
|
|
|
|
gpio_reg[GPFSEL0] = (gpio_reg[GPFSEL0] & ~(7 << 12)) | (4 << 12); //ENABLE CLOCK - In case used by digilite
|
|
clk_reg[GPCLK_CNTL] = 0x5A << 24 |0<<4 | PLL_PWM;
|
|
udelay(2000);
|
|
clk_reg[GPCLK_DIV] = 0x5A << 24 | (FreqDivider<<12) | FreqFractionnal ; //CLK FREQ = SR for Digilite
|
|
udelay(500);
|
|
clk_reg[GPCLK_CNTL] = 0x5A << 24 | 1 << 9 | 1 << 4 | PLL_PWM; //MASH !!!
|
|
udelay(500);
|
|
#endif
|
|
|
|
}
|
|
|
|
pwm_reg[PWM_CTL] = 0;
|
|
if(SymbolRate<250)
|
|
{
|
|
SRClock=PLLFREQ_192/(1000*SymbolRate);
|
|
clk_reg[PWMCLK_CNTL] = 0x5A000000 | (0 << 9) |PLL_192 ;
|
|
udelay(300);
|
|
clk_reg[PWMCLK_DIV] = 0x5A000000 | ((SRClock)<<12); //*2: FIXME : Because SRClock is normaly based on 500Mhz not 1GH
|
|
udelay(300);
|
|
clk_reg[PWMCLK_CNTL] = 0x5A000010 | (0 << 9) | PLL_192;
|
|
printf("Real SR = %d KSymbol / Clock Divider =%d \n",PLLFREQ_192/(SRClock*1000),SRClock);
|
|
}
|
|
else
|
|
{
|
|
SRClock=PLLFREQ_PCM/(1000*SymbolRate);
|
|
clk_reg[PWMCLK_CNTL] = 0x5A000000 | (0 << 9) |PLL_PCM ;
|
|
udelay(300);
|
|
clk_reg[PWMCLK_DIV] = 0x5A000000 | ((SRClock)<<12); //*2: FIXME : Because SRClock is normaly based on 500Mhz not 1GH
|
|
udelay(300);
|
|
clk_reg[PWMCLK_CNTL] = 0x5A000010 | (0 << 9) | PLL_PCM;
|
|
printf("Real SR = %d KSymbol / Clock Divider =%d \n",PLLFREQ_PCM/(SRClock*1000),SRClock);
|
|
}
|
|
pwm_reg[PWM_RNG1] = 32;// 32 Mandatory for Serial Mode without gap
|
|
udelay(100);
|
|
pwm_reg[PWM_RNG2] = 32;// 32 Mandatory for Serial Mode without gap
|
|
|
|
pwm_reg[PWM_DMAC] = PWMDMAC_ENAB | PWMDMAC_THRSHLD;
|
|
udelay(100);
|
|
pwm_reg[PWM_CTL] = PWMCTL_CLRF;
|
|
udelay(100);
|
|
|
|
|
|
//printf("Playing File =%s at %d KSymbol FEC=%d ",argv[1],PLLFREQ_PCM/SRClock/1000,abs(FEC));
|
|
|
|
// --------------------- INIT DMA IQ ------------------------------
|
|
ctl = (struct control_data_s *)virtbase;
|
|
dma_cb_t *cbp = ctl->cb;
|
|
|
|
|
|
uint32_t phys_pwm_fifo_addr = 0x7e20c000 + 0x18;//PWM
|
|
int samplecnt;
|
|
NUM_SAMPLES = NUM_SAMPLES_MAX/2; // Minize the buffer in IQ Mode
|
|
for (samplecnt = 0; samplecnt < NUM_SAMPLES; samplecnt++) {
|
|
|
|
// Write a PWM sample
|
|
|
|
cbp->info = BCM2708_DMA_NO_WIDE_BURSTS | BCM2708_DMA_WAIT_RESP|BCM2708_DMA_D_DREQ | BCM2708_DMA_PER_MAP(5);
|
|
cbp->src = mem_virt_to_phys(ctl->sample + samplecnt);
|
|
cbp->dst = phys_pwm_fifo_addr;
|
|
cbp->length = 4;
|
|
cbp->stride = 0;
|
|
cbp->next = mem_virt_to_phys(cbp + 1);
|
|
//printf("cbp : sample %x src %x dest %x next %x\n",ctl->sample + i,cbp->src,cbp->dst,cbp->next);
|
|
cbp++;
|
|
|
|
}
|
|
cbp--;
|
|
cbp->next = mem_virt_to_phys(virtbase);
|
|
|
|
// ------------------------------ END DMA INIT ---------------------------------
|
|
pwm_reg[PWM_CTL] = PWMCTL_USEF2|PWMCTL_PWEN2|PWMCTL_MODE2|PWMCTL_USEF1| PWMCTL_MODE1| PWMCTL_PWEN1; //PWM0
|
|
return 1;
|
|
}
|
|
|
|
|
|
int InitDTX1()
|
|
{
|
|
int i;
|
|
|
|
gpioSetMode(2, 1); // D0 GPIO 2 : Header 3
|
|
gpioSetMode(3, 1); // D1 GPIO 3 : Header 5
|
|
gpioSetMode(4, 1); // D2 GPIO 4 : Header 7
|
|
gpioSetMode(14, 1); // D3 GPIO 14 : Header 8
|
|
gpioSetMode(15, 1); // D4 GPIO 15 : Header 10
|
|
gpioSetMode(17, 1); // D5 GPIO 17 : Header 11
|
|
gpioSetMode(18, 1); // D6 GPIO 18 : Header 12
|
|
gpioSetMode(27, 1); // D7 GPIO 27 : Header 13
|
|
|
|
gpioSetMode(22, 1); // CLK GPIO 22 : Header 15
|
|
//gpioSetMode(23, 1); // TPCLK GPIO 23 : Header 16
|
|
gpioSetMode(24, 1); // TPCLK GPIO 24 : Header 18
|
|
|
|
/*
|
|
for(i=0;i<40000;i++)
|
|
{
|
|
gpio_reg[0x1C/4]=1<<22;
|
|
usleep(100);
|
|
gpio_reg[0x28/4]=1<<22;
|
|
usleep(100);
|
|
}
|
|
*/
|
|
|
|
uint32_t TSRate=SymbolRate*125/*1000/8*/*2*FEC*188/(204*(FEC+1L));
|
|
//TSRate=100000;//TEST
|
|
uint32_t SRTSClock=PLLFREQ_PCM/(TSRate*2*32); //32 mais SET/CLR *2
|
|
// TEST ********************
|
|
//SRTSClock=1000/32;
|
|
// **************
|
|
printf("DTX1 : TS Rate = %lu ClockDiv=%lu\n",(unsigned long int)TSRate,(unsigned long int)SRTSClock);
|
|
|
|
pwm_reg[PWM_CTL] = 0;
|
|
clk_reg[PWMCLK_CNTL] = 0x5A000000 | (0 << 9) |PLL_PCM ;
|
|
udelay(600);
|
|
clk_reg[PWMCLK_DIV] = 0x5A000000 | ((SRTSClock)<<12); //*2: FIXME : Because SRClock is normaly based on 500Mhz not 1GH
|
|
udelay(300);
|
|
clk_reg[PWMCLK_CNTL] = 0x5A000010 | (0 << 9) | PLL_PCM;
|
|
pwm_reg[PWM_RNG1] = 32;// 32 Mandatory for Serial Mode without gap
|
|
udelay(100);
|
|
pwm_reg[PWM_RNG2] = 32;// 32 Mandatory for Serial Mode without gap
|
|
|
|
pwm_reg[PWM_DMAC] = PWMDMAC_ENAB | PWMDMAC_THRSHLD;
|
|
udelay(100);
|
|
pwm_reg[PWM_CTL] = PWMCTL_CLRF;
|
|
udelay(100);
|
|
|
|
pwm_reg[PWM_CTL] = /*PWMCTL_USEF2|PWMCTL_PWEN2|PWMCTL_MODE2|*/PWMCTL_USEF1| PWMCTL_MODE1| PWMCTL_PWEN1; //PWM
|
|
|
|
//------------------------- INIT DMA DTX1 --------------------
|
|
ctl = (struct control_data_s *)virtbase;
|
|
dma_cb_t *cbp = ctl->cb;
|
|
uint32_t phys_pwm_fifo_addr = 0x7e20c000 + 0x18;//PWM
|
|
uint32_t dummy_gpio = 0x7e20b000;
|
|
NUM_SAMPLES = NUM_SAMPLES_MAX/2; // Minize the buffer in DTX1 Mode
|
|
for (i = 0; i < NUM_SAMPLES; i++)
|
|
{
|
|
ctl->sample[i] = 0; // Silence
|
|
|
|
if((i%2)==0)
|
|
{
|
|
cbp->info = BCM2708_DMA_NO_WIDE_BURSTS | BCM2708_DMA_WAIT_RESP;
|
|
cbp->src = mem_virt_to_phys(ctl->sample + i );//Surement WORD1=CLR
|
|
cbp->dst = 0x7E200028; //CLEAR BIT
|
|
cbp->length = 4;
|
|
cbp->stride = 0;
|
|
cbp->next = mem_virt_to_phys(cbp + 1);
|
|
//printf("cbp CLEAR : sample %x src %x dest %x next %x\n",ctl->sample + i,cbp->src,cbp->dst,cbp->next);
|
|
cbp++;
|
|
|
|
/*
|
|
cbp->info = BCM2708_DMA_NO_WIDE_BURSTS | BCM2708_DMA_WAIT_RESP|BCM2708_DMA_D_DREQ | BCM2708_DMA_PER_MAP(5);
|
|
cbp->src = mem_virt_to_phys(virtbase);
|
|
cbp->dst = phys_pwm_fifo_addr;
|
|
cbp->length = 4;
|
|
cbp->stride = 0;
|
|
cbp->next = mem_virt_to_phys(cbp + 1);
|
|
//printf("cbp : sample %x src %x dest %x next %x\n",ctl->sample + i,cbp->src,cbp->dst,cbp->next);
|
|
cbp++;*/
|
|
|
|
|
|
cbp->info = BCM2708_DMA_NO_WIDE_BURSTS| BCM2708_DMA_WAIT_RESP;
|
|
cbp->src = mem_virt_to_phys(virtbase);
|
|
cbp->dst = dummy_gpio ;
|
|
cbp->length = 4;
|
|
cbp->stride = 0;
|
|
cbp->next = mem_virt_to_phys(cbp + 1);
|
|
//printf("cbp DUMMY: sample %x src %x dest %x next %x\n",ctl->sample + i,cbp->src,cbp->dst,cbp->next);
|
|
cbp++;
|
|
|
|
}
|
|
else
|
|
{
|
|
cbp->info = BCM2708_DMA_NO_WIDE_BURSTS | BCM2708_DMA_WAIT_RESP;
|
|
cbp->src = mem_virt_to_phys(ctl->sample + i );//Surement WORD1=CLR
|
|
cbp->dst = 0x7E20001C; //SET BIT
|
|
cbp->length = 4;
|
|
cbp->stride = 0;
|
|
cbp->next = mem_virt_to_phys(cbp + 1);
|
|
//printf("cbp SET: sample %x src %x dest %x next %x\n",ctl->sample + i,cbp->src,cbp->dst,cbp->next);
|
|
cbp++;
|
|
|
|
cbp->info = BCM2708_DMA_NO_WIDE_BURSTS | BCM2708_DMA_WAIT_RESP|BCM2708_DMA_D_DREQ | BCM2708_DMA_PER_MAP(5);
|
|
cbp->src = mem_virt_to_phys(virtbase);
|
|
cbp->dst = phys_pwm_fifo_addr;
|
|
cbp->length = 4;
|
|
cbp->stride = 0;
|
|
cbp->next = mem_virt_to_phys(cbp + 1);
|
|
//printf("cbp PWM: sample %x src %x dest %x next %x\n",ctl->sample + i,cbp->src,cbp->dst,cbp->next);
|
|
cbp++;
|
|
}
|
|
|
|
}
|
|
cbp--;
|
|
cbp->next = mem_virt_to_phys(virtbase);
|
|
return 1;
|
|
}
|
|
|
|
|
|
//#define BIG_BUFFER_SIZE ((int)((NUM_SAMPLES*4*1.5)/188)*188)
|
|
#define BIG_BUFFER_SIZE (18800*8*2)
|
|
#define BURST_MEM_SIZE (188)
|
|
typedef struct circular_buffer
|
|
{
|
|
unsigned char *buffer;
|
|
volatile unsigned int head;
|
|
volatile unsigned int tail;
|
|
pthread_mutex_t lock;
|
|
}ring_buffer;
|
|
|
|
ring_buffer my_circular_buffer;
|
|
|
|
|
|
int BufferAvailable()
|
|
{
|
|
int Available= my_circular_buffer.head-my_circular_buffer.tail;
|
|
if(Available<0) Available+=BIG_BUFFER_SIZE;
|
|
return Available;
|
|
}
|
|
|
|
void store_in_buffer(unsigned char data)
|
|
{
|
|
|
|
while(((unsigned int)(my_circular_buffer.head + 1) % BIG_BUFFER_SIZE)==my_circular_buffer.tail)
|
|
usleep(50000);
|
|
|
|
unsigned int next = (unsigned long)(my_circular_buffer.head + 1) % BIG_BUFFER_SIZE;
|
|
|
|
my_circular_buffer.buffer[my_circular_buffer.head] = data;
|
|
my_circular_buffer.head = next;
|
|
|
|
|
|
}
|
|
|
|
void store_in_buffer_1880(unsigned char *data)
|
|
{
|
|
|
|
while(((unsigned int)(my_circular_buffer.head + BURST_MEM_SIZE) % BIG_BUFFER_SIZE)==my_circular_buffer.tail)
|
|
{
|
|
//printf("Bigbuffer plein\n");
|
|
usleep(50000);
|
|
}
|
|
pthread_mutex_lock(&my_circular_buffer.lock);
|
|
|
|
memcpy(my_circular_buffer.buffer+my_circular_buffer.head,data,BURST_MEM_SIZE);
|
|
unsigned int next = (unsigned long)(my_circular_buffer.head + BURST_MEM_SIZE) % BIG_BUFFER_SIZE;
|
|
my_circular_buffer.head = next;
|
|
pthread_mutex_unlock(&my_circular_buffer.lock);
|
|
|
|
}
|
|
|
|
char read_from_buffer()
|
|
{
|
|
//printf("#\n");
|
|
// if the head isn't ahead of the tail, we don't have any characters
|
|
|
|
while (my_circular_buffer.head == my_circular_buffer.tail)
|
|
{
|
|
sleep(0);//sched_yield();
|
|
}
|
|
|
|
char data = my_circular_buffer.buffer[my_circular_buffer.tail];
|
|
my_circular_buffer.tail = (unsigned int)(my_circular_buffer.tail + 1) % BIG_BUFFER_SIZE;
|
|
|
|
return data;
|
|
|
|
}
|
|
|
|
void read_from_buffer_188(unsigned char *Dest)
|
|
{
|
|
while(BufferAvailable()<188) {usleep(5000);printf("B");} // Carefull of deadlock ! BE sure to have available
|
|
pthread_mutex_lock(&my_circular_buffer.lock);
|
|
memcpy(Dest,my_circular_buffer.buffer+my_circular_buffer.tail,188);
|
|
my_circular_buffer.tail = (unsigned int)(my_circular_buffer.tail + 188) % BIG_BUFFER_SIZE;
|
|
pthread_mutex_unlock(&my_circular_buffer.lock);
|
|
}
|
|
|
|
void *FillBigBuffer (void * arg)
|
|
{
|
|
|
|
char name[16];
|
|
static uchar buff188[BURST_MEM_SIZE];
|
|
static int ByteRead=0;
|
|
static int TotalByteRead=0;
|
|
static int NbWrite=0;
|
|
memset(name, '\0', sizeof(name));
|
|
strcpy(name,"BIGBUFFER");
|
|
//pthread_setname_np(pthread_self(), name);
|
|
prctl(PR_SET_NAME, name, 0, 0, 0);
|
|
pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
|
|
pthread_setcanceltype (PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
|
|
struct stat bufstat;
|
|
int ret;
|
|
ret=fstat(fdts,&bufstat);
|
|
if(S_ISFIFO(bufstat.st_mode))
|
|
printf("Using a Pipe\n");
|
|
else
|
|
printf("Using a File %d %d\n",ret,bufstat.st_mode);
|
|
while(EndOfApp==0)
|
|
{
|
|
TotalByteRead=0;
|
|
//usleep(200);
|
|
do
|
|
{
|
|
int n;
|
|
if(S_ISFIFO(bufstat.st_mode))
|
|
{
|
|
do
|
|
{
|
|
ioctl(fdts, FIONREAD, &n);
|
|
if(n<(BURST_MEM_SIZE)) {usleep(10000);}
|
|
}
|
|
while(n<(BURST_MEM_SIZE));
|
|
}
|
|
ByteRead=read(fdts,buff188+TotalByteRead,BURST_MEM_SIZE-TotalByteRead);
|
|
|
|
if(ByteRead<=0)
|
|
{
|
|
if(Loop==1)
|
|
{
|
|
close(fdts);
|
|
fdts = open(FileName, 'r');
|
|
TotalByteRead=0;
|
|
printf("Loop %s\n",FileName);
|
|
}
|
|
else
|
|
{
|
|
printf("Filling BUffer : EOF\n");
|
|
//terminate(0);
|
|
close(fdts);
|
|
return 0; // END OF FILE
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if(ByteRead!=(BURST_MEM_SIZE-TotalByteRead))
|
|
{
|
|
//usleep(25000);
|
|
|
|
}
|
|
TotalByteRead+=ByteRead;
|
|
|
|
//usleep(2000);
|
|
}
|
|
|
|
}
|
|
while (TotalByteRead<BURST_MEM_SIZE); // Read should be around 20us
|
|
|
|
if(((BIG_BUFFER_SIZE-BufferAvailable())<=BIG_BUFFER_SIZE/8))
|
|
{
|
|
unsigned char Dummy[188];
|
|
|
|
if(S_ISFIFO(bufstat.st_mode))
|
|
{
|
|
printf("PIdatv ov=%d/%d\n",BufferAvailable(),BIG_BUFFER_SIZE);
|
|
while(BufferAvailable()>BIG_BUFFER_SIZE/8)
|
|
read_from_buffer_188(Dummy);
|
|
}
|
|
//usleep(1000);
|
|
|
|
}
|
|
|
|
//printf("Lock BigBuffer\n");
|
|
|
|
|
|
store_in_buffer_1880(buff188);
|
|
|
|
//printf("#");
|
|
/*
|
|
for(NbWrite=0;NbWrite<1880;NbWrite++)
|
|
{
|
|
store_in_buffer(buff188[NbWrite]);
|
|
}
|
|
*/
|
|
|
|
|
|
// printf("UNLock BigBuffer\n");
|
|
|
|
}
|
|
pthread_exit (0);
|
|
}
|
|
|
|
int CalibrateSystem()
|
|
{
|
|
struct timex ntx;
|
|
int ppm;
|
|
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("Warning: NTP\n");
|
|
return 0;
|
|
}
|
|
clockppm = (double)ntx.freq/(double)(1 << 16);
|
|
if(abs(clockppm)<200)
|
|
ppm=clockppm;
|
|
printf("Clock PPM = %d\n",ppm);
|
|
return ppm;
|
|
}
|
|
|
|
void print_usage()
|
|
{
|
|
|
|
fprintf(stderr,\
|
|
"\nrpidatv -%s\n\
|
|
Usage:\nrpidatv -i File Input -s Symbolrate -c Fec [-o OutputMode] [-f frequency output] [-l] [-p Power] [-h] \n\
|
|
-i path to Transport File Input \n\
|
|
-s SymbolRate in KS (125-4000) \n\
|
|
-c Fec : 1/2 or 3/4 or 5/6 or 7/8 \n\
|
|
-m OutputMode\n\
|
|
{RF(Modulate QSK in RF need -f option to set frequency)}\n\
|
|
{IQ(Output QPSK I/Q}\n\
|
|
{PARALLEL(Output parallel (DTX1,MINIMOD..)}\n\
|
|
{IQWITHCLK(Output I/Q with CLK (F5LGJ)}\n\
|
|
{DIGITHIN (Output I/Q for Digithin)}\n\
|
|
-f Frequency to output in RF Mode in MHZ\n\
|
|
-l loop file input\n\
|
|
-p Power on output 1..7\n\
|
|
-x GPIO Pin output for I or RF {12,18,40}\n\
|
|
-y GPIO Pin output for Q {13,19,41,45}\n\
|
|
-h help (print this help).\n\
|
|
Example : sudo ./rpidatv -i sample.ts -s 250 -c 1/2 -o RF -f 437.5 -l\n\
|
|
\n",\
|
|
PROGRAM_VERSION);
|
|
|
|
} /* end function print_usage */
|
|
|
|
int
|
|
main(int argc, char **argv)
|
|
{
|
|
int i;
|
|
//char pagemap_fn[64];
|
|
|
|
//unsigned char *data;
|
|
int OutputPower=5;
|
|
int ModeIQ=0;
|
|
|
|
|
|
int DigithinMode=0; //1 allow Clock output + SerialOutput
|
|
|
|
//int ModeDTX1=0;
|
|
|
|
my_circular_buffer.buffer=malloc(BIG_BUFFER_SIZE);
|
|
my_circular_buffer.head=0;
|
|
my_circular_buffer.tail=0;
|
|
|
|
|
|
|
|
fprintf(stdout,"RPIDATV Version %s (F5OEO Evariste) on ",__DATE__);
|
|
|
|
|
|
|
|
// Nearly there.. open the .ts file specified on the cmdline
|
|
fdts = 0;
|
|
int a;
|
|
int anyargs = 0;
|
|
while(1)
|
|
{
|
|
a = getopt(argc, argv, "i:s:c:hlf:m:p:x:y:");
|
|
|
|
if(a == -1)
|
|
{
|
|
if(anyargs) break;
|
|
else a='h'; //print usage and exit
|
|
}
|
|
anyargs = 1;
|
|
|
|
switch(a)
|
|
{
|
|
case 'i': // InputFile
|
|
FileName=optarg;
|
|
fdts = open(FileName, 'r');
|
|
break;
|
|
case 's': // SymbolRate
|
|
SymbolRate = atoi(optarg);
|
|
break;
|
|
case 'c': // FEC
|
|
if(strcmp("1/2",optarg)==0) FEC=1;
|
|
if(strcmp("2/3",optarg)==0) FEC=2;
|
|
if(strcmp("3/4",optarg)==0) FEC=3;
|
|
if(strcmp("5/6",optarg)==0) FEC=5;
|
|
if(strcmp("7/8",optarg)==0) FEC=7;
|
|
if(strcmp("carrier",optarg)==0) {printf("Rpidatv:Carrier mode\n");FEC=0;}//CARRIER MODE
|
|
if(strcmp("test",optarg)==0) FEC=-1;//TEST MODE
|
|
break;
|
|
case 'h': // help
|
|
print_usage();
|
|
terminate(0);
|
|
break;
|
|
case 'l': // loop mode
|
|
Loop = 1;
|
|
break;
|
|
case 'f': // Frequency (Mode RF)
|
|
TuneFrequency = atof(optarg)*1000000;
|
|
break;
|
|
case 'm': // Output mode
|
|
if(strcmp("IQ",optarg)==0) ModeIQ=1;
|
|
if(strcmp("RF",optarg)==0) ModeIQ=0;;
|
|
if(strcmp("PARALLEL",optarg)==0) ModeIQ=2;
|
|
if(strcmp("IQWITHCLK",optarg)==0) ModeIQ=1;
|
|
if(strcmp("DIGITHIN",optarg)==0) {ModeIQ=1;DigithinMode=1;};
|
|
|
|
break;
|
|
case 'p': // Power
|
|
OutputPower= atoi(optarg);
|
|
break;
|
|
case 'x': // Pin mapping GPIO I or RF
|
|
PinOutput[0]=atoi(optarg);
|
|
break;
|
|
case 'y': // Pin mapping GPIO Q
|
|
PinOutput[1]=atoi(optarg);
|
|
break;
|
|
|
|
case -1:
|
|
break;
|
|
case '?':
|
|
if (isprint(optopt) )
|
|
{
|
|
fprintf(stderr, "rpidatv: unknown option `-%c'.\n", optopt);
|
|
}
|
|
else
|
|
{
|
|
fprintf(stderr, "rpidatv: unknown option character `\\x%x'.\n", optopt);
|
|
}
|
|
print_usage();
|
|
|
|
exit(1);
|
|
break;
|
|
default:
|
|
print_usage();
|
|
exit(1);
|
|
break;
|
|
}/* end switch a */
|
|
}/* end while getopt() */
|
|
|
|
|
|
|
|
/* SEARCH FOR NEAR FREQUENCY */
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//data=malloc(DATA_FILE_SIZE);
|
|
|
|
|
|
//CalibrateSystem();
|
|
|
|
dvbsenco_init() ;
|
|
|
|
if(abs(FEC)>0)
|
|
viterbi_init(abs(FEC));
|
|
else
|
|
viterbi_init(1);
|
|
|
|
//setSchedPriority(SCHED_PRIORITY); //Seems to help a lot ---------> REMOVED SINCE MEMORY BUFFERING
|
|
|
|
|
|
|
|
|
|
// Calculate the frequency control word
|
|
// The fractional part is stored in the lower 12 bits
|
|
//freq_ctl = ((float)(PLLFREQ / CARRIERFREQ)) * ( 1 << 12 );
|
|
|
|
InitGpio();
|
|
|
|
InitDma(terminate);
|
|
|
|
if(ModeIQ==0)
|
|
InitUgly();
|
|
if(ModeIQ==1)
|
|
InitIQ(DigithinMode);
|
|
if(ModeIQ==2)
|
|
InitDTX1();
|
|
usleep(500);
|
|
//REMOVE TO TEST
|
|
|
|
pad_gpios_reg[PADS_GPIO_0] = 0x5a000000 + (OutputPower&0x7) + (1<<4) + (0<<3); // Set output power for I/Q GPIO18/GPIO19
|
|
|
|
|
|
|
|
//int NbStep;
|
|
|
|
// Initialise the DMA
|
|
|
|
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(1000);
|
|
|
|
|
|
|
|
dma_reg[DMA_CONBLK_AD+DMA_CHANNEL*0x40] = mem_virt_to_phys(ctl->cb);
|
|
//printf("DMA CONBLOCK : Virt %x-> Phys %x\n",ctl->cb,mem_virt_to_phys(ctl->cb));
|
|
|
|
|
|
|
|
// LET'S START DMA
|
|
udelay(100);
|
|
dma_reg[DMA_DEBUG+DMA_CHANNEL*0x40] = 7; // clear debug error flags
|
|
udelay(100);
|
|
// START DMA
|
|
//dma_reg[DMA_CS+DMA_CHANNEL*0x40] = 0x10880001; // go, mid priority, wait for outstanding writes :7 Seems Max Priority
|
|
|
|
udelay(200);
|
|
uint32_t last_cb = (uint32_t)ctl->cb;
|
|
uint32_t NbSymbol = 0;
|
|
|
|
//terminate(0); // Before going to DMA
|
|
|
|
|
|
|
|
//int PosInByte=0;
|
|
// 4000 samples * (32/2IQ) / SR/2 (half full) -> 4000*16/1E6Hz= 64 ms
|
|
int TimeToSleep=((8000)*1000)/SymbolRate;
|
|
//printf("TimeToSleep = %d\n",TimeToSleep);
|
|
//TRY REMOVING DELAY
|
|
//printf("Removing Delay");
|
|
static volatile uint32_t cur_cb;
|
|
int last_sample;
|
|
int this_sample;
|
|
int free_slots;
|
|
//int SumDelay=0;
|
|
|
|
if(Loop==1) printf("(looping)\n"); else printf("\n");
|
|
printf("TS Bitrate should be %lu bit/s\n",(uint32_t)SymbolRate*1000*2*188*abs((long)FEC)/(204L*(abs(FEC)+1L)));
|
|
|
|
|
|
long int start_time;
|
|
static long time_difference=0;
|
|
struct timespec gettime_now;
|
|
|
|
|
|
|
|
// CALIBRATION *************
|
|
|
|
|
|
//#define CALIBRATION 1
|
|
#ifdef CALIBRATION
|
|
int DiffCB;
|
|
dma_reg[DMA_CS+DMA_CHANNEL*0x40] = 0x10880001;
|
|
for(i=0;i<30;i++)
|
|
{
|
|
volatile uint32_t phys_cur_cb,phys_cur_cb2;
|
|
int try;
|
|
//for(try=0;try<10;try++) {if((phys_cur_cb=dma_reg[DMA_CONBLK_AD])==0) break;usleep(100);}
|
|
phys_cur_cb=dma_reg[DMA_CONBLK_AD+DMA_CHANNEL*0x40];
|
|
cur_cb = mem_phys_to_virt(phys_cur_cb);
|
|
|
|
|
|
clock_gettime(CLOCK_REALTIME, &gettime_now);
|
|
|
|
start_time = gettime_now.tv_nsec;
|
|
usleep(10000);
|
|
phys_cur_cb2=dma_reg[DMA_CONBLK_AD+DMA_CHANNEL*0x40];
|
|
|
|
DiffCB = mem_phys_to_virt(phys_cur_cb2)-cur_cb;
|
|
clock_gettime(CLOCK_REALTIME, &gettime_now);
|
|
time_difference = gettime_now.tv_nsec - start_time;
|
|
printf("cb_phys =%lx cb_phy2=%lx :Diff %lx \n",phys_cur_cb,phys_cur_cb2,abs(phys_cur_cb2-phys_cur_cb));
|
|
DiffCB=abs(phys_cur_cb-phys_cur_cb2);
|
|
if(ModeIQ==0)
|
|
printf("Calibrate NbCB=%d Time=%d : %f\n",DiffCB/(sizeof(dma_cb_t) * 2),time_difference,1000000.0*DiffCB/(sizeof(dma_cb_t) * 2)/(float)time_difference);
|
|
else
|
|
printf("Calibrate NbCB=%d Time=%d : %f\n",DiffCB/(sizeof(dma_cb_t) ),time_difference,1000000.0*16*DiffCB/(sizeof(dma_cb_t) )/(float)time_difference);
|
|
}
|
|
#endif
|
|
|
|
|
|
uchar PacketNULL[BURST_MEM_SIZE];
|
|
int k;
|
|
for(k=0;k<BURST_MEM_SIZE/188;k++)
|
|
{
|
|
PacketNULL[0+k*188]=0x47;
|
|
PacketNULL[1+k*188]=0x00;
|
|
for(i=2;i<188;i++) PacketNULL[i+k*188]=0xFF;
|
|
}
|
|
|
|
|
|
if(ModeIQ==0) //UGLY
|
|
{
|
|
cur_cb = (uint32_t)virtbase;
|
|
|
|
last_cb=(uint32_t)virtbase + 2*204*8* /*NUM_SAMPLES/8* */ sizeof(dma_cb_t) *2 ;//AGAIN *2 TO AVOID ISSUE ?
|
|
}
|
|
if(ModeIQ==1)
|
|
{
|
|
cur_cb = (uint32_t)virtbase;
|
|
last_cb=(uint32_t)virtbase + 204*8*2*sizeof(dma_cb_t) ;//*2 Should be "paire" else not aligned to I/Q
|
|
}
|
|
if(ModeIQ==2)
|
|
{
|
|
cur_cb = (uint32_t)virtbase;
|
|
last_cb=(uint32_t)virtbase + 4*188*8*sizeof(dma_cb_t)*2 ;//*2 Should be "paire" else not aligned to I/Q
|
|
}
|
|
|
|
dma_reg[DMA_CONBLK_AD+DMA_CHANNEL*0x40]=mem_virt_to_phys(virtbase);
|
|
|
|
// Pour que Freeslot<0 au 1er coup
|
|
//dma_reg[DMA_CONBLK_AD+DMA_CHANNEL*0x40] = mem_virt_to_phys(virtbase);
|
|
unsigned char Init=1;
|
|
//unsigned char Resync=0;
|
|
//unsigned char Start=1;
|
|
clock_gettime(CLOCK_REALTIME, &gettime_now);
|
|
printf("%ld:%ld : Fulling buffer \n",gettime_now.tv_sec,gettime_now.tv_nsec);
|
|
|
|
|
|
|
|
|
|
static uint32_t MaxToGetBuffer=0; // Time of the circular buffer : depend on SIZE and SymbolRate
|
|
uint64_t TSRate=SymbolRate*FEC*188*1000.0/(4*204*(FEC+1L));
|
|
if(ModeIQ==0)
|
|
MaxToGetBuffer=((NUM_SAMPLES-(204*2*4))*1000)/(SymbolRate*2); // ONLY UGLY
|
|
if(ModeIQ==1)
|
|
MaxToGetBuffer=((NUM_SAMPLES-(204*2*4))*1000*16*2)/(SymbolRate*2);//
|
|
if(ModeIQ==2)
|
|
{
|
|
|
|
MaxToGetBuffer=((NUM_SAMPLES-(188*8*4))*1000)/(TSRate);//
|
|
}
|
|
|
|
#ifdef WITH_MEMORY_BUFFER
|
|
// ------------------- START THE BUFFER FILLING THREAD ---------------
|
|
{
|
|
//uint64_t TSRate=SymbolRate*FEC*188*1000.0/(4*204*(FEC+1L));
|
|
pthread_attr_t attr;
|
|
pthread_mutex_init (&my_circular_buffer.lock, NULL);
|
|
printf("TSrate in byte =%llu\n",TSRate);
|
|
pthread_attr_init (&attr);
|
|
//pthread_attr_setdetachstate (&attr, PTHREAD_CREATE_DETACHED);
|
|
//pthread_create (&th1,&attr, &FillBigBuffer,NULL);
|
|
pthread_create (&th1,NULL, &FillBigBuffer,NULL);
|
|
pthread_attr_destroy (&attr);
|
|
|
|
if(FEC>0)
|
|
{
|
|
while(BufferAvailable()<(BIG_BUFFER_SIZE*5/10)) // 1/10 SECOND BUFFERING DEPEND ON SYMBOLRATE OR 80% BUFFERSIZE
|
|
{
|
|
//printf("Init Filling Memory buffer %d\n",BufferAvailable());
|
|
//printf(".");
|
|
usleep(10000);
|
|
}
|
|
}
|
|
/*
|
|
int NbByteInitRead=0;
|
|
pthread_mutex_lock(&my_circular_buffer.lock);
|
|
while((BufferAvailable()>TSRate)&&(NbByteInitRead%188!=0))
|
|
{
|
|
read_from_buffer();
|
|
NbByteInitRead++;
|
|
}
|
|
pthread_mutex_unlock(&my_circular_buffer.lock);
|
|
*/
|
|
printf("End Init Filling Memory buffer %d\n",BufferAvailable());
|
|
}
|
|
#endif
|
|
// -----------------------------------------------------------------
|
|
|
|
for (;;)
|
|
{
|
|
//********************************* MODE UGLY **************************************
|
|
if(ModeIQ==0)
|
|
{
|
|
static int StatusCompteur=0;
|
|
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) * 2);
|
|
last_sample = (last_cb - (uint32_t)virtbase) / (sizeof(dma_cb_t) * 2);
|
|
free_slots = this_sample - last_sample;
|
|
if (free_slots < 0) // WARNING : ORIGINAL CODE WAS < strictly
|
|
{
|
|
free_slots += NUM_SAMPLES;
|
|
|
|
|
|
}
|
|
else
|
|
{
|
|
//if(ctl->OverflowControl[0]==ctl->OverflowStatus[1])
|
|
{
|
|
//printf("Cptoverflow = %d %d \n",ctl->OverflowControl[0],ctl->OverflowStatus[0]);
|
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
TimeToSleep=((NUM_SAMPLES-free_slots-204*2*4)*1000)/((float)SymbolRate*2);//-22000; // 22ms de Switch process
|
|
//TimeToSleep=15000+KERNEL_GRANULARITY;
|
|
//TimeToSleep=25000;
|
|
|
|
|
|
|
|
|
|
//printf("cur_cb %lx FreeSlots = %d Time to sleep=%d\n",cur_cb,free_slots,TimeToSleep);
|
|
//printf("Buffer Available=%d\n",BufferAvailable());
|
|
|
|
clock_gettime(CLOCK_REALTIME, &gettime_now);
|
|
start_time = gettime_now.tv_nsec;
|
|
if(Init==0)
|
|
{
|
|
if(TimeToSleep>=(2200+KERNEL_GRANULARITY)) // 2ms : Time to process File/Canal Coding
|
|
{
|
|
if(TimeToSleep>=20000) TimeToSleep=20000;
|
|
udelay(TimeToSleep-(2200+KERNEL_GRANULARITY));
|
|
TimeToSleep=0;
|
|
}
|
|
|
|
else
|
|
{
|
|
|
|
printf("!");
|
|
usleep(1000);//20 ms mini !!
|
|
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) * 2);
|
|
last_sample = (last_cb - (uint32_t)virtbase) / (sizeof(dma_cb_t) * 2);
|
|
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%100==0)
|
|
{
|
|
|
|
//SetUglyFrequency(TuneFrequency);
|
|
//TuneFrequency+=5000.0;
|
|
|
|
printf("Memavailable %d/%d FreeSlot=%d/%d Bitrate : %f\n",BufferAvailable(),BIG_BUFFER_SIZE,free_slots_now,NUM_SAMPLES,(1000000.0*(free_slots_now-free_slots))/(float)time_difference);
|
|
}
|
|
StatusCompteur++;
|
|
//printf(" DiffTime = %ld FreeSlot=%d Bitrate : %f\n",time_difference,free_slots_now-free_slots,(1000000.0*(free_slots_now-free_slots))/(float)time_difference);
|
|
|
|
free_slots=free_slots_now;
|
|
// FIX IT : Max(freeslot et Numsample/8)
|
|
if(((Init==1)&&(free_slots <= 204*2*4 /*NUM_SAMPLES/8*/))||(FEC==0))
|
|
{
|
|
printf("%ld:%ld : End of Fulling buffer \n",gettime_now.tv_sec,gettime_now.tv_nsec);
|
|
dma_reg[DMA_CS+DMA_CHANNEL*0x40] = 0x10880001; // go, mid priority, wait for outstanding writes :7 Seems Max Priority
|
|
Init=0;
|
|
|
|
}
|
|
clock_gettime(CLOCK_REALTIME, &gettime_now);
|
|
start_time = gettime_now.tv_nsec;
|
|
|
|
#ifdef WITH_MEMORY_BUFFER
|
|
if((Init==0)&&(free_slots > (NUM_SAMPLES*9/10))&&(BufferAvailable()<=188*2))
|
|
{
|
|
int k;
|
|
store_in_buffer_1880(PacketNULL);
|
|
|
|
|
|
printf("Underflow\n");
|
|
}
|
|
#endif
|
|
|
|
while (((free_slots>204*2*4)&&(BufferAvailable()>188*2))||(FEC==0)) //204Bytes*2(IQ)*4 paires/octet
|
|
{
|
|
|
|
static uint32_t BuffAligned[256];
|
|
static uchar *buff=(uchar *)BuffAligned ;
|
|
|
|
static uchar *pRS204;
|
|
static int NbIQOutput;
|
|
|
|
static unsigned char BuffIQ[204*2];
|
|
static int k=0;
|
|
|
|
|
|
|
|
|
|
if((abs(FEC)>0)) //NORMAL MODE , 0 : CARRIER MODE
|
|
{
|
|
|
|
if(FEC>0)
|
|
{
|
|
#ifndef WITH_MEMORY_BUFFER
|
|
static int ByteRead=0;
|
|
if ((ByteRead=read(fdts,buff,188))!=188) // Read should be around 20us
|
|
{
|
|
// printf("END OF FILE OR packet is not 188 long %d\n",data_len);
|
|
if(Loop==1)
|
|
{
|
|
close(fdts);
|
|
fdts = open(FileName, 'r');
|
|
}
|
|
else
|
|
{
|
|
while(ByteRead!=188)
|
|
ByteRead+=read(fdts,buff+ByteRead,188-ByteRead);
|
|
//printf("End of processing file\n");
|
|
//terminate(0);
|
|
}
|
|
}
|
|
#else
|
|
|
|
read_from_buffer_188(buff);
|
|
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
buff[0]=0x47;
|
|
memset(buff+1,0,187);
|
|
}
|
|
|
|
|
|
if(buff[0]!=0x47) printf("SYNC ERROR \n");
|
|
|
|
// Time to compute is beetween 200 and 1000 us
|
|
|
|
energy (buff,buff) ;
|
|
|
|
|
|
|
|
|
|
|
|
if(FEC<0)
|
|
{
|
|
//unsigned char SyncByte=buff[0];
|
|
|
|
memset(buff+1,0,187);
|
|
}
|
|
reed (buff) ;
|
|
pRS204= interleave(buff) ;
|
|
|
|
NbIQOutput=viterbi (pRS204,BuffIQ);
|
|
}
|
|
else // TEST MODE (CARRIER)
|
|
{
|
|
|
|
NbIQOutput=204;
|
|
}
|
|
|
|
for(k=0;k<NbIQOutput;k++)
|
|
{
|
|
for(i=3;i>=0;i--)
|
|
{
|
|
if(abs(FEC)>0)
|
|
{
|
|
if(FEC>0)
|
|
{
|
|
ctl->sample[last_sample++]=TabIQ[(BuffIQ[k]>>(i*2))&0x3];
|
|
}
|
|
|
|
else
|
|
{
|
|
if((k<=32)) ctl->sample[last_sample++]=TabIQ[(BuffIQ[k]>>(i*2))&0x3];
|
|
else
|
|
{
|
|
|
|
ctl->sample[last_sample++]=TabIQ[i];
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
|
|
ctl->sample[last_sample++]=TabIQTest[0];
|
|
}
|
|
if (last_sample == NUM_SAMPLES) last_sample = 0;
|
|
NbSymbol++;
|
|
}
|
|
free_slots -= 4;
|
|
|
|
}
|
|
/*
|
|
static int free_slot_process;
|
|
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) * 2);
|
|
|
|
free_slot_process = this_sample - last_sample;
|
|
if (free_slot_process < 0) // WARNING : ORIGINAL CODE WAS < strictly
|
|
free_slot_process += NUM_SAMPLES;
|
|
if(free_slot_process>10000) printf("FreeSlotP=%d\n",free_slot_process);
|
|
*/
|
|
}
|
|
//printf("Process UNLOCK\n");
|
|
|
|
clock_gettime(CLOCK_REALTIME, &gettime_now);
|
|
time_difference = gettime_now.tv_nsec - start_time;
|
|
if(time_difference<0) time_difference+=1E9;
|
|
//printf("Time to Process file =%ld ByteRead = %ld BitRateTS= %ld\n",time_difference,TotalByteRead,TotalByteRead*8*1000000L/(time_difference));
|
|
if((Init==0)&&(FEC>0)&&(time_difference>MaxToGetBuffer*1000))
|
|
{
|
|
dma_reg[DMA_CONBLK_AD+DMA_CHANNEL*0x40]=mem_virt_to_phys(virtbase);
|
|
last_cb=(uint32_t)virtbase + 2*204*8* /*NUM_SAMPLES/8* */ sizeof(dma_cb_t) *2 ;
|
|
printf("File to slow !! wait for %ldus\n",(long int)time_difference/1000);
|
|
|
|
|
|
}
|
|
else
|
|
last_cb = (uint32_t)virtbase + last_sample * sizeof(dma_cb_t) * 2;
|
|
|
|
}
|
|
//********************************* MODE IQ **************************************
|
|
if(ModeIQ==1)
|
|
{
|
|
static int StatusCompteur=0;
|
|
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) );
|
|
last_sample = (last_cb - (uint32_t)virtbase) / (sizeof(dma_cb_t) );
|
|
free_slots = this_sample - last_sample;
|
|
if (free_slots < 0) // WARNING : ORIGINAL CODE WAS < strictly
|
|
free_slots += NUM_SAMPLES;
|
|
|
|
TimeToSleep=((NUM_SAMPLES-free_slots-204*2*4)*1000*16)/((float)SymbolRate*2); // 22ms de Switch process
|
|
|
|
//printf("FreeSlots = %d Time to sleep=%d\n",free_slots,TimeToSleep);
|
|
|
|
clock_gettime(CLOCK_REALTIME, &gettime_now);
|
|
start_time = gettime_now.tv_nsec;
|
|
if(Init==0)
|
|
{
|
|
if(TimeToSleep>=(2000+KERNEL_GRANULARITY)){
|
|
if(TimeToSleep>=20000) TimeToSleep=20000; //Digithin should be every 250ms
|
|
udelay(TimeToSleep-(2000+KERNEL_GRANULARITY));
|
|
}
|
|
else
|
|
{
|
|
printf("!");
|
|
usleep(1000);//20 ms mini !!
|
|
if(free_slots>(NUM_SAMPLES*9/10))
|
|
{
|
|
//printf("Buffer nearly empty...%d/%d\n",free_slots,NUM_SAMPLES);
|
|
//printf("$");
|
|
}
|
|
}
|
|
}
|
|
//printf("FreeSlots = %d Time to sleep=%d\n",free_slots,TimeToSleep);
|
|
|
|
if(DigithinMode==1)
|
|
{
|
|
|
|
static unsigned long int serial_start_time;
|
|
clock_gettime(CLOCK_REALTIME, &gettime_now);
|
|
if(Init==1) serial_start_time=gettime_now.tv_nsec;
|
|
static long int DiffSerialTime;
|
|
DiffSerialTime=gettime_now.tv_nsec-serial_start_time;
|
|
if(DiffSerialTime<0) DiffSerialTime+=1E9;
|
|
//printf("Time = %lu\n",DiffSerialTime/1000000L);
|
|
//if((DiffSerialTime)/1000000L>200) // Every 500 ms
|
|
{
|
|
if (uart0_filestream != -1)
|
|
{
|
|
//printf("Serial %s\n",DigithinCommand);
|
|
int count = write(uart0_filestream, DigithinCommand,sizeof(DigithinCommand));
|
|
//Filestream, bytes to write, number of bytes to write
|
|
if (count < 0)
|
|
{
|
|
printf("UART TX error\n");
|
|
}
|
|
else
|
|
{
|
|
//printf("Serial %d\n",count);
|
|
}
|
|
}
|
|
|
|
serial_start_time = gettime_now.tv_nsec;
|
|
}
|
|
}
|
|
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) );
|
|
last_sample = (last_cb - (uint32_t)virtbase) / (sizeof(dma_cb_t));
|
|
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%100==0)
|
|
{
|
|
|
|
//SetUglyFrequency(TuneFrequency);
|
|
//TuneFrequency+=5000.0;
|
|
|
|
printf("Memavailable %d/%d FreeSlot=%d/%d Bitrate : %f\n",BufferAvailable(),BIG_BUFFER_SIZE,free_slots_now,NUM_SAMPLES,(1000000.0*(free_slots_now-free_slots))/(float)time_difference);
|
|
}
|
|
StatusCompteur++;
|
|
|
|
//printf("DiffTime = %ld FreeSlot=%ld Bitrate : %f\n",time_difference,free_slots_now-free_slots,(1000000.0*(free_slots_now-free_slots)*16.0)/(float)time_difference);
|
|
free_slots=free_slots_now;
|
|
|
|
|
|
if((Init==1)&&(free_slots <= (204*8*2)))
|
|
{
|
|
printf("%ld:%ld : End of Fulling buffer \n",gettime_now.tv_sec,gettime_now.tv_nsec);
|
|
dma_reg[DMA_CS+DMA_CHANNEL*0x40] = 0x10880001; // go, mid priority, wait for outstanding writes :7 Seems Max Priority
|
|
Init=0;
|
|
sleep(0);
|
|
}
|
|
//printf("Free Slots = %d\n",free_slots);
|
|
//if (time_difference/1000>TimeToSleep*2) printf("Time %d\n",time_difference/1000);
|
|
|
|
// FEC 1/2 : 204*2*1/2/8(8=4octets de I et 4 octets de Q) : il faut divisible par 8
|
|
//FEC 1/2 : il faut 2*204 pour divisble par 8
|
|
//FEC 2/3 : 204 OK
|
|
//FEC 3/4 : il faut 4*204
|
|
//FEC 5/6 : 340 Octets : il faut 2
|
|
//FEC 7/8 : 357 8
|
|
|
|
clock_gettime(CLOCK_REALTIME, &gettime_now);
|
|
start_time = gettime_now.tv_nsec;
|
|
#ifdef WITH_MEMORY_BUFFER
|
|
if((Init==0)&&(free_slots > (NUM_SAMPLES*9/10))&&(BufferAvailable()<=188*8))
|
|
{
|
|
int k;
|
|
store_in_buffer_1880(PacketNULL);
|
|
/*while(BufferAvailable()<188*8)
|
|
{
|
|
//printf("!");
|
|
sleep(1000);
|
|
}
|
|
*/
|
|
/*
|
|
pthread_mutex_lock(&my_circular_buffer.lock);
|
|
for(k=0;k<8*2;k++)
|
|
store_in_buffer_1880(PacketNULL);
|
|
pthread_mutex_unlock(&my_circular_buffer.lock);
|
|
*/
|
|
|
|
printf("Underflow\n");
|
|
}
|
|
#endif
|
|
#ifdef WITH_MEMORY_BUFFER
|
|
while ((free_slots > (204*8*2))&&((BufferAvailable()>=188*8)||(FEC==0))) //204Bytes*2(IQ)/32
|
|
#else
|
|
while ((free_slots > (204*8*2))||(FEC==0)) //204Bytes*2(IQ)/32
|
|
#endif
|
|
{
|
|
|
|
//static uint32_t BuffAligned[256];
|
|
//static uchar *buff=(uchar *)BuffAligned ;
|
|
|
|
static uchar buff[208];
|
|
|
|
static uchar *pRS204;
|
|
static int NbIQOutput=0;
|
|
static unsigned char BuffIQ[204*8*2];
|
|
static int k=0;
|
|
|
|
|
|
//static int data_len_total;
|
|
//data_len_total=0;
|
|
|
|
|
|
NbIQOutput=0;
|
|
// Time to compute is beetween 200 and 1000 us
|
|
|
|
if(abs(FEC)>0) //NORMAL MODE , 0 : CARRIER MODE
|
|
{
|
|
for(i=0;i<8;i++)
|
|
{
|
|
|
|
if(FEC>0)
|
|
{
|
|
#ifndef WITH_MEMORY_BUFFER
|
|
/*static*/ int ByteRead=0;
|
|
if ((ByteRead=read(fdts,buff,188))!=188) // Read should be around 20us
|
|
{
|
|
printf("END OF FILE OR packet is not 188 long %d\n",ByteRead);
|
|
if(Loop==1)
|
|
{
|
|
close(fdts);
|
|
fdts = open(FileName, 'r');
|
|
ByteRead=read(fdts,buff,188);
|
|
}
|
|
else
|
|
{
|
|
while(ByteRead!=188)
|
|
{
|
|
ByteRead+=read(fdts,buff+ByteRead,188-ByteRead);
|
|
usleep(1000);
|
|
}
|
|
printf("ok 188\n");
|
|
//terminate(0);
|
|
}
|
|
|
|
}
|
|
#else
|
|
int ii;
|
|
|
|
read_from_buffer_188(buff);
|
|
|
|
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
buff[0]=0x47;
|
|
memset(buff+1,0,187);
|
|
}
|
|
|
|
|
|
if(buff[0]!=0x47) printf("SYNC 188 ERROR \n");
|
|
|
|
energy (buff,buff) ;
|
|
|
|
|
|
if(FEC<0)
|
|
{
|
|
memset(buff+1,0x183,187);
|
|
//memset(pRS204+1,0,203);
|
|
}
|
|
reed (buff) ;
|
|
pRS204= interleave(buff) ;
|
|
//pRS204 = dvbsenco (buff) ;
|
|
NbIQOutput+=viterbi (pRS204,BuffIQ+NbIQOutput);
|
|
//printf("NbIQ=%d\n",NbIQOutput);
|
|
if((NbIQOutput%8)==0) break;
|
|
|
|
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//printf("408\n");
|
|
NbIQOutput=408;
|
|
}
|
|
|
|
//printf("NbIQoutput=%d %d\n",NbIQOutput,free_slots);
|
|
if((NbIQOutput%8)!=0) printf("ERROR ALIGNED \n");
|
|
|
|
for(k=0;k<NbIQOutput;k+=8)
|
|
{
|
|
static uint32_t I32,Q32;
|
|
I32=0L;
|
|
Q32=0L;
|
|
for (i=0;i<4;i++)
|
|
{
|
|
I32|=((BuffIQ[k]&(1<<(i*2+1)))>>(i*2+1))<<(28+i);
|
|
I32|=((BuffIQ[k+1]&(1<<(i*2+1)))>>(i*2+1))<<(24+i);
|
|
I32|=((BuffIQ[k+2]&(1<<(i*2+1)))>>(i*2+1))<<(20+i);
|
|
I32|=((BuffIQ[k+3]&(1<<(i*2+1)))>>(i*2+1))<<(16+i);
|
|
I32|=((BuffIQ[k+4]&(1<<(i*2+1)))>>(i*2+1))<<(12+i);
|
|
I32|=((BuffIQ[k+5]&(1<<(i*2+1)))>>(i*2+1))<<(8+i);
|
|
I32|=((BuffIQ[k+6]&(1<<(i*2+1)))>>(i*2+1))<<(4+i);
|
|
I32|=((BuffIQ[k+7]&(1<<(i*2+1)))>>(i*2+1))<<i;
|
|
|
|
|
|
Q32|=((BuffIQ[k]&(1<<(i*2)))>>(i*2))<<(28+i);
|
|
Q32|=((BuffIQ[k+1]&(1<<(i*2)))>>(i*2))<<(24+i);
|
|
Q32|=((BuffIQ[k+2]&(1<<(i*2)))>>(i*2))<<(20+i);
|
|
Q32|=((BuffIQ[k+3]&(1<<(i*2)))>>(i*2))<<(16+i);
|
|
Q32|=((BuffIQ[k+4]&(1<<(i*2)))>>(i*2))<<(12+i);
|
|
Q32|=((BuffIQ[k+5]&(1<<(i*2)))>>(i*2))<<(8+i);
|
|
Q32|=((BuffIQ[k+6]&(1<<(i*2)))>>(i*2))<<(4+i);
|
|
Q32|=((BuffIQ[k+7]&(1<<(i*2)))>>(i*2))<<i;
|
|
|
|
}
|
|
|
|
if(FEC==0) //CARRIER MODE : OVERWRITE IQ previously calculated
|
|
{
|
|
|
|
//I32=TabIQTestI[NbSymbol%4];
|
|
//Q32=TabIQTestQ[NbSymbol%4];
|
|
I32=0x55555555;
|
|
Q32=0x55555555;
|
|
}
|
|
/*
|
|
if(FEC<0)
|
|
{
|
|
if(k%204!=0)
|
|
{
|
|
I32=TabIQTestI[0];
|
|
Q32=TabIQTestQ[0];
|
|
}
|
|
}
|
|
*/
|
|
ctl->sample[last_sample++] =I32;
|
|
if (last_sample == NUM_SAMPLES) last_sample = 0;
|
|
ctl->sample[last_sample++] =Q32;
|
|
if (last_sample == NUM_SAMPLES) last_sample = 0;
|
|
//printf("I32 %x Q32 %x \n",I32,Q32);
|
|
NbSymbol++;
|
|
free_slots -= 2;
|
|
}
|
|
|
|
|
|
|
|
}
|
|
clock_gettime(CLOCK_REALTIME, &gettime_now);
|
|
time_difference = gettime_now.tv_nsec - start_time;
|
|
if(time_difference<0) time_difference+=1E9;
|
|
if((FEC>0)&&(time_difference>MaxToGetBuffer*1000))
|
|
{
|
|
printf("File to slow !! wait for %ld us/ %ld us\n",(long int)time_difference/1000,(long int)MaxToGetBuffer);
|
|
|
|
|
|
}
|
|
last_cb = (uint32_t)virtbase + last_sample * sizeof(dma_cb_t) ;
|
|
|
|
}
|
|
|
|
// ************************************************ MODE DTX 1 ***********************************************
|
|
if(ModeIQ==2)
|
|
{
|
|
//uint32_t static TSRate;
|
|
//TSRate=SymbolRate*FEC*188/(4*204*(FEC+1L));
|
|
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)*2 );
|
|
last_sample = (last_cb - (uint32_t)virtbase) / (sizeof(dma_cb_t)*2 );
|
|
|
|
free_slots = this_sample - last_sample;
|
|
if (free_slots < 0) // WARNING : ORIGINAL CODE WAS < strictly
|
|
free_slots += NUM_SAMPLES;
|
|
|
|
//TimeToSleep=((NUM_SAMPLES-free_slots-188*4*2)*800)/((float)TSRate); // 22ms de Switch process
|
|
TimeToSleep=2000+KERNEL_GRANULARITY;
|
|
//printf("FreeSlots = %d Time to sleep=%d\n",free_slots,TimeToSleep);
|
|
|
|
clock_gettime(CLOCK_REALTIME, &gettime_now);
|
|
start_time = gettime_now.tv_nsec;
|
|
if(TimeToSleep>=(2000+KERNEL_GRANULARITY))
|
|
{
|
|
if(TimeToSleep>=200000) TimeToSleep=200000; //Digithin should be every 250ms
|
|
udelay(TimeToSleep-(2000+KERNEL_GRANULARITY));
|
|
}
|
|
else
|
|
{
|
|
//usleep(0);//20 ms mini !!
|
|
if(free_slots>(NUM_SAMPLES*9/10))
|
|
printf("Buffer nearly empty...%d/%d\n",free_slots,NUM_SAMPLES);
|
|
}
|
|
//printf("FreeSlots = %d Time to sleep=%d\n",free_slots,TimeToSleep);
|
|
|
|
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)*2);
|
|
last_sample = (last_cb - (uint32_t)virtbase) / (sizeof(dma_cb_t)*2);
|
|
//printf("%lx %lx\n",this_sample,last_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;
|
|
|
|
//printf("DiffTime = %ld FreeSlot=%ld Bitrate : %f\n",time_difference,free_slots_now-free_slots,(1000000.0*(free_slots_now-free_slots))/(float)time_difference*2.0);
|
|
free_slots=free_slots_now;
|
|
|
|
|
|
if((Init==1)&&(free_slots <= (188*8*4)/*NUM_SAMPLES/8*/))
|
|
{
|
|
printf("%ld:%ld : End of Fulling buffer \n",gettime_now.tv_sec,gettime_now.tv_nsec);
|
|
dma_reg[DMA_CS+DMA_CHANNEL*0x40] = 0x10880001; // go, mid priority, wait for outstanding writes :7 Seems Max Priority
|
|
Init=0;
|
|
sleep(0);
|
|
}
|
|
|
|
|
|
clock_gettime(CLOCK_REALTIME, &gettime_now);
|
|
start_time = gettime_now.tv_nsec;
|
|
while ((free_slots > (188*8*4))&&(BufferAvailable()>188*8)) //
|
|
{
|
|
|
|
//static uint32_t BuffAligned[256];
|
|
//static uchar *buff=(uchar *)BuffAligned ;
|
|
|
|
static uchar buff[208];
|
|
|
|
|
|
//static int data_len_total;
|
|
//data_len_total=0;
|
|
|
|
|
|
|
|
for(i=0;i<8;i++)
|
|
{
|
|
|
|
if(FEC>0)
|
|
{
|
|
/*
|
|
static int ByteRead=0;
|
|
if ((ByteRead=read(fdts,buff,188))!=188) // Read should be around 20us
|
|
{
|
|
printf("END OF FILE OR packet is not 188 long %d\n",data_len);
|
|
if(Loop==1)
|
|
{
|
|
close(fdts);
|
|
fdts = open(argv[1], 'r');
|
|
}
|
|
else
|
|
{
|
|
while(ByteRead!=188)
|
|
ByteRead+=read(fdts,buff+ByteRead,188-ByteRead);
|
|
//printf("End of processing file\n");
|
|
//terminate(0);
|
|
}
|
|
|
|
}
|
|
*/
|
|
int ii;
|
|
pthread_mutex_lock(&my_circular_buffer.lock);
|
|
for(ii=0;ii<188;ii++) buff[ii]=read_from_buffer();
|
|
pthread_mutex_unlock(&my_circular_buffer.lock);
|
|
|
|
}
|
|
else
|
|
{
|
|
buff[0]=0x47;
|
|
memset(buff+1,0,187);
|
|
}
|
|
|
|
static char MapBit[8]={2,3,4,14,15,17,18,27};
|
|
|
|
|
|
if(buff[0]!=0x47) printf("SYNC 188 ERROR \n");
|
|
int BytePacket=0;
|
|
volatile uint32_t ByteClear;
|
|
volatile uint32_t ByteSet;
|
|
int bit;
|
|
|
|
for(BytePacket=0;BytePacket<188;BytePacket++)
|
|
{
|
|
|
|
ByteSet=0;
|
|
ByteClear=(1<<22); //CLK LOW
|
|
|
|
|
|
|
|
if((BytePacket==0))
|
|
{
|
|
|
|
ByteSet|= (1 << 24); //TPCLK HIGH
|
|
|
|
}
|
|
else
|
|
{
|
|
|
|
|
|
ByteClear|=(1 <<24); //TPCLK 0
|
|
|
|
}
|
|
|
|
//TEST
|
|
/*if(BytePacket==0)
|
|
buff[BytePacket]=0x47;
|
|
else buff[BytePacket]=0x12;*/
|
|
|
|
|
|
|
|
for(bit=0;bit<8;bit++)
|
|
{
|
|
ByteSet|=((buff[BytePacket]>>bit)&1)<<MapBit[bit];
|
|
ByteClear|=(~((buff[BytePacket]>>bit))&1)<<MapBit[bit];
|
|
|
|
}
|
|
|
|
//printf("CYCLE 1:Byte %lx ByteSet %lx ByteClear %lx \n",buff[BytePacket],ByteSet,ByteClear);
|
|
ctl->sample[last_sample++] =ByteClear;
|
|
if (last_sample == NUM_SAMPLES) last_sample = 0;
|
|
ctl->sample[last_sample++] =ByteSet;
|
|
if (last_sample == NUM_SAMPLES) last_sample = 0;
|
|
|
|
//ByteSet=(1<<22);//CLK HIGH
|
|
ByteSet=(1<<22);//CLK HIGH
|
|
ByteClear=0;//(1<<22);
|
|
|
|
//printf("CYCLE 2:Byte %lx ByteSet %lx ByteClear %lx \n",buff[BytePacket],ByteSet,ByteClear);
|
|
ctl->sample[last_sample++] =ByteClear;
|
|
if (last_sample == NUM_SAMPLES) last_sample = 0;
|
|
|
|
ctl->sample[last_sample++] =ByteSet;
|
|
if (last_sample == NUM_SAMPLES) last_sample = 0;
|
|
|
|
free_slots -= 4; // *2 porbleme de Index entre CBP ET DATA
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
clock_gettime(CLOCK_REALTIME, &gettime_now);
|
|
time_difference = gettime_now.tv_nsec - start_time;
|
|
if(time_difference<0) time_difference+=1E9;
|
|
if((FEC>0)&&(time_difference>MaxToGetBuffer*4000))
|
|
{
|
|
printf("File to slow !! wait for %ld us/ %ld us\n",(long int)time_difference/1000,(long int)MaxToGetBuffer);
|
|
|
|
|
|
}
|
|
|
|
last_cb = (uint32_t)virtbase + last_sample * sizeof(dma_cb_t)*2 ;
|
|
|
|
}
|
|
}
|
|
|
|
terminate(0);
|
|
return(0);
|
|
}
|
|
|