From 96276615b234e82dbb636d152d28eaa0be5d22b6 Mon Sep 17 00:00:00 2001 From: F5OEO Date: Mon, 19 Mar 2018 14:30:55 +0000 Subject: [PATCH 1/9] 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 +#include "librpitx/src/librpitx.h" -#ifdef __cplusplus -extern "C" { -#include "mailbox.h" -} -#endif /* __cplusplus */ +clkgpio *clk=NULL; +ngfmdmasync *ngfmtest=NULL; -// 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. #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); From 3c91027b44a20ef459893c21017240c71f7ee1e5 Mon Sep 17 00:00:00 2001 From: F5OEO Date: Mon, 19 Mar 2018 15:36:00 +0100 Subject: [PATCH 2/9] Update Readme for using librpitx --- README | 22 ++++++---------------- 1 file changed, 6 insertions(+), 16 deletions(-) diff --git a/README b/README index c0424d9..4e6286c 100644 --- a/README +++ b/README @@ -7,22 +7,19 @@ port to Antenna (and LPF), operates on LF, MF, HF and VHF bands from Compatible with the original Raspberry Pi, the Raspberry Pi 2/3, and the Pi Zero. -!!!!!! -2017-04-21 -Do note that some users have been reporting lockups with recent OS versions. -I have not been able to reproduce the problems on my RPI1 and RPI3 running -the latest Jessie-Lite. -https://github.com/JamesP6000/WsprryPi/issues/6#issuecomment-296233932 -!!!!!! ****** Installation / update: ****** Download and compile code: sudo apt-get install git - git clone https://github.com/JamesP6000/WsprryPi.git + git clone https://github.com/F5OEO/WsprryPi.git cd WsprryPi + git clone https://github.com/F5OEO/librpitx.git + cd librpitx/src make + cd ../../ + make Install to /usr/local/bin: sudo make install @@ -176,14 +173,6 @@ Calibration: set correction by specifying --test-tone 780000 --ppm on the command line and confirming that the Pi is still zero beating the AM station. -****** -PWM Peripheral: -****** - The code uses the RPi PWM peripheral to time the frequency transitions - of the output clock. This peripheral is also used by the RPi sound system - and hence any sound events that occur during a WSPR transmission will - interfere with WSPR transmissions. Sound can be permanently disabled - by editing /etc/modules and commenting out the snd-bcm2835 device. ****** Example usage: @@ -248,6 +237,7 @@ Credits: Retzler AndrĂ¡s (HA7ILM) for the massive changes that were required to incorporate the mailbox code so that the RPi2 and RPi3 could be supported. + Evariste COURJAUD F5OEO : adapt to use librpitx and simplify code [1] PiFM code from http://www.icrobotics.co.uk/wiki/index.php/Turning_the_Raspberry_Pi_Into_an_FM_Transmitter [2] Original WSPR Pi transmitter code by Dan: From 739e27efa523e42f6ffc0c71aa1be4df83e87fa0 Mon Sep 17 00:00:00 2001 From: F5OEO Date: Wed, 4 Apr 2018 07:04:47 +0000 Subject: [PATCH 3/9] Work with 2m --- wspr.cpp | 26 ++++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/wspr.cpp b/wspr.cpp index a75e7c0..94a2b8f 100644 --- a/wspr.cpp +++ b/wspr.cpp @@ -466,6 +466,8 @@ void parse_commandline( parsed_freq=70092500.0; } else if (!strcasecmp(argv[optind],"2m")) { parsed_freq=144490500.0; + } else if (!strcasecmp(argv[optind],"70cm")) { + parsed_freq=432300500.0; } else { // Not a string. See if it can be parsed as a double. char * endp; @@ -774,13 +776,26 @@ int main(const int argc, char * const argv[]) { if(ngfmtest==NULL) ngfmtest=new ngfmdmasync(center_freq_actual,SR,14,FifoSize); - - + double FreqResolution=ngfmtest->GetFrequencyResolution(); + + double RealFreq=ngfmtest->GetRealFrequency(0); + if(FreqResolution>tone_spacing) + { + fprintf(stderr,"Freq resolution=%f - Tone spacing =%f Erreur tuning=%f\n",FreqResolution,tone_spacing,RealFreq); + + } for (int i = 0; i < 162; i++) { - double tone_freq=-1.5*tone_spacing+symbols[i]*tone_spacing; + double tone_freq=-1.5*tone_spacing+symbols[i]*tone_spacing-RealFreq; int Nbtx=0; + + int Frac=ngfmtest->GetMasterFrac(0); + int IntFreq=floor(tone_freq/FreqResolution); + double ToneFreqInf=tone_freq-IntFreq; + int Step=ToneFreqInf*100.0/FreqResolution; + + while(Nbtx<100) { usleep(100); @@ -793,7 +808,10 @@ int main(const int argc, char * const argv[]) { for(int j=0;j5000)?1000:0); - ngfmtest->SetFrequencySample(Index+j,tone_freq/*+(rand()/((double)RAND_MAX)-.5)*8.0*/); + double pwmtone=(Nbtx>abs(Step))?(IntFreq*FreqResolution):((IntFreq+1)*FreqResolution); + //fprintf(stderr,"Frac %d IntFreq %d step %d tone= %f pwm %f\n",Frac,IntFreq,Step,tone_freq,pwmtone); + ngfmtest->SetFrequencySample(Index+j,pwmtone); + //ngfmtest->SetFrequencySample(Index+j,tone_freq); Nbtx++; From ab005a6b3afa130c74d9f8fa8aa265ba22e54e55 Mon Sep 17 00:00:00 2001 From: F5OEO Date: Thu, 8 Nov 2018 23:03:54 +0000 Subject: [PATCH 4/9] Fix repeat issue --- wspr.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/wspr.cpp b/wspr.cpp index 94a2b8f..d0f227e 100644 --- a/wspr.cpp +++ b/wspr.cpp @@ -759,7 +759,7 @@ int main(const int argc, char * const argv[]) { } // Send the message! - //cout << "TX started!" << std::endl; + //std::cout << "TX started!" << std::endl; if (center_freq_actual){ // Print a status message right before transmission begins. struct timeval tvBegin, tvEnd, tvDiff; @@ -775,7 +775,8 @@ int main(const int argc, char * const argv[]) { int FifoSize=1000; if(ngfmtest==NULL) ngfmtest=new ngfmdmasync(center_freq_actual,SR,14,FifoSize); - + else + ngfmtest->enableclk(4); double FreqResolution=ngfmtest->GetFrequencyResolution(); double RealFreq=ngfmtest->GetRealFrequency(0); @@ -823,7 +824,7 @@ int main(const int argc, char * const argv[]) { n_tx++; // Turn transmitter off - ngfmtest->stop(); + ngfmtest->disableclk(4); // End timestamp gettimeofday(&tvEnd, NULL); From a4727b43e49533c0a1b0fbcf038e2f63b5542f8c Mon Sep 17 00:00:00 2001 From: F5OEO Date: Fri, 9 Nov 2018 00:10:05 +0100 Subject: [PATCH 5/9] Warning about librpitx --- README | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README b/README index 4e6286c..883156d 100644 --- a/README +++ b/README @@ -7,6 +7,9 @@ port to Antenna (and LPF), operates on LF, MF, HF and VHF bands from Compatible with the original Raspberry Pi, the Raspberry Pi 2/3, and the Pi Zero. +This version uses https://github.com/F5OEO/librpitx +**You need to add gpu_freq=250 in /boot/config.txt. Without that transmiting is unstable" + ****** Installation / update: From b7987d7ccd88ff5c26204dd3a90eff6c43be2f49 Mon Sep 17 00:00:00 2001 From: F5OEO Date: Thu, 8 Nov 2018 23:10:58 +0000 Subject: [PATCH 6/9] Cosmectic --- README => README.md | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename README => README.md (100%) diff --git a/README b/README.md similarity index 100% rename from README rename to README.md From 332c94e40f966b9a4b78388f022408d4194a0df3 Mon Sep 17 00:00:00 2001 From: F5OEO Date: Fri, 9 Nov 2018 00:14:06 +0100 Subject: [PATCH 7/9] Readme cosmetics --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 883156d..1cd5963 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -Raspberry Pi bareback LF/MF/HF/VHF WSPR transmitter +# Raspberry Pi bareback LF/MF/HF/VHF WSPR transmitter Makes a very simple WSPR beacon from your RasberryPi by connecting GPIO port to Antenna (and LPF), operates on LF, MF, HF and VHF bands from @@ -8,7 +8,7 @@ Compatible with the original Raspberry Pi, the Raspberry Pi 2/3, and the Pi Zero. This version uses https://github.com/F5OEO/librpitx -**You need to add gpu_freq=250 in /boot/config.txt. Without that transmiting is unstable" +**You need to add gpu_freq=250 in /boot/config.txt. Without that transmiting is unstable"** ****** From bc4a1e3f9735d1f44bedec3c1c88155d527e4b3a Mon Sep 17 00:00:00 2001 From: F5OEO Date: Fri, 9 Nov 2018 11:11:17 +0000 Subject: [PATCH 8/9] Use PWM (allowing i2s peripheral), move to simplified method for sending samples --- wspr.cpp | 54 ++++++++++++++++++++++++++++-------------------------- 1 file changed, 28 insertions(+), 26 deletions(-) diff --git a/wspr.cpp b/wspr.cpp index d0f227e..ca23ce5 100644 --- a/wspr.cpp +++ b/wspr.cpp @@ -771,10 +771,16 @@ int main(const int argc, char * const argv[]) { struct timeval sym_start; struct timeval diff; int bufPtr=0; - int SR=100*1/wspr_symtime; - int FifoSize=1000; + int Upsample=10000; + int SR=Upsample*1/wspr_symtime; + int FifoSize=40000; + bool usePWMSample=false; + static float *FreqPWM=NULL; if(ngfmtest==NULL) - ngfmtest=new ngfmdmasync(center_freq_actual,SR,14,FifoSize); + { + ngfmtest=new ngfmdmasync(center_freq_actual,SR,14,FifoSize,true); + FreqPWM=(float*)malloc(Upsample*sizeof(float)); + } else ngfmtest->enableclk(4); double FreqResolution=ngfmtest->GetFrequencyResolution(); @@ -783,6 +789,7 @@ int main(const int argc, char * const argv[]) { if(FreqResolution>tone_spacing) { fprintf(stderr,"Freq resolution=%f - Tone spacing =%f Erreur tuning=%f\n",FreqResolution,tone_spacing,RealFreq); + usePWMSample=true; } @@ -790,36 +797,30 @@ int main(const int argc, char * const argv[]) { { double tone_freq=-1.5*tone_spacing+symbols[i]*tone_spacing-RealFreq; int Nbtx=0; - + int f1=0; int Frac=ngfmtest->GetMasterFrac(0); int IntFreq=floor(tone_freq/FreqResolution); double ToneFreqInf=tone_freq-IntFreq; - int Step=ToneFreqInf*100.0/FreqResolution; + int Step=ToneFreqInf*Upsample/FreqResolution; - - while(Nbtx<100) + if(!usePWMSample) { - 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); - double pwmtone=(Nbtx>abs(Step))?(IntFreq*FreqResolution):((IntFreq+1)*FreqResolution); - //fprintf(stderr,"Frac %d IntFreq %d step %d tone= %f pwm %f\n",Frac,IntFreq,Step,tone_freq,pwmtone); - ngfmtest->SetFrequencySample(Index+j,pwmtone); - //ngfmtest->SetFrequencySample(Index+j,tone_freq); - Nbtx++; - - - } + for(int j=0;jSetFrequencySamples(FreqPWM,Upsample); - } } n_tx++; @@ -844,6 +845,7 @@ int main(const int argc, char * const argv[]) { break; } if ((terminate>0)&&(n_tx>=terminate)) { + break; } From 58a6fc6375f5f3a54ee5864e6c06ee80919b2929 Mon Sep 17 00:00:00 2001 From: F5OEO Date: Tue, 24 Jan 2023 12:20:34 +0100 Subject: [PATCH 9/9] Fix multi frequencies issue --- wspr.cpp | 1011 ++++++++++++++++++++++++++++++------------------------ 1 file changed, 563 insertions(+), 448 deletions(-) diff --git a/wspr.cpp b/wspr.cpp index ca23ce5..32658cc 100644 --- a/wspr.cpp +++ b/wspr.cpp @@ -17,6 +17,7 @@ // ha7ilm: added RPi2 support based on a patch to PiFmRds by Cristophe // Jacquet and Richard Hirst: http://git.io/vn7O9 +// F5OEO : adapt to librpitx for cleaner spectrum #include #include @@ -46,40 +47,42 @@ #include #include "librpitx/src/librpitx.h" - -clkgpio *clk=NULL; -ngfmdmasync *ngfmtest=NULL; - +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 -typedef enum {WSPR,TONE} mode_type; +typedef enum +{ + WSPR, + TONE +} mode_type; // WSRP nominal symbol time -#define WSPR_SYMTIME (8192.0/12000.0) +#define WSPR_SYMTIME (8192.0 / 12000.0) // How much random frequency offset should be added to WSPR transmissions // if the --offset option has been turned on. #define WSPR_RAND_OFFSET 80 #define WSPR15_RAND_OFFSET 8 // Disable the PWM clock and wait for it to become 'not busy'. -void disable_clock() { - +void disable_clock() +{ } // Turn on TX -void txon() { - - //ACCESS_BUS_ADDR(PADS_GPIO_0_27_BUS) = 0x5a000018 + 7; //16mA +10.6dBm +void txon() +{ - disable_clock(); + // ACCESS_BUS_ADDR(PADS_GPIO_0_27_BUS) = 0x5a000018 + 7; //16mA +10.6dBm - + disable_clock(); } // Turn transmitter on -void txoff() { +void txoff() +{ disable_clock(); } @@ -90,40 +93,39 @@ void txoff() { // do not know which DMA table entry is being processed by the DMA engine. #define PWM_CLOCKS_PER_ITER_NOMINAL 1000 void txSym( - const int & sym_num, - const double & center_freq, - const double & tone_spacing, - const double & tsym, - const std::vector & dma_table_freq, - const double & f_pwm_clk, - struct PageInfo instrs[], - struct PageInfo & constPage, - int & bufPtr -) { - + const int &sym_num, + const double ¢er_freq, + const double &tone_spacing, + const double &tsym, + const std::vector &dma_table_freq, + const double &f_pwm_clk, + struct PageInfo instrs[], + struct PageInfo &constPage, + int &bufPtr) +{ } // Turn off (reset) DMA engine -void unSetupDMA(){ - +void unSetupDMA() +{ + txoff(); } // Truncate at bit lsb. i.e. set all bits less than lsb to zero. double bit_trunc( - const double & d, - const int & lsb -) { - return floor(d/pow(2.0,lsb))*pow(2.0,lsb); + const double &d, + const int &lsb) +{ + return floor(d / pow(2.0, lsb)) * pow(2.0, lsb); } - - // Convert string to uppercase void to_upper( - char *str -) { - while(*str) { + char *str) +{ + while (*str) + { *str = toupper(*str); str++; } @@ -131,133 +133,157 @@ void to_upper( // Encode call, locator, and dBm into WSPR codeblock. void wspr( - const char* call, - const char* l_pre, - const char* dbm, - unsigned char* symbols -) { + const char *call, + const char *l_pre, + const char *dbm, + unsigned char *symbols) +{ // pack prefix in nadd, call in n1, grid, dbm in n2 - char* c, buf[16]; + char *c, buf[16]; strncpy(buf, call, 16); - c=buf; + c = buf; to_upper(c); - unsigned long ng,nadd=0; - - if(strchr(c, '/')){ //prefix-suffix - nadd=2; - int i=strchr(c, '/')-c; //stroke position - int n=strlen(c)-i-1; //suffix len, prefix-call len - c[i]='\0'; - if(n==1) ng=60000-32768+(c[i+1]>='0'&&c[i+1]<='9'?c[i+1]-'0':c[i+1]==' '?38:c[i+1]-'A'+10); // suffix /A to /Z, /0 to /9 - if(n==2) ng=60000+26+10*(c[i+1]-'0')+(c[i+2]-'0'); // suffix /10 to /99 - if(n>2){ // prefix EA8/, right align - ng=(i<3?36:c[i-3]>='0'&&c[i-3]<='9'?c[i-3]-'0':c[i-3]-'A'+10); - ng=37*ng+(i<2?36:c[i-2]>='0'&&c[i-2]<='9'?c[i-2]-'0':c[i-2]-'A'+10); - ng=37*ng+(i<1?36:c[i-1]>='0'&&c[i-1]<='9'?c[i-1]-'0':c[i-1]-'A'+10); - if(ng<32768) nadd=1; else ng=ng-32768; - c=c+i+1; + unsigned long ng, nadd = 0; + + if (strchr(c, '/')) + { // prefix-suffix + nadd = 2; + int i = strchr(c, '/') - c; // stroke position + int n = strlen(c) - i - 1; // suffix len, prefix-call len + c[i] = '\0'; + if (n == 1) + ng = 60000 - 32768 + (c[i + 1] >= '0' && c[i + 1] <= '9' ? c[i + 1] - '0' : c[i + 1] == ' ' ? 38 + : c[i + 1] - 'A' + 10); // suffix /A to /Z, /0 to /9 + if (n == 2) + ng = 60000 + 26 + 10 * (c[i + 1] - '0') + (c[i + 2] - '0'); // suffix /10 to /99 + if (n > 2) + { // prefix EA8/, right align + ng = (i < 3 ? 36 : c[i - 3] >= '0' && c[i - 3] <= '9' ? c[i - 3] - '0' + : c[i - 3] - 'A' + 10); + ng = 37 * ng + (i < 2 ? 36 : c[i - 2] >= '0' && c[i - 2] <= '9' ? c[i - 2] - '0' + : c[i - 2] - 'A' + 10); + ng = 37 * ng + (i < 1 ? 36 : c[i - 1] >= '0' && c[i - 1] <= '9' ? c[i - 1] - '0' + : c[i - 1] - 'A' + 10); + if (ng < 32768) + nadd = 1; + else + ng = ng - 32768; + c = c + i + 1; } } - int i=(isdigit(c[2])?2:isdigit(c[1])?1:0); //last prefix digit of de-suffixed/de-prefixed callsign - int n=strlen(c)-i-1; //2nd part of call len + int i = (isdigit(c[2]) ? 2 : isdigit(c[1]) ? 1 + : 0); // last prefix digit of de-suffixed/de-prefixed callsign + int n = strlen(c) - i - 1; // 2nd part of call len unsigned long n1; - n1=(i<2?36:c[i-2]>='0'&&c[i-2]<='9'?c[i-2]-'0':c[i-2]-'A'+10); - n1=36*n1+(i<1?36:c[i-1]>='0'&&c[i-1]<='9'?c[i-1]-'0':c[i-1]-'A'+10); - n1=10*n1+c[i]-'0'; - n1=27*n1+(n<1?26:c[i+1]-'A'); - n1=27*n1+(n<2?26:c[i+2]-'A'); - n1=27*n1+(n<3?26:c[i+3]-'A'); - - //if(rand() % 2) nadd=0; - if(!nadd){ + n1 = (i < 2 ? 36 : c[i - 2] >= '0' && c[i - 2] <= '9' ? c[i - 2] - '0' + : c[i - 2] - 'A' + 10); + n1 = 36 * n1 + (i < 1 ? 36 : c[i - 1] >= '0' && c[i - 1] <= '9' ? c[i - 1] - '0' + : c[i - 1] - 'A' + 10); + n1 = 10 * n1 + c[i] - '0'; + n1 = 27 * n1 + (n < 1 ? 26 : c[i + 1] - 'A'); + n1 = 27 * n1 + (n < 2 ? 26 : c[i + 2] - 'A'); + n1 = 27 * n1 + (n < 3 ? 26 : c[i + 3] - 'A'); + + // if(rand() % 2) nadd=0; + if (!nadd) + { // Copy locator locally since it is declared const and we cannot modify // its contents in-place. char l[4]; strncpy(l, l_pre, 4); - to_upper(l); //grid square Maidenhead locator (uppercase) - ng=180*(179-10*(l[0]-'A')-(l[2]-'0'))+10*(l[1]-'A')+(l[3]-'0'); + to_upper(l); // grid square Maidenhead locator (uppercase) + ng = 180 * (179 - 10 * (l[0] - 'A') - (l[2] - '0')) + 10 * (l[1] - 'A') + (l[3] - '0'); } - int p = atoi(dbm); //EIRP in dBm={0,3,7,10,13,17,20,23,27,30,33,37,40,43,47,50,53,57,60} - int corr[]={0,-1,1,0,-1,2,1,0,-1,1}; - p=p>60?60:p<0?0:p+corr[p%10]; - unsigned long n2=(ng<<7)|(p+64+nadd); + int p = atoi(dbm); // EIRP in dBm={0,3,7,10,13,17,20,23,27,30,33,37,40,43,47,50,53,57,60} + int corr[] = {0, -1, 1, 0, -1, 2, 1, 0, -1, 1}; + p = p > 60 ? 60 : p < 0 ? 0 + : p + corr[p % 10]; + unsigned long n2 = (ng << 7) | (p + 64 + nadd); // pack n1,n2,zero-tail into 50 bits char packed[11] = { - static_cast(n1>>20), - static_cast(n1>>12), - static_cast(n1>>4), - static_cast(((n1&0x0f)<<4)|((n2>>18)&0x0f)), - static_cast(n2>>10), - static_cast(n2>>2), - static_cast((n2&0x03)<<6), - 0, - 0, - 0, - 0 - }; + static_cast(n1 >> 20), + static_cast(n1 >> 12), + static_cast(n1 >> 4), + static_cast(((n1 & 0x0f) << 4) | ((n2 >> 18) & 0x0f)), + static_cast(n2 >> 10), + static_cast(n2 >> 2), + static_cast((n2 & 0x03) << 6), + 0, + 0, + 0, + 0}; // convolutional encoding K=32, r=1/2, Layland-Lushbaugh polynomials int k = 0; - int j,s; + int j, s; int nstate = 0; unsigned char symbol[176]; - for(j=0;j!=sizeof(packed);j++){ - for(i=7;i>=0;i--){ - unsigned long poly[2] = { 0xf2d05351L, 0xe4613c47L }; - nstate = (nstate<<1) | ((packed[j]>>i)&1); - for(s=0;s!=2;s++){ //convolve - unsigned long n = nstate & poly[s]; - int even = 0; // even := parity(n) - while(n){ - even = 1 - even; - n = n & (n - 1); - } - symbol[k] = even; - k++; + for (j = 0; j != sizeof(packed); j++) + { + for (i = 7; i >= 0; i--) + { + unsigned long poly[2] = {0xf2d05351L, 0xe4613c47L}; + nstate = (nstate << 1) | ((packed[j] >> i) & 1); + for (s = 0; s != 2; s++) + { // convolve + unsigned long n = nstate & poly[s]; + int even = 0; // even := parity(n) + while (n) + { + even = 1 - even; + n = n & (n - 1); } - } + symbol[k] = even; + k++; + } + } } // interleave symbols const unsigned char npr3[162] = { - 1,1,0,0,0,0,0,0,1,0,0,0,1,1,1,0,0,0,1,0,0,1,0,1,1,1,1,0,0,0,0,0, - 0,0,1,0,0,1,0,1,0,0,0,0,0,0,1,0,1,1,0,0,1,1,0,1,0,0,0,1,1,0,1,0, - 0,0,0,1,1,0,1,0,1,0,1,0,1,0,0,1,0,0,1,0,1,1,0,0,0,1,1,0,1,0,1,0, - 0,0,1,0,0,0,0,0,1,0,0,1,0,0,1,1,1,0,1,1,0,0,1,1,0,1,0,0,0,1,1,1, - 0,0,0,0,0,1,0,1,0,0,1,1,0,0,0,0,0,0,0,1,1,0,1,0,1,1,0,0,0,1,1,0, - 0,0 }; - for(i=0;i!=162;i++){ - // j0 := bit reversed_values_smaller_than_161[i] - unsigned char j0; - p=-1; - for(k=0;p!=i;k++){ - for(j=0;j!=8;j++) // j0:=bit_reverse(k) - j0 = ((k>>j)&1)|(j0<<1); - if(j0<162) - p++; - } - symbols[j0]=npr3[j0]|symbol[i]<<1; //interleave and add sync std::vector + 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, + 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 0, 1, 1, 0, 1, 0, + 0, 0, 0, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 0, 1, 0, 1, 0, + 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1, 1, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 0, 1, 1, 1, + 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 0, + 0, 0}; + for (i = 0; i != 162; i++) + { + // j0 := bit reversed_values_smaller_than_161[i] + unsigned char j0; + p = -1; + for (k = 0; p != i; k++) + { + for (j = 0; j != 8; j++) // j0:=bit_reverse(k) + j0 = ((k >> j) & 1) | (j0 << 1); + if (j0 < 162) + p++; + } + symbols[j0] = npr3[j0] | symbol[i] << 1; // interleave and add sync std::vector } } // Wait for the system clock's minute to reach one second past 'minute' void wait_every( - int minute -) { + int minute) +{ time_t t; - struct tm* ptm; - for(;;){ + struct tm *ptm; + for (;;) + { time(&t); ptm = gmtime(&t); - if((ptm->tm_min % minute) == 0 && ptm->tm_sec == 0) break; + if ((ptm->tm_min % minute) == 0 && ptm->tm_sec == 0) + break; usleep(1000); } usleep(1000000); // wait another second } -void print_usage() { +void print_usage() +{ std::cout << "Usage:" << std::endl; std::cout << " wspr [options] callsign locator tx_pwr_dBm f1 ..." << std::endl; std::cout << " OR" << std::endl; @@ -298,181 +324,229 @@ void print_usage() { } void parse_commandline( - // Inputs - const int & argc, - char * const argv[], - // Outputs - std::string & callsign, - std::string & locator, - std::string & tx_power, - std::vector & center_freq_set, - double & ppm, - bool & self_cal, - bool & repeat, - bool & random_offset, - double & test_tone, - bool & no_delay, - mode_type & mode, - int & terminate -) { + // Inputs + const int &argc, + char *const argv[], + // Outputs + std::string &callsign, + std::string &locator, + std::string &tx_power, + std::vector ¢er_freq_set, + double &ppm, + bool &self_cal, + bool &repeat, + bool &random_offset, + double &test_tone, + bool &no_delay, + mode_type &mode, + int &terminate) +{ // Default values - ppm=0; - self_cal=true; - repeat=false; - random_offset=false; - test_tone=NAN; - no_delay=false; - mode=WSPR; - terminate=-1; + ppm = 0; + self_cal = true; + repeat = false; + random_offset = false; + test_tone = NAN; + no_delay = false; + mode = WSPR; + terminate = -1; static struct option long_options[] = { - {"help", no_argument, 0, 'h'}, - {"ppm", required_argument, 0, 'p'}, - {"self-calibration", no_argument, 0, 's'}, - {"free-running", no_argument, 0, 'f'}, - {"repeat", no_argument, 0, 'r'}, - {"terminate", required_argument, 0, 'x'}, - {"offset", no_argument, 0, 'o'}, - {"test-tone", required_argument, 0, 't'}, - {"no-delay", no_argument, 0, 'n'}, - {0, 0, 0, 0} - }; - - while (true) { + {"help", no_argument, 0, 'h'}, + {"ppm", required_argument, 0, 'p'}, + {"self-calibration", no_argument, 0, 's'}, + {"free-running", no_argument, 0, 'f'}, + {"repeat", no_argument, 0, 'r'}, + {"terminate", required_argument, 0, 'x'}, + {"offset", no_argument, 0, 'o'}, + {"test-tone", required_argument, 0, 't'}, + {"no-delay", no_argument, 0, 'n'}, + {0, 0, 0, 0}}; + + while (true) + { /* getopt_long stores the option index here. */ int option_index = 0; - int c = getopt_long (argc, argv, "hp:sfrx:ot:n", - long_options, &option_index); + int c = getopt_long(argc, argv, "hp:sfrx:ot:n", + long_options, &option_index); if (c == -1) break; - switch (c) { - char * endp; - case 0: - // Code should only get here if a long option was given a non-null - // flag value. - std::cout << "Check code!" << std::endl; + switch (c) + { + char *endp; + case 0: + // Code should only get here if a long option was given a non-null + // flag value. + std::cout << "Check code!" << std::endl; + ABORT(-1); + break; + case 'h': + print_usage(); + ABORT(-1); + break; + case 'p': + ppm = strtod(optarg, &endp); + if ((optarg == endp) || (*endp != '\0')) + { + std::cerr << "Error: could not parse ppm value" << std::endl; ABORT(-1); - break; - case 'h': - print_usage(); + } + break; + case 's': + self_cal = true; + break; + case 'f': + self_cal = false; + break; + case 'r': + repeat = true; + break; + case 'x': + terminate = strtol(optarg, &endp, 10); + if ((optarg == endp) || (*endp != '\0')) + { + std::cerr << "Error: could not parse termination argument" << std::endl; ABORT(-1); - break; - case 'p': - ppm=strtod(optarg,&endp); - if ((optarg==endp)||(*endp!='\0')) { - std::cerr << "Error: could not parse ppm value" << std::endl; - ABORT(-1); - } - break; - case 's': - self_cal=true; - break; - case 'f': - self_cal=false; - break; - case 'r': - repeat=true; - break; - case 'x': - terminate=strtol(optarg,&endp,10); - if ((optarg==endp)||(*endp!='\0')) { - std::cerr << "Error: could not parse termination argument" << std::endl; - ABORT(-1); - } - if (terminate<1) { - std::cerr << "Error: termination parameter must be >= 1" << std::endl; - ABORT(-1); - } - break; - case 'o': - random_offset=true; - break; - case 't': - test_tone=strtod(optarg,&endp); - mode=TONE; - if ((optarg==endp)||(*endp!='\0')) { - std::cerr << "Error: could not parse test tone frequency" << std::endl; - ABORT(-1); - } - break; - case 'n': - no_delay=true; - break; - case '?': - /* getopt_long already printed an error message. */ + } + if (terminate < 1) + { + std::cerr << "Error: termination parameter must be >= 1" << std::endl; ABORT(-1); - default: + } + break; + case 'o': + random_offset = true; + break; + case 't': + test_tone = strtod(optarg, &endp); + mode = TONE; + if ((optarg == endp) || (*endp != '\0')) + { + std::cerr << "Error: could not parse test tone frequency" << std::endl; ABORT(-1); + } + break; + case 'n': + no_delay = true; + break; + case '?': + /* getopt_long already printed an error message. */ + ABORT(-1); + default: + ABORT(-1); } - } // Parse the non-option parameters - unsigned int n_free_args=0; - while (optind0) { + if (terminate > 0) + { temp << " TX will stop after " << terminate << " transmissions." << std::endl; - } else if (repeat) { + } + else if (repeat) + { temp << " Transmissions will continue forever until stopped with CTRL-C" << std::endl; } - if (random_offset) { + if (random_offset) + { temp << " A small random frequency offset will be added to all transmissions" << std::endl; } - if (temp.str().length()) { + if (temp.str().length()) + { std::cout << "Extra options:" << std::endl; std::cout << temp.str(); } std::cout << std::endl; - } else { + } + else + { std::stringstream temp; - temp << std::setprecision(6) << std::fixed << "A test tone will be generated at frequency " << test_tone/1e6 << " MHz" << std::endl; + temp << std::setprecision(6) << std::fixed << "A test tone will be generated at frequency " << test_tone / 1e6 << " MHz" << std::endl; std::cout << temp.str(); - if (self_cal) { + if (self_cal) + { std::cout << "NTP will be used to calibrate the tone frequency" << std::endl; - } else if (ppm) { + } + else if (ppm) + { std::cout << "PPM value to be used to generate the tone: " << ppm << std::endl; } std::cout << std::endl; @@ -554,8 +650,8 @@ void parse_commandline( // Call ntp_adjtime() to obtain the latest calibration coefficient. void update_ppm( - double & ppm -) { + double &ppm) +{ struct timex ntx; int status; double ppm_new; @@ -563,63 +659,79 @@ void update_ppm( ntx.modes = 0; /* only read */ status = ntp_adjtime(&ntx); - if (status != TIME_OK) { - //cerr << "Error: clock not synchronized" << std::endl; - //return; + if (status != TIME_OK) + { + // cerr << "Error: clock not synchronized" << std::endl; + // return; } - ppm_new = (double)ntx.freq/(double)(1 << 16); /* frequency scale */ - if (abs(ppm_new)>200) { + ppm_new = (double)ntx.freq / (double)(1 << 16); /* frequency scale */ + if (abs(ppm_new) > 200) + { std::cerr << "Warning: absolute ppm value is greater than 200 and is being ignored!" << std::endl; - } else { - if (ppm!=ppm_new) { + } + else + { + if (ppm != ppm_new) + { std::cout << " Obtained new ppm value: " << ppm_new << std::endl; } - ppm=ppm_new; + ppm = ppm_new; } } /* Return 1 if the difference is negative, otherwise 0. */ // From StackOverflow: // http://stackoverflow.com/questions/1468596/c-programming-calculate-elapsed-time-in-milliseconds-unix -int timeval_subtract(struct timeval *result, struct timeval *t2, struct timeval *t1) { - long int diff = (t2->tv_usec + 1000000 * t2->tv_sec) - (t1->tv_usec + 1000000 * t1->tv_sec); - result->tv_sec = diff / 1000000; - result->tv_usec = diff % 1000000; +int timeval_subtract(struct timeval *result, struct timeval *t2, struct timeval *t1) +{ + long int diff = (t2->tv_usec + 1000000 * t2->tv_sec) - (t1->tv_usec + 1000000 * t1->tv_sec); + result->tv_sec = diff / 1000000; + result->tv_usec = diff % 1000000; - return (diff<0); + return (diff < 0); } -void timeval_print(struct timeval *tv) { - char buffer[30]; - time_t curtime; +void timeval_print(struct timeval *tv) +{ + char buffer[30]; + time_t curtime; - //printf("%ld.%06ld", tv->tv_sec, tv->tv_usec); - curtime = tv->tv_sec; - //strftime(buffer, 30, "%m-%d-%Y %T", localtime(&curtime)); - strftime(buffer, 30, "UTC %Y-%m-%d %T", gmtime(&curtime)); - printf("%s.%03ld", buffer, (tv->tv_usec+500)/1000); + // printf("%ld.%06ld", tv->tv_sec, tv->tv_usec); + curtime = tv->tv_sec; + // strftime(buffer, 30, "%m-%d-%Y %T", localtime(&curtime)); + strftime(buffer, 30, "UTC %Y-%m-%d %T", gmtime(&curtime)); + printf("%s.%03ld", buffer, (tv->tv_usec + 500) / 1000); } - // Called when exiting or when a signal is received. -void cleanup() { - if(clk!=NULL) {delete clk;clk=NULL;} - if(ngfmtest!=NULL) {delete ngfmtest;ngfmtest=NULL;} +void cleanup() +{ + if (clk != NULL) + { + delete clk; + clk = NULL; + } + if (ngfmtest != NULL) + { + delete ngfmtest; + ngfmtest = NULL; + } } // Called when a signal is received. Automatically calls cleanup(). -void cleanupAndExit(int sig) { +void cleanupAndExit(int sig) +{ std::cerr << "Exiting with error; caught signal: " << sig << std::endl; cleanup(); ABORT(-1); } - - -int main(const int argc, char * const argv[]) { - //catch all signals (like ctrl+c, ctrl+z, ...) to ensure DMA is disabled - for (int i = 0; i < 64; i++) { +int main(const int argc, char *const argv[]) +{ + // catch all signals (like ctrl+c, ctrl+z, ...) to ensure DMA is disabled + for (int i = 0; i < 64; i++) + { struct sigaction sa; memset(&sa, 0, sizeof(sa)); sa.sa_handler = cleanupAndExit; @@ -627,9 +739,6 @@ int main(const int argc, char * const argv[]) { } atexit(cleanup); - - - // Initialize the RNG srand(time(NULL)); @@ -637,7 +746,7 @@ int main(const int argc, char * const argv[]) { std::string callsign; std::string locator; std::string tx_power; - std::vector center_freq_set; + std::vector center_freq_set; double ppm; bool self_cal; bool repeat; @@ -647,53 +756,52 @@ int main(const int argc, char * const argv[]) { mode_type mode; int terminate; parse_commandline( - argc, - argv, - callsign, - locator, - tx_power, - center_freq_set, - ppm, - self_cal, - repeat, - random_offset, - test_tone, - no_delay, - mode, - terminate - ); - int nbands=center_freq_set.size(); - - - - - if (mode==TONE) { - if(clk==NULL) - clk=new clkgpio; - clk->SetAdvancedPllMode(true); + argc, + argv, + callsign, + locator, + tx_power, + center_freq_set, + ppm, + self_cal, + repeat, + random_offset, + test_tone, + no_delay, + mode, + terminate); + int nbands = center_freq_set.size(); + + 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; + double tone_spacing = 1.0 / wspr_symtime; std::stringstream temp; - temp << std::setprecision(6) << std::fixed << "Transmitting test tone on frequency " << test_tone/1.0e6 << " MHz" << std::endl; + temp << std::setprecision(6) << std::fixed << "Transmitting test tone on frequency " << test_tone / 1.0e6 << " MHz" << std::endl; std::cout << temp.str(); std::cout << "Press CTRL-C to exit!" << std::endl; txon(); - int bufPtr=0; - + int bufPtr = 0; + // Set to non-zero value to ensure setupDMATab is called at least once. - double ppm_prev=123456; + double ppm_prev = 123456; double center_freq_actual; - //SetTone - clk->SetCenterFrequency(test_tone,100); - clk->enableclk(4); - clk->SetFrequency(000); - while(true) usleep(1000000); + // SetTone + clk->SetCenterFrequency(test_tone, 100); + clk->enableclk(4); + clk->SetFrequency(000); + while (true) + usleep(1000000); // Should never get here... - - } else { + } + else + { // WSPR mode // Create WSPR symbols @@ -711,56 +819,65 @@ int main(const int argc, char * const argv[]) { */ std::cout << "Ready to transmit (setup complete)..." << std::endl; - int band=0; - int n_tx=0; - for(;;) { + int band = 0; + int n_tx = 0; + for (;;) + { // Calculate WSPR parameters for this transmission double center_freq_desired; center_freq_desired = center_freq_set[band]; bool wspr15 = - (center_freq_desired > 137600 && center_freq_desired < 137625) || \ - (center_freq_desired > 475800 && center_freq_desired < 475825) || \ - (center_freq_desired > 1838200 && center_freq_desired < 1838225); + (center_freq_desired > 137600 && center_freq_desired < 137625) || + (center_freq_desired > 475800 && center_freq_desired < 475825) || + (center_freq_desired > 1838200 && center_freq_desired < 1838225); double wspr_symtime = (wspr15) ? 8.0 * WSPR_SYMTIME : WSPR_SYMTIME; - double tone_spacing=1.0/wspr_symtime; + double tone_spacing = 1.0 / wspr_symtime; // Add random offset - if ((center_freq_desired!=0)&&random_offset) { - center_freq_desired+=(2.0*rand()/((double)RAND_MAX+1.0)-1.0)*(wspr15?WSPR15_RAND_OFFSET:WSPR_RAND_OFFSET); + if ((center_freq_desired != 0) && random_offset) + { + center_freq_desired += (2.0 * rand() / ((double)RAND_MAX + 1.0) - 1.0) * (wspr15 ? WSPR15_RAND_OFFSET : WSPR_RAND_OFFSET); } // Status message before transmission std::stringstream temp; temp << std::setprecision(6) << std::fixed; - temp << "Desired center frequency for " << (wspr15?"WSPR-15":"WSPR") << " transmission: "<< center_freq_desired/1e6 << " MHz" << std::endl; + temp << "Desired center frequency for " << (wspr15 ? "WSPR-15" : "WSPR") << " transmission: " << center_freq_desired / 1e6 << " MHz" << std::endl; std::cout << temp.str(); // Wait for WSPR transmission window to arrive. - if (no_delay) { + if (no_delay) + { std::cout << " Transmitting immediately (not waiting for WSPR window)" << std::endl; - } else { + } + else + { std::cout << " Waiting for next WSPR transmission window..." << std::endl; wait_every((wspr15) ? 15 : 2); } // Update crystal calibration information - if (self_cal) { + if (self_cal) + { update_ppm(ppm); } // Create the DMA table for this center frequency - std::vector dma_table_freq; + std::vector dma_table_freq; double center_freq_actual; - if (center_freq_desired) { - center_freq_actual=center_freq_desired; - - } else { - center_freq_actual=center_freq_desired; + if (center_freq_desired) + { + center_freq_actual = center_freq_desired; + } + else + { + center_freq_actual = center_freq_desired; } // Send the message! - //std::cout << "TX started!" << std::endl; - if (center_freq_actual){ + // std::cout << "TX started!" << std::endl; + if (center_freq_actual) + { // Print a status message right before transmission begins. struct timeval tvBegin, tvEnd, tvDiff; gettimeofday(&tvBegin, NULL); @@ -770,88 +887,86 @@ int main(const int argc, char * const argv[]) { struct timeval sym_start; struct timeval diff; - int bufPtr=0; - int Upsample=10000; - int SR=Upsample*1/wspr_symtime; - int FifoSize=40000; - bool usePWMSample=false; - static float *FreqPWM=NULL; - if(ngfmtest==NULL) - { - ngfmtest=new ngfmdmasync(center_freq_actual,SR,14,FifoSize,true); - FreqPWM=(float*)malloc(Upsample*sizeof(float)); - } - else - ngfmtest->enableclk(4); - double FreqResolution=ngfmtest->GetFrequencyResolution(); - - double RealFreq=ngfmtest->GetRealFrequency(0); - if(FreqResolution>tone_spacing) - { - fprintf(stderr,"Freq resolution=%f - Tone spacing =%f Erreur tuning=%f\n",FreqResolution,tone_spacing,RealFreq); - usePWMSample=true; - - } - + int bufPtr = 0; + int Upsample = 10000; + int SR = Upsample * 1 / wspr_symtime; + int FifoSize = 40000; + bool usePWMSample = false; + static float *FreqPWM = NULL; + + //New modulator and tx on + ngfmtest = new ngfmdmasync(center_freq_actual, SR, 14, FifoSize, true); + FreqPWM = (float *)malloc(Upsample * sizeof(float)); + + double FreqResolution = ngfmtest->GetFrequencyResolution(); + + double RealFreq = ngfmtest->GetRealFrequency(0); + if (FreqResolution > tone_spacing) + { + fprintf(stderr, "Freq resolution=%f - Tone spacing =%f Erreur tuning=%f\n", FreqResolution, tone_spacing, RealFreq); + usePWMSample = true; + } + for (int i = 0; i < 162; i++) - { - double tone_freq=-1.5*tone_spacing+symbols[i]*tone_spacing-RealFreq; - int Nbtx=0; - int f1=0; - int Frac=ngfmtest->GetMasterFrac(0); - int IntFreq=floor(tone_freq/FreqResolution); - double ToneFreqInf=tone_freq-IntFreq; - int Step=ToneFreqInf*Upsample/FreqResolution; - - if(!usePWMSample) - { - for(int j=0;jSetFrequencySamples(FreqPWM,Upsample); - - } + { + double tone_freq = -1.5 * tone_spacing + symbols[i] * tone_spacing - RealFreq; + int Nbtx = 0; + int f1 = 0; + int Frac = ngfmtest->GetMasterFrac(0); + int IntFreq = floor(tone_freq / FreqResolution); + double ToneFreqInf = tone_freq - IntFreq; + int Step = ToneFreqInf * Upsample / FreqResolution; + + if (!usePWMSample) + { + for (int j = 0; j < Upsample; j++) + { + FreqPWM[j] = tone_freq; + } + } + else + { + // Todo : Implement PWMFrequency to obtain better frequency resolution + for (int j = 0; j < Upsample; j++) + { + FreqPWM[j] = tone_freq; + } + } + ngfmtest->SetFrequencySamples(FreqPWM, Upsample); + } n_tx++; // Turn transmitter off ngfmtest->disableclk(4); - + delete ngfmtest; + ngfmtest = NULL; + free(FreqPWM); // End timestamp gettimeofday(&tvEnd, NULL); std::cout << " TX ended at: "; timeval_print(&tvEnd); timeval_subtract(&tvDiff, &tvEnd, &tvBegin); - printf(" (%ld.%03ld s)\n", tvDiff.tv_sec, (tvDiff.tv_usec+500)/1000); - - } else { + printf(" (%ld.%03ld s)\n", tvDiff.tv_sec, (tvDiff.tv_usec + 500) / 1000); + } + else + { std::cout << " Skipping transmission" << std::endl; usleep(1000000); } // Advance to next band - band=(band+1)%nbands; - if ((band==0)&&!repeat) { + band = (band + 1) % nbands; + if ((band == 0) && !repeat) + { break; } - if ((terminate>0)&&(n_tx>=terminate)) { - + if ((terminate > 0) && (n_tx >= terminate)) + { + break; } - } } return 0; } -