From 96276615b234e82dbb636d152d28eaa0be5d22b6 Mon Sep 17 00:00:00 2001 From: F5OEO Date: Mon, 19 Mar 2018 14:30:55 +0000 Subject: [PATCH] Use librpitx - Should improve clean spectrum --- makefile | 19 +- wspr.cpp | 637 ++++++------------------------------------------------- 2 files changed, 68 insertions(+), 588 deletions(-) diff --git a/makefile b/makefile index 0a48b7a..b58801c 100644 --- a/makefile +++ b/makefile @@ -1,28 +1,19 @@ prefix=/usr/local -CFLAGS += -Wall -CXXFLAGS += -D_GLIBCXX_DEBUG -std=c++11 -Wall -Werror -fmax-errors=5 +CFLAGS += -Wall -Wno-unused-variable +CXXFLAGS += -Wall -Wall -Wno-unused-variable -std=c++11 LDLIBS += -lm -ifeq ($(findstring armv6,$(shell uname -m)),armv6) -# Broadcom BCM2835 SoC with 700 MHz 32-bit ARM 1176JZF-S (ARMv6 arch) -PI_VERSION = -DRPI1 -else -# Broadcom BCM2836 SoC with 900 MHz 32-bit quad-core ARM Cortex-A7 (ARMv7 arch) -# Broadcom BCM2837 SoC with 1.2 GHz 64-bit quad-core ARM Cortex-A53 (ARMv8 arch) -PI_VERSION = -DRPI23 -endif + all: wspr gpioclk -mailbox.o: mailbox.c mailbox.h - $(CC) $(CFLAGS) -c mailbox.c wspr: mailbox.o wspr.cpp mailbox.h - $(CXX) $(CXXFLAGS) $(LDFLAGS) $(LDLIBS) $(PI_VERSION) mailbox.o wspr.cpp -owspr + $(CXX) $(CXXFLAGS) $(LDFLAGS) $(LDLIBS) wspr.cpp librpitx/src/librpitx.a -owspr gpioclk: gpioclk.cpp - $(CXX) $(CXXFLAGS) $(LDFLAGS) $(LDLIBS) $(PI_VERSION) gpioclk.cpp -ogpioclk + $(CXX) $(CXXFLAGS) $(LDFLAGS) $(LDLIBS) gpioclk.cpp -ogpioclk clean: $(RM) *.o gpioclk wspr diff --git a/wspr.cpp b/wspr.cpp index a32f931..a75e7c0 100644 --- a/wspr.cpp +++ b/wspr.cpp @@ -44,86 +44,17 @@ #include #include #include - -#ifdef __cplusplus -extern "C" { -#include "mailbox.h" -} -#endif /* __cplusplus */ +#include "librpitx/src/librpitx.h" -// Note on accessing memory in RPi: -// -// There are 3 (yes three) address spaces in the Pi: -// Physical addresses -// These are the actual address locations of the RAM and are equivalent -// to offsets into /dev/mem. -// The peripherals (DMA engine, PWM, etc.) are located at physical -// address 0x2000000 for RPi1 and 0x3F000000 for RPi2/3. -// Virtual addresses -// These are the addresses that a program sees and can read/write to. -// Addresses 0x00000000 through 0xBFFFFFFF are the addresses available -// to a program running in user space. -// Addresses 0xC0000000 and above are available only to the kernel. -// The peripherals start at address 0xF2000000 in virtual space but -// this range is only accessible by the kernel. The kernel could directly -// access peripherals from virtual addresses. It is not clear to me my -// a user space application running as 'root' does not have access to this -// memory range. -// Bus addresses -// This is a different (virtual?) address space that also maps onto -// physical memory. -// The peripherals start at address 0x7E000000 of the bus address space. -// The DRAM is also available in bus address space in 4 different locations: -// 0x00000000 "L1 and L2 cached alias" -// 0x40000000 "L2 cache coherent (non allocating)" -// 0x80000000 "L2 cache (only)" -// 0xC0000000 "Direct, uncached access" -// -// Accessing peripherals from user space (virtual addresses): -// The technique used in this program is that mmap is used to map portions of -// /dev/mem to an arbitrary virtual address. For example, to access the -// GPIO's, the gpio range of addresses in /dev/mem (physical addresses) are -// mapped to a kernel chosen virtual address. After the mapping has been -// set up, writing to the kernel chosen virtual address will actually -// write to the GPIO addresses in physical memory. -// -// Accessing RAM from DMA engine -// The DMA engine is programmed by accessing the peripheral registers but -// must use bus addresses to access memory. Thus, to use the DMA engine to -// move memory from one virtual address to another virtual address, one needs -// to first find the physical addresses that corresponds to the virtual -// addresses. Then, one needs to find the bus addresses that corresponds to -// those physical addresses. Finally, the DMA engine can be programmed. i.e. -// DMA engine access should use addresses starting with 0xC. -// -// The perhipherals in the Broadcom documentation are described using their bus -// addresses and structures are created and calculations performed in this -// program to figure out how to access them with virtual addresses. +clkgpio *clk=NULL; +ngfmdmasync *ngfmtest=NULL; + #define ABORT(a) exit(a) // Used for debugging #define MARK std::cout << "Currently in file: " << __FILE__ << " line: " << __LINE__ << std::endl - -// PLLD clock frequency. -// For RPi1, after NTP converges, these is a 2.5 PPM difference between -// the PPM correction reported by NTP and the actual frequency offset of -// the crystal. This 2.5 PPM offset is not present in the RPi2 and RPi3. -// This 2.5 PPM offset is compensated for here, but only for the RPi1. -#ifdef RPI23 -#define F_PLLD_CLK (500000000.0) -#else -#ifdef RPI1 -#define F_PLLD_CLK (500000000.0*(1-2.500e-6)) -#else -#error "RPI version macro is not defined" -#endif -#endif -// Empirical value for F_PWM_CLK that produces WSPR symbols that are 'close' to -// 0.682s long. For some reason, despite the use of DMA, the load on the PI -// affects the TX length of the symbols. However, the varying symbol length is -// compensated for in the main loop. -#define F_PWM_CLK_INIT (31156186.6125761) +typedef enum {WSPR,TONE} mode_type; // WSRP nominal symbol time #define WSPR_SYMTIME (8192.0/12000.0) @@ -132,216 +63,23 @@ extern "C" { #define WSPR_RAND_OFFSET 80 #define WSPR15_RAND_OFFSET 8 -// Choose proper base address depending on RPI1/RPI23 macro from makefile. -// PERI_BASE_PHYS is the base address of the peripherals, in physical -// address space. -#ifdef RPI23 -#define PERI_BASE_PHYS 0x3f000000 -#define MEM_FLAG 0x04 -#else -#ifdef RPI1 -#define PERI_BASE_PHYS 0x20000000 -#define MEM_FLAG 0x0c -#else -#error "RPI version macro is not defined" -#endif -#endif - -#define PAGE_SIZE (4*1024) -#define BLOCK_SIZE (4*1024) - -// peri_base_virt is the base virtual address that a userspace program (this -// program) can use to read/write to the the physical addresses controlling -// the peripherals. This address is mapped at runtime using mmap and /dev/mem. -// This must be declared global so that it can be called by the atexit -// function. -volatile unsigned *peri_base_virt = NULL; - -// Given an address in the bus address space of the peripherals, this -// macro calculates the appropriate virtual address to use to access -// the requested bus address space. It does this by first subtracting -// 0x7e000000 from the supplied bus address to calculate the offset into -// the peripheral address space. Then, this offset is added to peri_base_virt -// Which is the base address of the peripherals, in virtual address space. -#define ACCESS_BUS_ADDR(buss_addr) *(volatile int*)((long int)peri_base_virt+(buss_addr)-0x7e000000) -// Given a bus address in the peripheral address space, set or clear a bit. -#define SETBIT_BUS_ADDR(base, bit) ACCESS_BUS_ADDR(base) |= 1<=mbox.pool_size) { - std::cerr << "Error: unable to allocated more pages!" << std::endl; - ABORT(-1); - } - unsigned offset = mbox.pool_cnt*4096; - *vAddr = (void*)(((unsigned)mbox.virt_addr) + offset); - *bAddr = (void*)(((unsigned)mbox.bus_addr) + offset); - //printf("getRealMemoryPageFromPool bus_addr=%x virt_addr=%x\n", (unsigned)*pAddr,(unsigned)*vAddr); - mbox.pool_cnt++; -} - -// Free the memory pool -void deallocMemPool() { - if(mbox.virt_addr!=NULL) { - unmapmem(mbox.virt_addr, mbox.pool_size*4096); - } - if (mbox.mem_ref!=0) { - mem_unlock(mbox.handle, mbox.mem_ref); - mem_free(mbox.handle, mbox.mem_ref); - } -} - // Disable the PWM clock and wait for it to become 'not busy'. void disable_clock() { - // Check if mapping has been set up yet. - if (peri_base_virt==NULL) { - return; - } - // Disable the clock (in case it's already running) by reading current - // settings and only clearing the enable bit. - auto settings=ACCESS_BUS_ADDR(CM_GP0CTL_BUS); - // Clear enable bit and add password - settings=(settings&0x7EF)|0x5A000000; - // Disable - ACCESS_BUS_ADDR(CM_GP0CTL_BUS) = *((int*)&settings); - // Wait for clock to not be busy. - while (true) { - if (!(ACCESS_BUS_ADDR(CM_GP0CTL_BUS)&(1<<7))) { - break; - } - } + } // Turn on TX void txon() { - // Set function select for GPIO4. - // Fsel 000 => input - // Fsel 001 => output - // Fsel 100 => alternate function 0 - // Fsel 101 => alternate function 1 - // Fsel 110 => alternate function 2 - // Fsel 111 => alternate function 3 - // Fsel 011 => alternate function 4 - // Fsel 010 => alternate function 5 - // Function select for GPIO is configured as 'b100 which selects - // alternate function 0 for GPIO4. Alternate function 0 is GPCLK0. - // See section 6.2 of Arm Peripherals Manual. - SETBIT_BUS_ADDR(GPIO_BUS_BASE , 14); - CLRBIT_BUS_ADDR(GPIO_BUS_BASE , 13); - CLRBIT_BUS_ADDR(GPIO_BUS_BASE , 12); - - // Set GPIO drive strength, more info: http://www.scribd.com/doc/101830961/GPIO-Pads-Control2 - //ACCESS_BUS_ADDR(PADS_GPIO_0_27_BUS) = 0x5a000018 + 0; //2mA -3.4dBm - //ACCESS_BUS_ADDR(PADS_GPIO_0_27_BUS) = 0x5a000018 + 1; //4mA +2.1dBm - //ACCESS_BUS_ADDR(PADS_GPIO_0_27_BUS) = 0x5a000018 + 2; //6mA +4.9dBm - //ACCESS_BUS_ADDR(PADS_GPIO_0_27_BUS) = 0x5a000018 + 3; //8mA +6.6dBm(default) - //ACCESS_BUS_ADDR(PADS_GPIO_0_27_BUS) = 0x5a000018 + 4; //10mA +8.2dBm - //ACCESS_BUS_ADDR(PADS_GPIO_0_27_BUS) = 0x5a000018 + 5; //12mA +9.2dBm - //ACCESS_BUS_ADDR(PADS_GPIO_0_27_BUS) = 0x5a000018 + 6; //14mA +10.0dBm - ACCESS_BUS_ADDR(PADS_GPIO_0_27_BUS) = 0x5a000018 + 7; //16mA +10.6dBm + + //ACCESS_BUS_ADDR(PADS_GPIO_0_27_BUS) = 0x5a000018 + 7; //16mA +10.6dBm disable_clock(); - // Set clock source as PLLD. - struct GPCTL setupword = {6/*SRC*/, 0, 0, 0, 0, 3,0x5a}; - - // Enable clock. - setupword = {6/*SRC*/, 1, 0, 0, 0, 3,0x5a}; - ACCESS_BUS_ADDR(CM_GP0CTL_BUS) = *((int*)&setupword); + } // Turn transmitter on void txoff() { - //struct GPCTL setupword = {6/*SRC*/, 0, 0, 0, 0, 1,0x5a}; - //ACCESS_BUS_ADDR(CM_GP0CTL_BUS) = *((int*)&setupword); disable_clock(); } @@ -362,74 +100,12 @@ void txSym( struct PageInfo & constPage, int & bufPtr ) { - const int f0_idx=sym_num*2; - const int f1_idx=f0_idx+1; - const double f0_freq=dma_table_freq[f0_idx]; - const double f1_freq=dma_table_freq[f1_idx]; - const double tone_freq=center_freq-1.5*tone_spacing+sym_num*tone_spacing; - // Double check... - assert((tone_freq>=f0_freq)&&(tone_freq<=f1_freq)); - const double f0_ratio=1.0-(tone_freq-f0_freq)/(f1_freq-f0_freq); - //cout << "f0_ratio = " << f0_ratio << std::endl; - assert ((f0_ratio>=0)&&(f0_ratio<=1)); - const long int n_pwmclk_per_sym=round(f_pwm_clk*tsym); - - long int n_pwmclk_transmitted=0; - long int n_f0_transmitted=0; - //printf("",(unsigned)&instrs[bufPtr]); - while (n_pwmclk_transmittedn_pwmclk_per_sym) { - n_pwmclk=n_pwmclk_per_sym-n_pwmclk_transmitted; - } - - // Calculate number of clocks to transmit f0 during this iteration so - // that the long term average is as close to f0_ratio as possible. - const long int n_f0=round(f0_ratio*(n_pwmclk_transmitted+n_pwmclk))-n_f0_transmitted; - const long int n_f1=n_pwmclk-n_f0; - - // Configure the transmission for this iteration - // Set GPIO pin to transmit f0 - bufPtr++; - while( ACCESS_BUS_ADDR(DMA_BUS_BASE + 0x04 /* CurBlock*/) == (long int)(instrs[bufPtr].b)) usleep(100); - ((struct CB*)(instrs[bufPtr].v))->SOURCE_AD = (long int)constPage.b + f0_idx*4; - - // Wait for n_f0 PWM clocks - bufPtr++; - while( ACCESS_BUS_ADDR(DMA_BUS_BASE + 0x04 /* CurBlock*/) == (long int)(instrs[bufPtr].b)) usleep(100); - ((struct CB*)(instrs[bufPtr].v))->TXFR_LEN = n_f0; - - // Set GPIO pin to transmit f1 - bufPtr++; - while( ACCESS_BUS_ADDR(DMA_BUS_BASE + 0x04 /* CurBlock*/) == (long int)(instrs[bufPtr].b)) usleep(100); - ((struct CB*)(instrs[bufPtr].v))->SOURCE_AD = (long int)constPage.b + f1_idx*4; - - // Wait for n_f1 PWM clocks - bufPtr=(bufPtr+1) % (1024); - while( ACCESS_BUS_ADDR(DMA_BUS_BASE + 0x04 /* CurBlock*/) == (long int)(instrs[bufPtr].b)) usleep(100); - ((struct CB*)(instrs[bufPtr].v))->TXFR_LEN = n_f1; - - // Update counters - n_pwmclk_transmitted+=n_pwmclk; - n_f0_transmitted+=n_f0; - } - //printf("",(unsigned)instrs[bufPtr].v,(unsigned)instrs[bufPtr].b); + } // Turn off (reset) DMA engine void unSetupDMA(){ - // Check if mapping has been set up yet. - if (peri_base_virt==NULL) { - return; - } - //cout << "Exiting!" << std::endl; - struct DMAregs* DMA0 = (struct DMAregs*)&(ACCESS_BUS_ADDR(DMA_BUS_BASE)); - DMA0->CS =1<<31; // reset dma controller + txoff(); } @@ -441,139 +117,7 @@ double bit_trunc( return floor(d/pow(2.0,lsb))*pow(2.0,lsb); } -// Program the tuning words into the DMA table. -void setupDMATab( - const double & center_freq_desired, - const double & tone_spacing, - const double & plld_actual_freq, - std::vector & dma_table_freq, - double & center_freq_actual, - struct PageInfo & constPage -){ - // Make sure that all the WSPR tones can be produced solely by - // varying the fractional part of the frequency divider. - center_freq_actual=center_freq_desired; - double div_lo=bit_trunc(plld_actual_freq/(center_freq_desired-1.5*tone_spacing),-12)+pow(2.0,-12); - double div_hi=bit_trunc(plld_actual_freq/(center_freq_desired+1.5*tone_spacing),-12); - if (floor(div_lo)!=floor(div_hi)) { - center_freq_actual=plld_actual_freq/floor(div_lo)-1.6*tone_spacing; - std::stringstream temp; - temp << std::setprecision(6) << std::fixed << " Warning: center frequency has been changed to " << center_freq_actual/1e6 << " MHz" << std::endl; - std::cout << temp.str(); - std::cout << " because of hardware limitations!" << std::endl; - } - // Create DMA table of tuning words. WSPR tone i will use entries 2*i and - // 2*i+1 to generate the appropriate tone. - double tone0_freq=center_freq_actual-1.5*tone_spacing; - std::vector tuning_word(1024); - for (int i=0;i<8;i++) { - double tone_freq=tone0_freq+(i>>1)*tone_spacing; - double div=bit_trunc(plld_actual_freq/tone_freq,-12); - if (i%2==0) { - div=div+pow(2.0,-12); - } - tuning_word[i]=((int)(div*pow(2.0,12))); - } - // Fill the remaining table, just in case... - for (int i=8;i<1024;i++) { - double div=500+i; - tuning_word[i]=((int)(div*pow(2.0,12))); - } - - // Program the table - dma_table_freq.resize(1024); - for (int i=0;i<1024;i++) { - dma_table_freq[i]=plld_actual_freq/(tuning_word[i]/pow(2.0,12)); - ((int*)(constPage.v))[i] = (0x5a<<24)+tuning_word[i]; - if ((i%2==0)&&(i<8)) { - assert((tuning_word[i]&(~0xfff))==(tuning_word[i+1]&(~0xfff))); - } - } - -} - -// Create the memory structures needed by the DMA engine and perform initial -// clock configuration. -void setupDMA( - struct PageInfo & constPage, - struct PageInfo & instrPage, - struct PageInfo instrs[] -){ - allocMemPool(1025); - - // Allocate a page of ram for the constants - getRealMemPageFromPool(&constPage.v, &constPage.b); - - // Create 1024 instructions allocating one page at a time. - // Even instructions target the GP0 Clock divider - // Odd instructions target the PWM FIFO - int instrCnt = 0; - while (instrCnt<1024) { - // Allocate a page of ram for the instructions - getRealMemPageFromPool(&instrPage.v, &instrPage.b); - - // make copy instructions - // Only create as many instructions as will fit in the recently - // allocated page. If not enough space for all instructions, the - // next loop will allocate another page. - struct CB* instr0= (struct CB*)instrPage.v; - int i; - for (i=0; i<(signed)(4096/sizeof(struct CB)); i++) { - instrs[instrCnt].v = (void*)((long int)instrPage.v + sizeof(struct CB)*i); - instrs[instrCnt].b = (void*)((long int)instrPage.b + sizeof(struct CB)*i); - instr0->SOURCE_AD = (unsigned long int)constPage.b+2048; - instr0->DEST_AD = PWM_BUS_BASE+0x18 /* FIF1 */; - instr0->TXFR_LEN = 4; - instr0->STRIDE = 0; - //instr0->NEXTCONBK = (int)instrPage.b + sizeof(struct CB)*(i+1); - instr0->TI = (1/* DREQ */<<6) | (5 /* PWM */<<16) | (1<<26/* no wide*/) ; - instr0->RES1 = 0; - instr0->RES2 = 0; - - // Shouldn't this be (instrCnt%2) ??? - if (i%2) { - instr0->DEST_AD = CM_GP0DIV_BUS; - instr0->STRIDE = 4; - instr0->TI = (1<<26/* no wide*/) ; - } - - if (instrCnt!=0) ((struct CB*)(instrs[instrCnt-1].v))->NEXTCONBK = (long int)instrs[instrCnt].b; - instr0++; - instrCnt++; - } - } - // Create a circular linked list of instructions - ((struct CB*)(instrs[1023].v))->NEXTCONBK = (long int)instrs[0].b; - - // set up a clock for the PWM - ACCESS_BUS_ADDR(CLK_BUS_BASE + 40*4 /*PWMCLK_CNTL*/) = 0x5A000026; // Source=PLLD and disable - usleep(1000); - //ACCESS_BUS_ADDR(CLK_BUS_BASE + 41*4 /*PWMCLK_DIV*/) = 0x5A002800; - ACCESS_BUS_ADDR(CLK_BUS_BASE + 41*4 /*PWMCLK_DIV*/) = 0x5A002000; // set PWM div to 2, for 250MHz - ACCESS_BUS_ADDR(CLK_BUS_BASE + 40*4 /*PWMCLK_CNTL*/) = 0x5A000016; // Source=PLLD and enable - usleep(1000); - - // set up pwm - ACCESS_BUS_ADDR(PWM_BUS_BASE + 0x0 /* CTRL*/) = 0; - usleep(1000); - ACCESS_BUS_ADDR(PWM_BUS_BASE + 0x4 /* status*/) = -1; // clear errors - usleep(1000); - // Range should default to 32, but it is set at 2048 after reset on my RPi. - ACCESS_BUS_ADDR(PWM_BUS_BASE + 0x10)=32; - ACCESS_BUS_ADDR(PWM_BUS_BASE + 0x20)=32; - ACCESS_BUS_ADDR(PWM_BUS_BASE + 0x0 /* CTRL*/) = -1; //(1<<13 /* Use fifo */) | (1<<10 /* repeat */) | (1<<9 /* serializer */) | (1<<8 /* enable ch */) ; - usleep(1000); - ACCESS_BUS_ADDR(PWM_BUS_BASE + 0x8 /* DMAC*/) = (1<<31 /* DMA enable */) | 0x0707; - - //activate dma - struct DMAregs* DMA0 = (struct DMAregs*)&(ACCESS_BUS_ADDR(DMA_BUS_BASE)); - DMA0->CS =1<<31; // reset - DMA0->CONBLK_AD=0; - DMA0->TI=0; - DMA0->CONBLK_AD = (unsigned long int)(instrPage.b); - DMA0->CS =(1<<0)|(255 <<16); // enable bit = 0, clear end flag = 1, prio=19-16 -} // Convert string to uppercase void to_upper( @@ -1055,21 +599,11 @@ void timeval_print(struct timeval *tv) { printf("%s.%03ld", buffer, (tv->tv_usec+500)/1000); } -// Create the mbox special files and open mbox. -void open_mbox() { - mbox.handle = mbox_open(); - if (mbox.handle < 0) { - std::cerr << "Failed to open mailbox." << std::endl; - ABORT(-1); - } -} // Called when exiting or when a signal is received. void cleanup() { - disable_clock(); - unSetupDMA(); - deallocMemPool(); - unlink(LOCAL_DEVICE_FILE_NAME); + if(clk!=NULL) {delete clk;clk=NULL;} + if(ngfmtest!=NULL) {delete ngfmtest;ngfmtest=NULL;} } // Called when a signal is received. Automatically calls cleanup(). @@ -1079,43 +613,7 @@ void cleanupAndExit(int sig) { ABORT(-1); } -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 = pthread_setschedparam(pthread_self(), SCHED_FIFO, &sp); - if (ret) { - std::cerr << "Warning: pthread_setschedparam (increase thread priority) returned non-zero: " << ret << std::endl; - } -} -// Create the memory map between virtual memory and the peripheral range -// of physical memory. -void setup_peri_base_virt( - volatile unsigned * & peri_base_virt -) { - int mem_fd; - // open /dev/mem - if ((mem_fd = open("/dev/mem", O_RDWR|O_SYNC) ) < 0) { - std::cerr << "Error: can't open /dev/mem" << std::endl; - ABORT (-1); - } - peri_base_virt = (unsigned *)mmap( - NULL, - 0x01000000, //len - PROT_READ|PROT_WRITE, - MAP_SHARED, - mem_fd, - PERI_BASE_PHYS //base - ); - if ((long int)peri_base_virt==-1) { - std::cerr << "Error: peri_base_virt mmap error!" << std::endl; - ABORT(-1); - } - close(mem_fd); -} int main(const int argc, char * const argv[]) { //catch all signals (like ctrl+c, ctrl+z, ...) to ensure DMA is disabled @@ -1126,17 +624,9 @@ int main(const int argc, char * const argv[]) { sigaction(i, &sa, NULL); } atexit(cleanup); - setSchedPriority(30); -#ifdef RPI1 - std::cout << "Detected Raspberry Pi version 1" << std::endl; -#else -#ifdef RPI23 - std::cout << "Detected Raspberry Pi version 2/3" << std::endl; -#else -#error "RPI version macro is not defined" -#endif -#endif + + // Initialize the RNG srand(time(NULL)); @@ -1172,18 +662,13 @@ int main(const int argc, char * const argv[]) { ); int nbands=center_freq_set.size(); - // Initial configuration - struct PageInfo constPage; - struct PageInfo instrPage; - struct PageInfo instrs[1024]; - setup_peri_base_virt(peri_base_virt); - // Set up DMA - open_mbox(); - txon(); - setupDMA(constPage,instrPage,instrs); - txoff(); - + + + if (mode==TONE) { + if(clk==NULL) + clk=new clkgpio; + clk->SetAdvancedPllMode(true); // Test tone mode... double wspr_symtime = WSPR_SYMTIME; double tone_spacing=1.0/wspr_symtime; @@ -1195,31 +680,15 @@ int main(const int argc, char * const argv[]) { txon(); int bufPtr=0; - std::vector dma_table_freq; + // Set to non-zero value to ensure setupDMATab is called at least once. double ppm_prev=123456; double center_freq_actual; - while (true) { - if (self_cal) { - update_ppm(ppm); - } - if (ppm!=ppm_prev) { - setupDMATab(test_tone+1.5*tone_spacing,tone_spacing,F_PLLD_CLK*(1-ppm/1e6),dma_table_freq,center_freq_actual,constPage); - //cout << std::setprecision(30) << dma_table_freq[0] << std::endl; - //cout << std::setprecision(30) << dma_table_freq[1] << std::endl; - //cout << std::setprecision(30) << dma_table_freq[2] << std::endl; - //cout << std::setprecision(30) << dma_table_freq[3] << std::endl; - if (center_freq_actual!=test_tone+1.5*tone_spacing) { - std::cout << " Warning: because of hardware limitations, test tone will be transmitted on" << std::endl; - std::stringstream temp; - temp << std::setprecision(6) << std::fixed << " frequency: " << (center_freq_actual-1.5*tone_spacing)/1e6 << " MHz" << std::endl; - std::cout << temp.str(); - } - ppm_prev=ppm; - } - txSym(0, center_freq_actual, tone_spacing, 60, dma_table_freq, F_PWM_CLK_INIT, instrs, constPage, bufPtr); - } - + //SetTone + clk->SetCenterFrequency(test_tone,100); + clk->enableclk(4); + clk->SetFrequency(000); + while(true) usleep(1000000); // Should never get here... } else { @@ -1281,7 +750,8 @@ int main(const int argc, char * const argv[]) { std::vector dma_table_freq; double center_freq_actual; if (center_freq_desired) { - setupDMATab(center_freq_desired,tone_spacing,F_PLLD_CLK*(1-ppm/1e6),dma_table_freq,center_freq_actual,constPage); + center_freq_actual=center_freq_desired; + } else { center_freq_actual=center_freq_desired; } @@ -1299,24 +769,43 @@ int main(const int argc, char * const argv[]) { struct timeval sym_start; struct timeval diff; int bufPtr=0; - txon(); - for (int i = 0; i < 162; i++) { - gettimeofday(&sym_start,NULL); - timeval_subtract(&diff, &sym_start, &tvBegin); - double elapsed=diff.tv_sec+diff.tv_usec/1e6; - //elapsed=(i)*wspr_symtime; - double sched_end=(i+1)*wspr_symtime; - //cout << "symbol " << i << " " << wspr_symtime << std::endl; - //cout << sched_end-elapsed << std::endl; - double this_sym=sched_end-elapsed; - this_sym=(this_sym<.2)?.2:this_sym; - this_sym=(this_sym>2*wspr_symtime)?2*wspr_symtime:this_sym; - txSym(symbols[i], center_freq_actual, tone_spacing, sched_end-elapsed, dma_table_freq, F_PWM_CLK_INIT, instrs, constPage, bufPtr); - } + int SR=100*1/wspr_symtime; + int FifoSize=1000; + if(ngfmtest==NULL) + ngfmtest=new ngfmdmasync(center_freq_actual,SR,14,FifoSize); + + + + + for (int i = 0; i < 162; i++) + { + double tone_freq=-1.5*tone_spacing+symbols[i]*tone_spacing; + int Nbtx=0; + while(Nbtx<100) + { + usleep(100); + int Available=ngfmtest->GetBufferAvailable(); + if(Available>FifoSize/2) + { + int Index=ngfmtest->GetUserMemIndex(); + if(Available>100-Nbtx) Available=100-Nbtx; + //printf("GetIndex=%d\n",Index); + for(int j=0;j5000)?1000:0); + ngfmtest->SetFrequencySample(Index+j,tone_freq/*+(rand()/((double)RAND_MAX)-.5)*8.0*/); + Nbtx++; + + + } + } + + } + } n_tx++; // Turn transmitter off - txoff(); + ngfmtest->stop(); // End timestamp gettimeofday(&tvEnd, NULL);