From 67ca9cfcdfefdc048acaf27603ad615e55517362 Mon Sep 17 00:00:00 2001 From: James Peroulas Date: Sun, 26 Feb 2017 16:35:33 +0000 Subject: [PATCH] Lots more comments. Defaults to NTP calibration. --- .gitignore | 2 +- wspr.cpp | 551 ++++++++++++++++++++++++++++------------------------- 2 files changed, 291 insertions(+), 262 deletions(-) diff --git a/.gitignore b/.gitignore index 55005c7..4b59f9b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,3 @@ wspr gpioclk - +mailbox.o diff --git a/wspr.cpp b/wspr.cpp index e6d43c2..126b94d 100644 --- a/wspr.cpp +++ b/wspr.cpp @@ -46,20 +46,20 @@ #include "mailbox.h" -// Note on accessing memory in RPi +// Note on accessing memory in RPi: // -// There are 3 types of addresses in the 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. +// 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 +// 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 +// 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 @@ -67,7 +67,7 @@ // 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 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)" @@ -83,17 +83,17 @@ // write to the GPIO addresses in physical memory. // // Accessing RAM from DMA engine -// The DMA enginer 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 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 calculations are performed in this program to figure -// out how to access them with virtual addresses. +// 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 @@ -138,7 +138,7 @@ // 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. +// 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; @@ -168,6 +168,7 @@ volatile unsigned *peri_base_virt = NULL; typedef enum {WSPR,TONE} mode_type; +// Structure used to control clock generator struct GPCTL { char SRC : 4; char ENAB : 1; @@ -180,6 +181,7 @@ struct GPCTL { char PASSWD : 8; }; +// Structure used to tell the DMA engine what to do struct CB { volatile unsigned int TI; volatile unsigned int SOURCE_AD; @@ -191,6 +193,7 @@ struct CB { volatile unsigned int RES2; }; +// DMA engine status registers struct DMAregs { volatile unsigned int CS; volatile unsigned int CONBLK_AD; @@ -203,6 +206,7 @@ struct DMAregs { volatile unsigned int DEBUG; }; +// Virtual and bus addresses of a page of physical memory. struct PageInfo { void* b; // bus address void* v; // virtual address @@ -210,50 +214,58 @@ struct PageInfo { // Must be global so that exit handlers can access this. static struct { - int handle; /* From mbox_open() */ - unsigned mem_ref = 0; /* From mem_alloc() */ - unsigned bus_addr; /* From mem_lock() */ - unsigned char *virt_addr = NULL; /* From mapmem() */ //ha7ilm: originally uint8_t - unsigned pool_size; - unsigned pool_cnt; + int handle; /* From mbox_open() */ + unsigned mem_ref = 0; /* From mem_alloc() */ + unsigned bus_addr; /* From mem_lock() */ + unsigned char *virt_addr = NULL; /* From mapmem() */ //ha7ilm: originally uint8_t + unsigned pool_size; + unsigned pool_cnt; } mbox; -void allocMemPool(unsigned numpages) -{ - mbox.mem_ref=mem_alloc(mbox.handle, 4096*numpages, 4096, MEM_FLAG); - mbox.bus_addr = mem_lock(mbox.handle, mbox.mem_ref); - mbox.virt_addr = (unsigned char*)mapmem(BUS_TO_PHYS(mbox.bus_addr), 4096*numpages); - mbox.pool_size=numpages; - mbox.pool_cnt=0; - //printf("allocMemoryPool bus_addr=%x virt_addr=%x mem_ref=%x\n",mbox.bus_addr,(unsigned)mbox.virt_addr,mbox.mem_ref); +// Use the mbox interface to allocate a single chunk of memory to hold +// all the pages we will need. The bus address and the virtual address +// are saved in the mbox structure. +void allocMemPool(unsigned numpages) { + // Allocate space. + mbox.mem_ref = mem_alloc(mbox.handle, 4096*numpages, 4096, MEM_FLAG); + // Lock down the allocated space and return its bus address. + mbox.bus_addr = mem_lock(mbox.handle, mbox.mem_ref); + // Conert the bus address to a physical address and map this to virtual + // (aka user) space. + mbox.virt_addr = (unsigned char*)mapmem(BUS_TO_PHYS(mbox.bus_addr), 4096*numpages); + // The number of pages in the pool. Never changes! + mbox.pool_size=numpages; + // How many of the created pages have actually been used. + mbox.pool_cnt=0; + //printf("allocMemoryPool bus_addr=%x virt_addr=%x mem_ref=%x\n",mbox.bus_addr,(unsigned)mbox.virt_addr,mbox.mem_ref); } // Returns the virtual and bus address (NOT physical address!) of another // page in the pool. -void getRealMemPageFromPool(void ** vAddr, void **bAddr) -{ - if (mbox.pool_cnt>=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++; +void getRealMemPageFromPool(void ** vAddr, void **bAddr) { + if (mbox.pool_cnt>=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++; } -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); - } +// 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() { // Disable the clock (in case it's already running) by reading current // settings and only clearing the enable bit. @@ -270,8 +282,8 @@ void disable_clock() { } } -void txon() -{ +// Turn on TX +void txon() { // Set function select for GPIO4. // Fsel 000 => input // Fsel 001 => output @@ -308,8 +320,8 @@ void txon() ACCESS_BUS_ADDR(CM_GP0CTL_BUS) = *((int*)&setupword); } -void txoff() -{ +// 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(); @@ -391,6 +403,7 @@ void txSym( //printf("",(unsigned)instrs[bufPtr].v,(unsigned)instrs[bufPtr].b); } +// Turn off (reset) DMA engine void unSetupDMA(){ //cout << "Exiting!" << std::endl; struct DMAregs* DMA0 = (struct DMAregs*)&(ACCESS_BUS_ADDR(DMA_BUS_BASE)); @@ -398,10 +411,7 @@ void unSetupDMA(){ txoff(); } -void handSig(const int h) { - exit(0); -} - +// Truncate at bit lsb. i.e. set all bits less than lsb to zero. double bit_trunc( const double & d, const int & lsb @@ -461,207 +471,215 @@ void setupDMATab( } +// 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); + allocMemPool(1025); - // Allocate a page of ram for the constants - getRealMemPageFromPool(&constPage.v, &constPage.b); + // 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); + // 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; + // 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*/) ; - } + // 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; + 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 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; + // 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 + //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(char *str) -{ while(*str) - { - *str = toupper(*str); - str++; - } +void to_upper( + char *str +) { + while(*str) { + *str = toupper(*str); + str++; + } } // Encode call, locator, and dBm into WSPR codeblock. -void wspr(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]; - strncpy(buf, call, 16); - c=buf; - to_upper(c); - unsigned long ng,nadd=0; +void wspr( + 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]; + strncpy(buf, call, 16); + 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; + 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 + 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){ + // 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'); + } + 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 + }; + + // convolutional encoding K=32, r=1/2, Layland-Lushbaugh polynomials + int k = 0; + 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++; + } } - } + } - 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){ - // 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'); - } - 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 - }; - - // convolutional encoding K=32, r=1/2, Layland-Lushbaugh polynomials - int k = 0; - 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++; - } - } - } - - // 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 - } + // 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 + } } // Wait for the system clock's minute to reach one second past 'minute' -void wait_every(int minute) -{ +void wait_every( + int minute +) { time_t t; struct tm* ptm; for(;;){ @@ -685,8 +703,10 @@ void print_usage() { std::cout << " -p --ppm ppm" << std::endl; std::cout << " Known PPM correction to 19.2MHz RPi nominal crystal frequency." << std::endl; std::cout << " -s --self-calibration" << std::endl; - std::cout << " Call ntp_adjtime() before every transmission to obtain the PPM error of the" << std::endl; - std::cout << " crystal." << std::endl; + std::cout << " Check NTP before every transmission to obtain the PPM error of the" << std::endl; + std::cout << " crystal (default setting!)." << std::endl; + std::cout << " -f --free-running" << std::endl; + std::cout << " Do not use NTP to correct frequency error of RPi crystal." << std::endl; std::cout << " -r --repeat" << std::endl; std::cout << " Repeatedly, and in order, transmit on all the specified command line freqs." << std::endl; std::cout << " -x --terminate " << std::endl; @@ -731,7 +751,7 @@ void parse_commandline( ) { // Default values ppm=0; - self_cal=false; + self_cal=true; repeat=false; random_offset=false; test_tone=NAN; @@ -743,6 +763,7 @@ void parse_commandline( {"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'}, @@ -751,10 +772,10 @@ void parse_commandline( {0, 0, 0, 0} }; - while (1) { + while (true) { /* getopt_long stores the option index here. */ int option_index = 0; - int c = getopt_long (argc, argv, "hp:srx:ot:n", + int c = getopt_long (argc, argv, "hp:sfrx:ot:n", long_options, &option_index); if (c == -1) break; @@ -781,6 +802,9 @@ void parse_commandline( case 's': self_cal=true; break; + case 'f': + self_cal=false; + break; case 'r': repeat=true; break; @@ -889,7 +913,6 @@ void parse_commandline( center_freq_set.push_back(parsed_freq); } - // Check consistency among command line options. if (ppm&&self_cal) { std::cout << "Warning: ppm value is being ignored!" << std::endl; @@ -927,8 +950,7 @@ void parse_commandline( std::cout << temp.str(); temp.str(""); if (self_cal) { - temp << " ntp_adjtime() will be used to peridocially calibrate the transmission" << std::endl; - temp << " frequency" << std::endl; + temp << " NTP will be used to peridocially calibrate the transmission frequency" << std::endl; } else if (ppm) { temp << " PPM value to be used for all transmissions: " << ppm << std::endl; } @@ -950,7 +972,7 @@ void parse_commandline( 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) { - std::cout << "ntp_adjtime() will be used to calibrate the tone" << std::endl; + std::cout << "NTP will be used to calibrate the tone frequency" << std::endl; } else if (ppm) { std::cout << "PPM value to be used to generate the tone: " << ppm << std::endl; } @@ -1007,6 +1029,7 @@ 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() { unlink(DEVICE_FILE_NAME); unlink(LOCAL_DEVICE_FILE_NAME); @@ -1021,6 +1044,7 @@ void open_mbox() { } } +// Called when exiting or when a signal is received. void cleanup() { disable_clock(); unSetupDMA(); @@ -1029,23 +1053,27 @@ void cleanup() { unlink(LOCAL_DEVICE_FILE_NAME); } +// Called when a signal is received. Automatically calls cleanup(). void cleanupAndExit(int sig) { + std::cerr << "Exiting with error; caught signal: " << sig << std::endl; cleanup(); - printf("Exiting with error; caught signal: %i\n", sig); - exit(1); + 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. + //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) { - printf("Warning: pthread_setschedparam (increase thread priority) returned non-zero: %i\n", 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 ) { @@ -1056,15 +1084,15 @@ void setup_peri_base_virt( ABORT (-1); } peri_base_virt = (unsigned *)mmap( - NULL, - 0x002FFFFF, //len - PROT_READ|PROT_WRITE, - MAP_SHARED, - mem_fd, - PERI_BASE_PHYS //base - ); + 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: mmap error!" << std::endl; + std::cerr << "Error: peri_base_virt mmap error!" << std::endl; ABORT(-1); } close(mem_fd); @@ -1122,13 +1150,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(); - struct PageInfo constPage; - struct PageInfo instrPage; - struct PageInfo instrs[1024]; setupDMA(constPage,instrPage,instrs); txoff(); @@ -1267,6 +1295,7 @@ int main(const int argc, char * const argv[]) { // Turn transmitter off txoff(); + // End timestamp gettimeofday(&tvEnd, NULL); std::cout << " TX ended at: "; timeval_print(&tvEnd);