From 7b77d0a7d229cdcf5a5f010ff68612305840ac0c Mon Sep 17 00:00:00 2001 From: James Peroulas Date: Sun, 12 Feb 2017 16:02:26 -0800 Subject: [PATCH 01/16] Creating branch to freshen up the code. --- wspr.cpp | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/wspr.cpp b/wspr.cpp index 7798024..66677db 100644 --- a/wspr.cpp +++ b/wspr.cpp @@ -726,21 +726,6 @@ void print_usage() { cout << "Transmission gaps can be created by specifying a TX frequency of 0" << endl; } -// From StackOverflow: -// http://stackoverflow.com/questions/478898/how-to-execute-a-command-and-get-output-of-command-within-c -std::string exec(const char * cmd) { - FILE* pipe = popen(cmd, "r"); - if (!pipe) return "ERROR"; - char buffer[128]; - std::string result = ""; - while (!feof(pipe)) { - if (fgets(buffer, 128, pipe) != NULL) - result += buffer; - } - pclose(pipe); - return result; -} - void parse_commandline( // Inputs const int & argc, From 1822377a543dcc015d308bb7e00317fec6d0736f Mon Sep 17 00:00:00 2001 From: James Peroulas Date: Sun, 12 Feb 2017 16:39:18 -0800 Subject: [PATCH 02/16] Fixed some compile time warnings. Cosmetic changes (tabs to spaces). --- gpioclk.cpp | 6 +---- wspr.cpp | 73 +++++++++++++++++++++++++++++------------------------ 2 files changed, 41 insertions(+), 38 deletions(-) diff --git a/gpioclk.cpp b/gpioclk.cpp index 077f4c2..7391d7b 100644 --- a/gpioclk.cpp +++ b/gpioclk.cpp @@ -49,17 +49,13 @@ using namespace std; #define F_XTAL (19200000.0) #define F_PLLD_CLK (500000000.0) -//Now we autodetect it via makefile. +// Choose proper base address depending on RPI1/RPI2 setting from makefile. #ifdef RPI2 - #define BCM2708_PERI_BASE 0x3f000000 #pragma message "Raspberry Pi 2 detected." - #else - #define BCM2708_PERI_BASE 0x20000000 #pragma message "Raspberry Pi 1 detected." - #endif #define GPIO_BASE (BCM2708_PERI_BASE + 0x200000) /* GPIO controller */ diff --git a/wspr.cpp b/wspr.cpp index 66677db..64d9dfa 100644 --- a/wspr.cpp +++ b/wspr.cpp @@ -69,19 +69,15 @@ using namespace std; #define WSPR_RAND_OFFSET 80 #define WSPR15_RAND_OFFSET 8 -//Now we autodetect it via makefile. +// Choose proper base address depending on RPI1/RPI2 setting from makefile. #ifdef RPI2 - #define BCM2708_PERI_BASE 0x3f000000 #define MEM_FLAG 0x04 #pragma message "Raspberry Pi 2 detected." - #else - #define BCM2708_PERI_BASE 0x20000000 #define MEM_FLAG 0x0c #pragma message "Raspberry Pi 1 detected." - #endif #define PAGE_SIZE (4*1024) @@ -105,7 +101,7 @@ volatile unsigned *allof7e = NULL; #define CLRBIT(base, bit) ACCESS(base) &= ~(1<=mbox.pool_size) return 1; - unsigned offset = mbox.pool_cnt*4096; - *vAddr = (void*)(((unsigned)mbox.virt_addr) + offset); - *pAddr = (void*)(((unsigned)mbox.bus_addr) + offset); - //printf("getRealMemoryPageFromPool bus_addr=%x virt_addr=%x\n", (unsigned)*pAddr,(unsigned)*vAddr); - mbox.pool_cnt++; - return 0; + if(mbox.pool_cnt>=mbox.pool_size) return 1; + unsigned offset = mbox.pool_cnt*4096; + *vAddr = (void*)(((unsigned)mbox.virt_addr) + offset); + *pAddr = (void*)(((unsigned)mbox.bus_addr) + offset); + //printf("getRealMemoryPageFromPool bus_addr=%x virt_addr=%x\n", (unsigned)*pAddr,(unsigned)*vAddr); + mbox.pool_cnt++; + return 0; } void deallocMemPool() { - if(mbox.virt_addr) //it will be 0 by default as in .bss - { - unmapmem(mbox.virt_addr, mbox.pool_size*4096); - mem_unlock(mbox.handle, mbox.mem_ref); - mem_free(mbox.handle, mbox.mem_ref); - } + if(mbox.virt_addr) //it will be 0 by default as in .bss + { + unmapmem(mbox.virt_addr, mbox.pool_size*4096); + mem_unlock(mbox.handle, mbox.mem_ref); + mem_free(mbox.handle, mbox.mem_ref); + } } void txon() @@ -628,8 +624,19 @@ void wspr(const char* call, const char* l_pre, const char* dbm, unsigned char* s unsigned long n2=(ng<<7)|(p+64+nadd); // pack n1,n2,zero-tail into 50 bits - char packed[11] = {n1>>20, n1>>12, n1>>4, ((n1&0x0f)<<4)|((n2>>18)&0x0f), -n2>>10, n2>>2, (n2&0x03)<<6, 0, 0, 0, 0}; + 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; @@ -1069,7 +1076,7 @@ int main(const int argc, char * const argv[]) { int mem_fd; char *gpio_mem, *gpio_map; volatile unsigned *gpio = NULL; - + setup_io(mem_fd,gpio_mem,gpio_map,gpio); setup_gpios(gpio); allof7e = (unsigned *)mmap( @@ -1103,7 +1110,7 @@ int main(const int argc, char * const argv[]) { cout << "Press CTRL-C to exit!" << endl; txon(); - int bufPtr=0; + int bufPtr=0; vector dma_table_freq; // Set to non-zero value to ensure setupDMATab is called at least once. double ppm_prev=123456; From 6e0b8f85d6ca300ed277e7b33bdb0d0df121aba8 Mon Sep 17 00:00:00 2001 From: James Peroulas Date: Mon, 13 Feb 2017 20:30:28 -0800 Subject: [PATCH 03/16] More cosmetic changes. --- README | 11 ++++-- wspr.cpp | 109 +++++++++++++++++++------------------------------------ 2 files changed, 45 insertions(+), 75 deletions(-) diff --git a/README b/README index 45a4a1c..afadad7 100644 --- a/README +++ b/README @@ -4,7 +4,7 @@ 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 0 to 250 MHz. -It is now compatible with both the original Raspberry Pi and the Raspberry Pi 2. +Compatible with the original Raspberry Pi and the Raspberry Pi 2/3. ****** Installation / update: @@ -36,7 +36,8 @@ Usage: (WSPR --help output): -p --ppm ppm Known PPM correction to 19.2MHz RPi nominal crystal frequency. -s --self-calibration - Call ntp_adjtime() before every transmission to obtain the PPM error of the xtal. + Call ntp_adjtime() before every transmission to obtain the PPM error + of the xtal. -r --repeat Repeatedly, and in order, transmit on all the specified freqs. -x --terminate @@ -59,7 +60,7 @@ Usage: (WSPR --help output): -15 indicates the WSPR-15 region of band . Transmission gaps can be created by specifying a TX frequency of 0 - + Note that 'callsign', 'locator', and 'tx_power_dBm' are simply used to fill in the appropriate fields of the WSPR message. Normally, tx_power_dBm should be 10, representing the signal power coming out of the Pi. Set this value @@ -171,7 +172,7 @@ Example usage: sudo ./wspr --self-calibration N9NNN EM10 33 20m Transmit a WSPR transmission slightly off-center on 30m every 10 minutes for - a total of 7 transmissions, and using a fixed PPM correction value. sudo + a total of 7 transmissions, and using a fixed PPM correction value. sudo ./wspr --repeat --terminate 7 --ppm 43.17 N9NNN EM10 33 10140210 0 0 0 0 Transmit repeatedly on 40m, use NTP based frequency offset calibration, @@ -213,6 +214,8 @@ Credits: Michael Tatarinov for adding a patch to get PPM info directly from the kernel. + Retzler András (HA7ILM) added support for the RPi2/3. + [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: diff --git a/wspr.cpp b/wspr.cpp index 64d9dfa..3ffc169 100644 --- a/wspr.cpp +++ b/wspr.cpp @@ -18,7 +18,8 @@ License: */ -//ha7ilm: added RPi2 support based on a patch to PiFmRds by Cristophe Jacquet and Richard Hirst: http://git.io/vn7O9 +// ha7ilm: added RPi2 support based on a patch to PiFmRds by Cristophe +// Jacquet and Richard Hirst: http://git.io/vn7O9 #include #include @@ -89,21 +90,20 @@ volatile unsigned *allof7e = NULL; // GPIO setup macros. Always use INP_GPIO(x) before using OUT_GPIO(x) or SET_GPIO_ALT(x,y) #define INP_GPIO(g) *(gpio+((g)/10)) &= ~(7<<(((g)%10)*3)) -#define OUT_GPIO(g) *(gpio+((g)/10)) |= (1<<(((g)%10)*3)) -#define SET_GPIO_ALT(g,a) *(gpio+(((g)/10))) |= (((a)<=3?(a)+4:(a)==4?3:2)<<(((g)%10)*3)) +//#define OUT_GPIO(g) *(gpio+((g)/10)) |= (1<<(((g)%10)*3)) +//#define SET_GPIO_ALT(g,a) *(gpio+(((g)/10))) |= (((a)<=3?(a)+4:(a)==4?3:2)<<(((g)%10)*3)) -#define GPIO_SET *(gpio+7) // sets bits which are 1 ignores bits which are 0 -#define GPIO_CLR *(gpio+10) // clears bits which are 1 ignores bits which are 0 -#define GPIO_GET *(gpio+13) // sets bits which are 1 ignores bits which are 0 +//#define GPIO_SET *(gpio+7) // sets bits which are 1 ignores bits which are 0 +//#define GPIO_CLR *(gpio+10) // clears bits which are 1 ignores bits which are 0 +//#define GPIO_GET *(gpio+13) // sets bits which are 1 ignores bits which are 0 #define ACCESS(base) *(volatile int*)((long int)allof7e+base-0x7e000000) #define SETBIT(base, bit) ACCESS(base) |= 1<=mbox.pool_size) return 1; + if (mbox.pool_cnt>=mbox.pool_size) { + cerr << "Error: unable to allocated more pages!" << endl; + ABORT(-1); + } unsigned offset = mbox.pool_cnt*4096; *vAddr = (void*)(((unsigned)mbox.virt_addr) + offset); *pAddr = (void*)(((unsigned)mbox.bus_addr) + offset); //printf("getRealMemoryPageFromPool bus_addr=%x virt_addr=%x\n", (unsigned)*pAddr,(unsigned)*vAddr); mbox.pool_cnt++; - return 0; } void deallocMemPool() @@ -338,7 +301,7 @@ void txSym( } void unSetupDMA(){ - printf("exiting\n"); + //cout << "Exiting!" << endl; struct DMAregs* DMA0 = (struct DMAregs*)&(ACCESS(DMA_PHYS_BASE)); DMA0->CS =1<<31; // reset dma controller txoff(); @@ -506,16 +469,16 @@ void setup_io( ) { /* open /dev/mem */ if ((mem_fd = open("/dev/mem", O_RDWR|O_SYNC) ) < 0) { - printf("can't open /dev/mem \n"); - exit (-1); + cerr << "Error: can't open /dev/mem" << endl; + ABORT (-1); } /* mmap GPIO */ // Allocate MAP block if ((gpio_mem = (char *)malloc(BLOCK_SIZE + (PAGE_SIZE-1))) == NULL) { - printf("allocation error \n"); - exit (-1); + cerr << "Error: allocation error" << endl; + ABORT (-1); } // Make sure pointer is on 4K boundary @@ -533,14 +496,13 @@ void setup_io( ); if ((long)gpio_map < 0) { - printf("mmap error %ld\n", (long int)gpio_map); - exit (-1); + cerr << "mmap error" << (long int)gpio_map << endl; + ABORT (-1); } // Always use volatile pointer! gpio = (volatile unsigned *)gpio_map; - } void setup_gpios( @@ -1028,13 +990,18 @@ void timeval_print(struct timeval *tv) { printf("%s.%03ld", buffer, (tv->tv_usec+500)/1000); } -int open_mbox() +void open_mbox() { unlink(DEVICE_FILE_NAME); - if (mknod(DEVICE_FILE_NAME, S_IFCHR|0600, makedev(100, 0)) < 0) { printf("Failed to create mailbox device.\n"); return 1; } + if (mknod(DEVICE_FILE_NAME, S_IFCHR|0600, makedev(100, 0)) < 0) { + cerr << "Failed to create mailbox device." << endl; + ABORT(-1); + } mbox.handle = mbox_open(); - if (mbox.handle < 0) { printf("Failed to open mailbox.\n"); return 1; } - return 0; + if (mbox.handle < 0) { + cerr << "Failed to open mailbox." << endl; + ABORT(-1); + } } int main(const int argc, char * const argv[]) { @@ -1091,7 +1058,7 @@ int main(const int argc, char * const argv[]) { cerr << "Error: mmap error!" << endl; ABORT(-1); } - if (open_mbox()) return 1; + open_mbox(); txon(); struct PageInfo constPage; struct PageInfo instrPage; @@ -1155,7 +1122,7 @@ int main(const int argc, char * const argv[]) { printf("\n"); */ - printf("Ready to transmit (setup complete)...\n"); + cout << "Ready to transmit (setup complete)..." << endl; int band=0; int n_tx=0; for(;;) { @@ -1184,7 +1151,7 @@ int main(const int argc, char * const argv[]) { if (no_delay) { cout << " Transmitting immediately (not waiting for WSPR window)" << endl; } else { - printf(" Waiting for next WSPR transmission window...\n"); + cout << " Waiting for next WSPR transmission window..." << endl; wait_every((wspr15) ? 15 : 2); } From 1e3547e180ec3b6ff342f326f323447259628f60 Mon Sep 17 00:00:00 2001 From: James Peroulas Date: Wed, 15 Feb 2017 21:13:35 -0800 Subject: [PATCH 04/16] Further spring cleaning. Removed some messages from mailbox.c. --- gpioclk.cpp | 2 +- mailbox.c | 6 +++--- mailbox.h | 2 +- makefile | 4 ++-- wspr.cpp | 17 +++++++++++++---- 5 files changed, 20 insertions(+), 11 deletions(-) diff --git a/gpioclk.cpp b/gpioclk.cpp index 7391d7b..915e062 100644 --- a/gpioclk.cpp +++ b/gpioclk.cpp @@ -52,7 +52,7 @@ using namespace std; // Choose proper base address depending on RPI1/RPI2 setting from makefile. #ifdef RPI2 #define BCM2708_PERI_BASE 0x3f000000 -#pragma message "Raspberry Pi 2 detected." +#pragma message "Raspberry Pi 2/3 detected." #else #define BCM2708_PERI_BASE 0x20000000 #pragma message "Raspberry Pi 1 detected." diff --git a/mailbox.c b/mailbox.c index a04b38d..8336eba 100644 --- a/mailbox.c +++ b/mailbox.c @@ -247,7 +247,7 @@ int mbox_open() { // Open a char device file used for communicating with kernel mbox driver. file_desc = open(DEVICE_FILE_NAME, 0); if(file_desc >= 0) { - printf("Using mbox device " DEVICE_FILE_NAME ".\n"); + //printf("Using mbox device " DEVICE_FILE_NAME ".\n"); return file_desc; } @@ -255,14 +255,14 @@ int mbox_open() { unlink(LOCAL_DEVICE_FILE_NAME); if(mknod(LOCAL_DEVICE_FILE_NAME, S_IFCHR|0600, makedev(MAJOR_NUM_A, 0)) >= 0 && (file_desc = open(LOCAL_DEVICE_FILE_NAME, 0)) >= 0) { - printf("Using local mbox device file with major %d.\n", MAJOR_NUM_A); + //printf("Using local mbox device file with major %d.\n", MAJOR_NUM_A); return file_desc; } unlink(LOCAL_DEVICE_FILE_NAME); if(mknod(LOCAL_DEVICE_FILE_NAME, S_IFCHR|0600, makedev(MAJOR_NUM_B, 0)) >= 0 && (file_desc = open(LOCAL_DEVICE_FILE_NAME, 0)) >= 0) { - printf("Using local mbox device file with major %d.\n", MAJOR_NUM_B); + //printf("Using local mbox device file with major %d.\n", MAJOR_NUM_B); return file_desc; } diff --git a/mailbox.h b/mailbox.h index 80bacb0..d1e49f2 100644 --- a/mailbox.h +++ b/mailbox.h @@ -30,7 +30,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #define MAJOR_NUM_B 100 #define IOCTL_MBOX_PROPERTY _IOWR(MAJOR_NUM_B, 0, char *) #define DEVICE_FILE_NAME "/dev/vcio" -#define LOCAL_DEVICE_FILE_NAME "mbox" +#define LOCAL_DEVICE_FILE_NAME "/tmp/mbox" int mbox_open(); void mbox_close(int file_desc); diff --git a/makefile b/makefile index dac7964..da3fafc 100644 --- a/makefile +++ b/makefile @@ -5,10 +5,10 @@ pi_version_flag = $(if $(call archis,armv7,dummy-text),-DRPI2,-DRPI1) all: wspr gpioclk -mailbox.o: +mailbox.o: mailbox.c mailbox.h g++ -c -Wall -lm mailbox.c -wspr: mailbox.o wspr.cpp +wspr: mailbox.o wspr.cpp mailbox.h g++ -Wall -lm $(pi_version_flag) mailbox.o wspr.cpp -owspr gpioclk: gpioclk.cpp diff --git a/wspr.cpp b/wspr.cpp index 3ffc169..e0f121e 100644 --- a/wspr.cpp +++ b/wspr.cpp @@ -74,7 +74,7 @@ using namespace std; #ifdef RPI2 #define BCM2708_PERI_BASE 0x3f000000 #define MEM_FLAG 0x04 -#pragma message "Raspberry Pi 2 detected." +#pragma message "Raspberry Pi 2/3 detected." #else #define BCM2708_PERI_BASE 0x20000000 #define MEM_FLAG 0x0c @@ -670,7 +670,8 @@ void print_usage() { cout << " -p --ppm ppm" << endl; cout << " Known PPM correction to 19.2MHz RPi nominal crystal frequency." << endl; cout << " -s --self-calibration" << endl; - cout << " Call ntp_adjtime() before every transmission to obtain the PPM error of the crystal." << endl; + cout << " Call ntp_adjtime() before every transmission to obtain the PPM error of the" << endl; + cout << " crystal." << endl; cout << " -r --repeat" << endl; cout << " Repeatedly, and in order, transmit on all the specified command line freqs." << endl; cout << " -x --terminate " << endl; @@ -680,7 +681,7 @@ void print_usage() { cout << " +/- " << WSPR_RAND_OFFSET << " Hz for WSPR" << endl; cout << " +/- " << WSPR15_RAND_OFFSET << " Hz for WSPR-15" << endl; cout << " -t --test-tone freq" << endl; - cout << " Simply output a test tone and the specified frequency. Only used" << endl; + cout << " Simply output a test tone at the specified frequency. Only used" << endl; cout << " for debugging and to verify calibration." << endl; cout << " -n --no-delay" << endl; cout << " Transmit immediately, do not wait for a WSPR TX window. Used" << endl; @@ -911,7 +912,8 @@ void parse_commandline( cout << temp.str(); temp.str(""); if (self_cal) { - temp << " ntp_adjtime() will be used to peridocially calibrate the transmission frequency" << endl; + temp << " ntp_adjtime() will be used to peridocially calibrate the transmission" << endl; + temp << " frequency" << endl; } else if (ppm) { temp << " PPM value to be used for all transmissions: " << ppm << endl; } @@ -1004,7 +1006,14 @@ void open_mbox() } } +void unlinkmbox() { + unlink(DEVICE_FILE_NAME); + unlink(LOCAL_DEVICE_FILE_NAME); +}; + int main(const int argc, char * const argv[]) { + atexit(unlinkmbox); + // Initialize the RNG srand(time(NULL)); From c58449ad77dd3ce18c9ffa5694bd5d27ac289324 Mon Sep 17 00:00:00 2001 From: James Peroulas Date: Fri, 17 Feb 2017 15:32:29 +0000 Subject: [PATCH 05/16] RPI3 does not need 2.5ppm correction. --- wspr.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/wspr.cpp b/wspr.cpp index e0f121e..2150c1f 100644 --- a/wspr.cpp +++ b/wspr.cpp @@ -56,7 +56,12 @@ using namespace std; // There seems to be a 2.5ppm offset between the NTP measured frequency // error and the frequency error measured by a frequency counter. This fixed // PPM offset is compensated for here. +// This PPM correction is not needed for RPI2/3. +#ifdef RPI2 +#define F_PLLD_CLK (500000000.0*(1-0.000e-6)) +#else #define F_PLLD_CLK (500000000.0*(1-2.500e-6)) +#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 From dc960fbc0f180c912bb752cf422041e29dd95619 Mon Sep 17 00:00:00 2001 From: James Peroulas Date: Wed, 22 Feb 2017 07:14:00 +0000 Subject: [PATCH 06/16] Removed using namespace std. --- gpioclk.cpp | 10 +- makefile | 4 +- wspr.cpp | 300 ++++++++++++++++++++++++++-------------------------- 3 files changed, 162 insertions(+), 152 deletions(-) diff --git a/gpioclk.cpp b/gpioclk.cpp index 915e062..f457a58 100644 --- a/gpioclk.cpp +++ b/gpioclk.cpp @@ -52,10 +52,10 @@ using namespace std; // Choose proper base address depending on RPI1/RPI2 setting from makefile. #ifdef RPI2 #define BCM2708_PERI_BASE 0x3f000000 -#pragma message "Raspberry Pi 2/3 detected." +//#pragma message "Raspberry Pi 2/3 detected." #else #define BCM2708_PERI_BASE 0x20000000 -#pragma message "Raspberry Pi 1 detected." +//#pragma message "Raspberry Pi 1 detected." #endif #define GPIO_BASE (BCM2708_PERI_BASE + 0x200000) /* GPIO controller */ @@ -347,6 +347,12 @@ void parse_commandline( } int main(const int argc, char * const argv[]) { +#ifdef RPI1 + std::cout << "Detected Raspberry Pi version 1" << std::endl; +#else + std::cout << "Detected Raspberry Pi version 2/3" << std::endl; +#endif + // Parse arguments source_t source; bool freq_specified; diff --git a/makefile b/makefile index da3fafc..53f7911 100644 --- a/makefile +++ b/makefile @@ -9,10 +9,10 @@ mailbox.o: mailbox.c mailbox.h g++ -c -Wall -lm mailbox.c wspr: mailbox.o wspr.cpp mailbox.h - g++ -Wall -lm $(pi_version_flag) mailbox.o wspr.cpp -owspr + g++ -D_GLIBCXX_DEBUG -std=c++11 -Wall -Werror -fmax-errors=5 -lm $(pi_version_flag) mailbox.o wspr.cpp -owspr gpioclk: gpioclk.cpp - g++ -Wall -lm $(pi_version_flag) gpioclk.cpp -ogpioclk + g++ -D_GLIBCXX_DEBUG -std=c++11 -Wall -Werror -fmax-errors=5 -lm $(pi_version_flag) gpioclk.cpp -ogpioclk clean: -rm gpioclk diff --git a/wspr.cpp b/wspr.cpp index 2150c1f..04be074 100644 --- a/wspr.cpp +++ b/wspr.cpp @@ -46,8 +46,6 @@ License: #include "mailbox.h" -using namespace std; - #define ABORT(a) exit(a) // Used for debugging #define MARK std::cout << "Currently in file: " << __FILE__ << " line: " << __LINE__ << std::endl @@ -79,11 +77,11 @@ using namespace std; #ifdef RPI2 #define BCM2708_PERI_BASE 0x3f000000 #define MEM_FLAG 0x04 -#pragma message "Raspberry Pi 2/3 detected." +//#pragma message "Raspberry Pi 2/3 detected." #else #define BCM2708_PERI_BASE 0x20000000 #define MEM_FLAG 0x0c -#pragma message "Raspberry Pi 1 detected." +//#pragma message "Raspberry Pi 1 detected." #endif #define PAGE_SIZE (4*1024) @@ -183,7 +181,7 @@ void allocMemPool(unsigned numpages) void getRealMemPageFromPool(void ** vAddr, void **pAddr) { if (mbox.pool_cnt>=mbox.pool_size) { - cerr << "Error: unable to allocated more pages!" << endl; + std::cerr << "Error: unable to allocated more pages!" << std::endl; ABORT(-1); } unsigned offset = mbox.pool_cnt*4096; @@ -240,7 +238,7 @@ void txSym( const double & center_freq, const double & tone_spacing, const double & tsym, - const vector & dma_table_freq, + const std::vector & dma_table_freq, const double & f_pwm_clk, struct PageInfo instrs[], struct PageInfo & constPage, @@ -254,7 +252,7 @@ void txSym( // 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 << endl; + //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); @@ -306,7 +304,7 @@ void txSym( } void unSetupDMA(){ - //cout << "Exiting!" << endl; + //cout << "Exiting!" << std::endl; struct DMAregs* DMA0 = (struct DMAregs*)&(ACCESS(DMA_PHYS_BASE)); DMA0->CS =1<<31; // reset dma controller txoff(); @@ -328,7 +326,7 @@ void setupDMATab( const double & center_freq_desired, const double & tone_spacing, const double & plld_actual_freq, - vector & dma_table_freq, + std::vector & dma_table_freq, double & center_freq_actual, struct PageInfo & constPage ){ @@ -339,17 +337,16 @@ void setupDMATab( 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; - stringstream temp; - temp << setprecision(6) << fixed << " Warning: center frequency has been changed to " << center_freq_actual/1e6 << " MHz" << endl; - cout << temp.str(); - cout << " because of hardware limitations!" << endl; + 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. - dma_table_freq.resize(1024); double tone0_freq=center_freq_actual-1.5*tone_spacing; - vector tuning_word(1024); + 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); @@ -365,6 +362,7 @@ void setupDMATab( } // 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]; @@ -464,7 +462,7 @@ void setupDMA( // -// Set up a memory regions to access GPIO +// Set up memory regions to access GPIO // void setup_io( int & mem_fd, @@ -474,7 +472,7 @@ void setup_io( ) { /* open /dev/mem */ if ((mem_fd = open("/dev/mem", O_RDWR|O_SYNC) ) < 0) { - cerr << "Error: can't open /dev/mem" << endl; + std::cerr << "Error: can't open /dev/mem" << std::endl; ABORT (-1); } @@ -482,7 +480,7 @@ void setup_io( // Allocate MAP block if ((gpio_mem = (char *)malloc(BLOCK_SIZE + (PAGE_SIZE-1))) == NULL) { - cerr << "Error: allocation error" << endl; + std::cerr << "Error: allocation error" << std::endl; ABORT (-1); } @@ -501,15 +499,16 @@ void setup_io( ); if ((long)gpio_map < 0) { - cerr << "mmap error" << (long int)gpio_map << endl; + std::cerr << "Error: mmap error" << (long int)gpio_map << std::endl; ABORT (-1); } // Always use volatile pointer! gpio = (volatile unsigned *)gpio_map; - } +// Not sure why this function is needed as this code only uses GPIO4 and +// this function sets gpio 7 through 11 as input... void setup_gpios( volatile unsigned * & gpio ){ @@ -531,7 +530,7 @@ void setup_gpios( } -// Convert string to uppercase +// Convert std::string to uppercase void to_upper(char *str) { while(*str) { @@ -645,7 +644,7 @@ void wspr(const char* call, const char* l_pre, const char* dbm, unsigned char* s if(j0<162) p++; } - symbols[j0]=npr3[j0]|symbol[i]<<1; //interleave and add sync vector + symbols[j0]=npr3[j0]|symbol[i]<<1; //interleave and add sync std::vector } } @@ -664,41 +663,41 @@ void wait_every(int minute) } void print_usage() { - cout << "Usage:" << endl; - cout << " wspr [options] callsign locator tx_pwr_dBm f1 ..." << endl; - cout << " OR" << endl; - cout << " wspr [options] --test-tone f" << endl; - cout << endl; - cout << "Options:" << endl; - cout << " -h --help" << endl; - cout << " Print out this help screen." << endl; - cout << " -p --ppm ppm" << endl; - cout << " Known PPM correction to 19.2MHz RPi nominal crystal frequency." << endl; - cout << " -s --self-calibration" << endl; - cout << " Call ntp_adjtime() before every transmission to obtain the PPM error of the" << endl; - cout << " crystal." << endl; - cout << " -r --repeat" << endl; - cout << " Repeatedly, and in order, transmit on all the specified command line freqs." << endl; - cout << " -x --terminate " << endl; - cout << " Terminate after n transmissions have been completed." << endl; - cout << " -o --offset" << endl; - cout << " Add a random frequency offset to each transmission:" << endl; - cout << " +/- " << WSPR_RAND_OFFSET << " Hz for WSPR" << endl; - cout << " +/- " << WSPR15_RAND_OFFSET << " Hz for WSPR-15" << endl; - cout << " -t --test-tone freq" << endl; - cout << " Simply output a test tone at the specified frequency. Only used" << endl; - cout << " for debugging and to verify calibration." << endl; - cout << " -n --no-delay" << endl; - cout << " Transmit immediately, do not wait for a WSPR TX window. Used" << endl; - cout << " for testing only." << endl; - cout << endl; - cout << "Frequencies can be specified either as an absolute TX carrier frequency, or" << endl; - cout << "using one of the following strings. If a string is used, the transmission" << endl; - cout << "will happen in the middle of the WSPR region of the selected band." << endl; - cout << " LF LF-15 MF MF-15 160m 160m-15 80m 60m 40m 30m 20m 17m 15m 12m 10m 6m 4m 2m" << endl; - cout << "-15 indicates the WSPR-15 region of band ." << endl; - cout << endl; - cout << "Transmission gaps can be created by specifying a TX frequency of 0" << endl; + std::cout << "Usage:" << std::endl; + std::cout << " wspr [options] callsign locator tx_pwr_dBm f1 ..." << std::endl; + std::cout << " OR" << std::endl; + std::cout << " wspr [options] --test-tone f" << std::endl; + std::cout << std::endl; + std::cout << "Options:" << std::endl; + std::cout << " -h --help" << std::endl; + std::cout << " Print out this help screen." << std::endl; + 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 << " -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; + std::cout << " Terminate after n transmissions have been completed." << std::endl; + std::cout << " -o --offset" << std::endl; + std::cout << " Add a random frequency offset to each transmission:" << std::endl; + std::cout << " +/- " << WSPR_RAND_OFFSET << " Hz for WSPR" << std::endl; + std::cout << " +/- " << WSPR15_RAND_OFFSET << " Hz for WSPR-15" << std::endl; + std::cout << " -t --test-tone freq" << std::endl; + std::cout << " Simply output a test tone at the specified frequency. Only used" << std::endl; + std::cout << " for debugging and to verify calibration." << std::endl; + std::cout << " -n --no-delay" << std::endl; + std::cout << " Transmit immediately, do not wait for a WSPR TX window. Used" << std::endl; + std::cout << " for testing only." << std::endl; + std::cout << std::endl; + std::cout << "Frequencies can be specified either as an absolute TX carrier frequency, or" << std::endl; + std::cout << "using one of the following std::strings. If a std::string is used, the transmission" << std::endl; + std::cout << "will happen in the middle of the WSPR region of the selected band." << std::endl; + std::cout << " LF LF-15 MF MF-15 160m 160m-15 80m 60m 40m 30m 20m 17m 15m 12m 10m 6m 4m 2m" << std::endl; + std::cout << "-15 indicates the WSPR-15 region of band ." << std::endl; + std::cout << std::endl; + std::cout << "Transmission gaps can be created by specifying a TX frequency of 0" << std::endl; } void parse_commandline( @@ -706,10 +705,10 @@ void parse_commandline( const int & argc, char * const argv[], // Outputs - string & callsign, - string & locator, - string & tx_power, - vector & center_freq_set, + std::string & callsign, + std::string & locator, + std::string & tx_power, + std::vector & center_freq_set, double & ppm, bool & self_cal, bool & repeat, @@ -754,7 +753,7 @@ void parse_commandline( case 0: // Code should only get here if a long option was given a non-null // flag value. - cout << "Check code!" << endl; + std::cout << "Check code!" << std::endl; ABORT(-1); break; case 'h': @@ -764,7 +763,7 @@ void parse_commandline( case 'p': ppm=strtod(optarg,&endp); if ((optarg==endp)||(*endp!='\0')) { - cerr << "Error: could not parse ppm value" << endl; + std::cerr << "Error: could not parse ppm value" << std::endl; ABORT(-1); } break; @@ -777,11 +776,11 @@ void parse_commandline( case 'x': terminate=strtol(optarg,&endp,10); if ((optarg==endp)||(*endp!='\0')) { - cerr << "Error: could not parse termination argument" << endl; + std::cerr << "Error: could not parse termination argument" << std::endl; ABORT(-1); } if (terminate<1) { - cerr << "Error: termination parameter must be >= 1" << endl; + std::cerr << "Error: termination parameter must be >= 1" << std::endl; ABORT(-1); } break; @@ -792,7 +791,7 @@ void parse_commandline( test_tone=strtod(optarg,&endp); mode=TONE; if ((optarg==endp)||(*endp!='\0')) { - cerr << "Error: could not parse test tone frequency" << endl; + std::cerr << "Error: could not parse test tone frequency" << std::endl; ABORT(-1); } break; @@ -828,7 +827,7 @@ void parse_commandline( continue; } // Must be a frequency - // First see if it is a string. + // First see if it is a std::string. double parsed_freq; if (!strcmp(argv[optind],"LF")) { parsed_freq=137500.0; @@ -867,11 +866,11 @@ void parse_commandline( } else if (!strcmp(argv[optind],"2m")) { parsed_freq=144490500.0; } else { - // Not a string. See if it can be parsed as a double. + // Not a std::string. See if it can be parsed as a double. char * endp; parsed_freq=strtod(argv[optind],&endp); if ((optarg==endp)||(*endp!='\0')) { - cerr << "Error: could not parse transmit frequency: " << argv[optind] << endl; + std::cerr << "Error: could not parse transmit frequency: " << argv[optind] << std::endl; ABORT(-1); } } @@ -882,69 +881,69 @@ void parse_commandline( // Check consistency among command line options. if (ppm&&self_cal) { - cout << "Warning: ppm value is being ignored!" << endl; + std::cout << "Warning: ppm value is being ignored!" << std::endl; ppm=0.0; } if (mode==TONE) { if ((callsign!="")||(locator!="")||(tx_power!="")||(center_freq_set.size()!=0)||random_offset) { - cerr << "Warning: callsign, locator, etc. are ignored when generating test tone" << endl; + std::cerr << "Warning: callsign, locator, etc. are ignored when generating test tone" << std::endl; } random_offset=0; if (test_tone<=0) { - cerr << "Error: test tone frequency must be positive" << endl; + std::cerr << "Error: test tone frequency must be positive" << std::endl; ABORT(-1); } } else { if ((callsign=="")||(locator=="")||(tx_power=="")||(center_freq_set.size()==0)) { - cerr << "Error: must specify callsign, locator, dBm, and at least one frequency" << endl; - cerr << "Try: wspr --help" << endl; + std::cerr << "Error: must specify callsign, locator, dBm, and at least one frequency" << std::endl; + std::cerr << "Try: wspr --help" << std::endl; ABORT(-1); } } // Print a summary of the parsed options if (mode==WSPR) { - cout << "WSPR packet contents:" << endl; - cout << " Callsign: " << callsign << endl; - cout << " Locator: " << locator << endl; - cout << " Power: " << tx_power << " dBm" << endl; - cout << "Requested TX frequencies:" << endl; - stringstream temp; + std::cout << "WSPR packet contents:" << std::endl; + std::cout << " Callsign: " << callsign << std::endl; + std::cout << " Locator: " << locator << std::endl; + std::cout << " Power: " << tx_power << " dBm" << std::endl; + std::cout << "Requested TX frequencies:" << std::endl; + std::stringstream temp; for (unsigned int t=0;t0) { - temp << " TX will stop after " << terminate << " transmissions." << endl; + temp << " TX will stop after " << terminate << " transmissions." << std::endl; } else if (repeat) { - temp << " Transmissions will continue forever until stopped with CTRL-C" << endl; + temp << " Transmissions will continue forever until stopped with CTRL-C" << std::endl; } if (random_offset) { - temp << " A small random frequency offset will be added to all transmisisons" << endl; + temp << " A small random frequency offset will be added to all transmisisons" << std::endl; } if (temp.str().length()) { - cout << "Extra options:" << endl; - cout << temp.str(); + std::cout << "Extra options:" << std::endl; + std::cout << temp.str(); } - cout << endl; + std::cout << std::endl; } else { - stringstream temp; - temp << setprecision(6) << fixed << "A test tone will be generated at frequency " << test_tone/1e6 << " MHz" << endl; - cout << temp.str(); + std::stringstream temp; + 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) { - cout << "ntp_adjtime() will be used to calibrate the tone" << endl; + std::cout << "ntp_adjtime() will be used to calibrate the tone" << std::endl; } else if (ppm) { - cout << "PPM value to be used to generate the tone: " << ppm << endl; + std::cout << "PPM value to be used to generate the tone: " << ppm << std::endl; } - cout << endl; + std::cout << std::endl; } } @@ -960,16 +959,16 @@ void update_ppm( status = ntp_adjtime(&ntx); if (status != TIME_OK) { - //cerr << "Error: clock not synchronized" << endl; + //cerr << "Error: clock not synchronized" << std::endl; //return; } ppm_new = (double)ntx.freq/(double)(1 << 16); /* frequency scale */ if (abs(ppm_new)>200) { - cerr << "Warning: absolute ppm value is greater than 200 and is being ignored!" << endl; + std::cerr << "Warning: absolute ppm value is greater than 200 and is being ignored!" << std::endl; } else { if (ppm!=ppm_new) { - cout << " Obtained new ppm value: " << ppm_new << endl; + std::cout << " Obtained new ppm value: " << ppm_new << std::endl; } ppm=ppm_new; } @@ -997,18 +996,18 @@ void timeval_print(struct timeval *tv) { printf("%s.%03ld", buffer, (tv->tv_usec+500)/1000); } -void open_mbox() -{ - unlink(DEVICE_FILE_NAME); - if (mknod(DEVICE_FILE_NAME, S_IFCHR|0600, makedev(100, 0)) < 0) { - cerr << "Failed to create mailbox device." << endl; - ABORT(-1); - } - mbox.handle = mbox_open(); - if (mbox.handle < 0) { - cerr << "Failed to open mailbox." << endl; - ABORT(-1); - } +void open_mbox() { + unlink(DEVICE_FILE_NAME); + unlink(LOCAL_DEVICE_FILE_NAME); + if (mknod(DEVICE_FILE_NAME, S_IFCHR|0600, makedev(100, 0)) < 0) { + std::cerr << "Failed to create mailbox device." << std::endl; + ABORT(-1); + } + mbox.handle = mbox_open(); + if (mbox.handle < 0) { + std::cerr << "Failed to open mailbox." << std::endl; + ABORT(-1); + } } void unlinkmbox() { @@ -1017,16 +1016,22 @@ void unlinkmbox() { }; int main(const int argc, char * const argv[]) { +#ifdef RPI1 + std::cout << "Detected Raspberry Pi version 1" << std::endl; +#else + std::cout << "Detected Raspberry Pi version 2/3" << std::endl; +#endif + atexit(unlinkmbox); // Initialize the RNG srand(time(NULL)); // Parse arguments - string callsign; - string locator; - string tx_power; - vector center_freq_set; + std::string callsign; + std::string locator; + std::string tx_power; + std::vector center_freq_set; double ppm; bool self_cal; bool repeat; @@ -1057,7 +1062,6 @@ int main(const int argc, char * const argv[]) { int mem_fd; char *gpio_mem, *gpio_map; volatile unsigned *gpio = NULL; - setup_io(mem_fd,gpio_mem,gpio_map,gpio); setup_gpios(gpio); allof7e = (unsigned *)mmap( @@ -1069,7 +1073,7 @@ int main(const int argc, char * const argv[]) { BCM2708_PERI_BASE //base ); if ((long int)allof7e==-1) { - cerr << "Error: mmap error!" << endl; + std::cerr << "Error: mmap error!" << std::endl; ABORT(-1); } open_mbox(); @@ -1085,14 +1089,14 @@ int main(const int argc, char * const argv[]) { double wspr_symtime = WSPR_SYMTIME; double tone_spacing=1.0/wspr_symtime; - stringstream temp; - temp << setprecision(6) << fixed << "Transmitting test tone on frequency " << test_tone/1.0e6 << " MHz" << endl; - cout << temp.str(); - cout << "Press CTRL-C to exit!" << endl; + std::stringstream temp; + 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; - vector dma_table_freq; + 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; @@ -1102,15 +1106,15 @@ int main(const int argc, char * const argv[]) { } 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 << setprecision(30) << dma_table_freq[0] << endl; - //cout << setprecision(30) << dma_table_freq[1] << endl; - //cout << setprecision(30) << dma_table_freq[2] << endl; - //cout << setprecision(30) << dma_table_freq[3] << endl; + //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) { - cout << " Warning: because of hardware limitations, test tone will be transmitted on" << endl; - stringstream temp; - temp << setprecision(6) << fixed << " frequency: " << (center_freq_actual-1.5*tone_spacing)/1e6 << " MHz" << endl; - cout << temp.str(); + 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; } @@ -1129,14 +1133,14 @@ int main(const int argc, char * const argv[]) { printf("WSPR codeblock: "); for (int i = 0; i < (signed)(sizeof(symbols)/sizeof(*symbols)); i++) { if (i) { - cout << ","; + std::cout << ","; } printf("%d", symbols[i]); } printf("\n"); */ - cout << "Ready to transmit (setup complete)..." << endl; + std::cout << "Ready to transmit (setup complete)..." << std::endl; int band=0; int n_tx=0; for(;;) { @@ -1156,16 +1160,16 @@ int main(const int argc, char * const argv[]) { } // Status message before transmission - stringstream temp; - temp << setprecision(6) << fixed; - temp << "Desired center frequency for " << (wspr15?"WSPR-15":"WSPR") << " transmission: "<< center_freq_desired/1e6 << " MHz" << endl; - cout << temp.str(); + 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; + std::cout << temp.str(); // Wait for WSPR transmission window to arrive. if (no_delay) { - cout << " Transmitting immediately (not waiting for WSPR window)" << endl; + std::cout << " Transmitting immediately (not waiting for WSPR window)" << std::endl; } else { - cout << " Waiting for next WSPR transmission window..." << endl; + std::cout << " Waiting for next WSPR transmission window..." << std::endl; wait_every((wspr15) ? 15 : 2); } @@ -1175,7 +1179,7 @@ int main(const int argc, char * const argv[]) { } // Create the DMA table for this center frequency - vector dma_table_freq; + 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); @@ -1184,14 +1188,14 @@ int main(const int argc, char * const argv[]) { } // Send the message! - //cout << "TX started!" << endl; + //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); - cout << " TX started at: "; + std::cout << " TX started at: "; timeval_print(&tvBegin); - cout << endl; + std::cout << std::endl; struct timeval sym_start; struct timeval diff; @@ -1203,8 +1207,8 @@ int main(const int argc, char * const argv[]) { 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 << endl; - //cout << sched_end-elapsed << endl; + //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; @@ -1216,13 +1220,13 @@ int main(const int argc, char * const argv[]) { txoff(); gettimeofday(&tvEnd, NULL); - cout << " TX ended at: "; + 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 { - cout << " Skipping transmission" << endl; + std::cout << " Skipping transmission" << std::endl; usleep(1000000); } From 8d377c445b6e7e41448ae63c5c34c3c96b22983e Mon Sep 17 00:00:00 2001 From: James Peroulas Date: Thu, 23 Feb 2017 10:10:00 -0800 Subject: [PATCH 07/16] Fixed formatting (80 cols) --- README | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/README b/README index afadad7..c3390fa 100644 --- a/README +++ b/README @@ -36,10 +36,11 @@ Usage: (WSPR --help output): -p --ppm ppm Known PPM correction to 19.2MHz RPi nominal crystal frequency. -s --self-calibration - Call ntp_adjtime() before every transmission to obtain the PPM error - of the xtal. + Call ntp_adjtime() before every transmission to obtain the PPM error of + the crystal. -r --repeat - Repeatedly, and in order, transmit on all the specified freqs. + Repeatedly, and in order, transmit on all the specified command line + freqs. -x --terminate Terminate after n transmissions have been completed. -o --offset @@ -47,7 +48,7 @@ Usage: (WSPR --help output): +/- 80 Hz for WSPR +/- 8 Hz for WSPR-15 -t --test-tone freq - Simply output a test tone and the specified frequency. Only used + Simply output a test tone at the specified frequency. Only used for debugging and to verify calibration. -n --no-delay Transmit immediately, do not wait for a WSPR TX window. Used From 07f19700ed2c20c0b83eb74b23dd8ea3966ba51a Mon Sep 17 00:00:00 2001 From: James Peroulas Date: Sun, 26 Feb 2017 02:20:05 +0000 Subject: [PATCH 08/16] Added comments, clarified bus/phys/virt status of addresses, removed unused gpio code. Working. --- wspr.cpp | 272 +++++++++++++++++++++++++++++++++---------------------- 1 file changed, 162 insertions(+), 110 deletions(-) diff --git a/wspr.cpp b/wspr.cpp index 04be074..e6a1ae4 100644 --- a/wspr.cpp +++ b/wspr.cpp @@ -1,22 +1,19 @@ // WSPR transmitter for the Raspberry Pi. See accompanying README and BUILD // files for descriptions on how to use this code. -/* -License: - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . - -*/ +// License: +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . // ha7ilm: added RPi2 support based on a patch to PiFmRds by Cristophe // Jacquet and Richard Hirst: http://git.io/vn7O9 @@ -28,6 +25,8 @@ License: #include #include #include +#include +#include #include #include #include @@ -42,21 +41,71 @@ License: #include #include #include +#include #include #include "mailbox.h" +// Note on accessing memory in RPi +// +// There are 3 types of addresses in the RPi: +// 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 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 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. + #define ABORT(a) exit(a) // Used for debugging #define MARK std::cout << "Currently in file: " << __FILE__ << " line: " << __LINE__ << std::endl // PLLD clock frequency. -// There seems to be a 2.5ppm offset between the NTP measured frequency -// error and the frequency error measured by a frequency counter. This fixed -// PPM offset is compensated for here. -// This PPM correction is not needed for RPI2/3. +// 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 RPI2 -#define F_PLLD_CLK (500000000.0*(1-0.000e-6)) +#define F_PLLD_CLK (500000000.0) #else #define F_PLLD_CLK (500000000.0*(1-2.500e-6)) #endif @@ -74,47 +123,47 @@ License: #define WSPR15_RAND_OFFSET 8 // Choose proper base address depending on RPI1/RPI2 setting from makefile. +// PERI_BASE_PHYS is the base address of the peripherals, in physical +// address space. #ifdef RPI2 -#define BCM2708_PERI_BASE 0x3f000000 +#define PERI_BASE_PHYS 0x3f000000 #define MEM_FLAG 0x04 -//#pragma message "Raspberry Pi 2/3 detected." #else -#define BCM2708_PERI_BASE 0x20000000 +#define PERI_BASE_PHYS 0x20000000 #define MEM_FLAG 0x0c -//#pragma message "Raspberry Pi 1 detected." #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 must be declared global so that it can be called by the atexit // function. -volatile unsigned *allof7e = NULL; +volatile unsigned *peri_base_virt = NULL; -// GPIO setup macros. Always use INP_GPIO(x) before using OUT_GPIO(x) or SET_GPIO_ALT(x,y) -#define INP_GPIO(g) *(gpio+((g)/10)) &= ~(7<<(((g)%10)*3)) -//#define OUT_GPIO(g) *(gpio+((g)/10)) |= (1<<(((g)%10)*3)) -//#define SET_GPIO_ALT(g,a) *(gpio+(((g)/10))) |= (((a)<=3?(a)+4:(a)==4?3:2)<<(((g)%10)*3)) +// 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<SOURCE_AD = (long int)constPage.p + f0_idx*4; // Wait for n_f0 PWM clocks bufPtr++; - while( ACCESS(DMA_PHYS_BASE + 0x04 /* CurBlock*/) == (long int)(instrs[bufPtr].p)) usleep(100); + while( ACCESS_BUS_ADDR(DMA_BUS_BASE + 0x04 /* CurBlock*/) == (long int)(instrs[bufPtr].p)) usleep(100); ((struct CB*)(instrs[bufPtr].v))->TXFR_LEN = n_f0; // Set GPIO pin to transmit f1 bufPtr++; - while( ACCESS(DMA_PHYS_BASE + 0x04 /* CurBlock*/) == (long int)(instrs[bufPtr].p)) usleep(100); + while( ACCESS_BUS_ADDR(DMA_BUS_BASE + 0x04 /* CurBlock*/) == (long int)(instrs[bufPtr].p)) usleep(100); ((struct CB*)(instrs[bufPtr].v))->SOURCE_AD = (long int)constPage.p + f1_idx*4; // Wait for n_f1 PWM clocks bufPtr=(bufPtr+1) % (1024); - while( ACCESS(DMA_PHYS_BASE + 0x04 /* CurBlock*/) == (long int)(instrs[bufPtr].p)) usleep(100); + while( ACCESS_BUS_ADDR(DMA_BUS_BASE + 0x04 /* CurBlock*/) == (long int)(instrs[bufPtr].p)) usleep(100); ((struct CB*)(instrs[bufPtr].v))->TXFR_LEN = n_f1; // Update counters @@ -305,7 +354,7 @@ void txSym( void unSetupDMA(){ //cout << "Exiting!" << std::endl; - struct DMAregs* DMA0 = (struct DMAregs*)&(ACCESS(DMA_PHYS_BASE)); + struct DMAregs* DMA0 = (struct DMAregs*)&(ACCESS_BUS_ADDR(DMA_BUS_BASE)); DMA0->CS =1<<31; // reset dma controller txoff(); } @@ -408,7 +457,7 @@ void setupDMA( instrs[instrCnt].v = (void*)((long int)instrPage.v + sizeof(struct CB)*i); instrs[instrCnt].p = (void*)((long int)instrPage.p + sizeof(struct CB)*i); instr0->SOURCE_AD = (unsigned long int)constPage.p+2048; - instr0->DEST_AD = PWM_PHYS_BASE+0x18 /* FIF1 */; + instr0->DEST_AD = PWM_BUS_BASE+0x18 /* FIF1 */; instr0->TXFR_LEN = 4; instr0->STRIDE = 0; //instr0->NEXTCONBK = (int)instrPage.p + sizeof(struct CB)*(i+1); @@ -418,7 +467,7 @@ void setupDMA( // Shouldn't this be (instrCnt%2) ??? if (i%2) { - instr0->DEST_AD = CM_GP0DIV; + instr0->DEST_AD = CM_GP0DIV_BUS; instr0->STRIDE = 4; instr0->TI = (1<<26/* no wide*/) ; } @@ -432,27 +481,27 @@ void setupDMA( ((struct CB*)(instrs[1023].v))->NEXTCONBK = (long int)instrs[0].p; // set up a clock for the PWM - ACCESS(CLK_PHYS_BASE + 40*4 /*PWMCLK_CNTL*/) = 0x5A000026; // Source=PLLD and disable + ACCESS_BUS_ADDR(CLK_BUS_BASE + 40*4 /*PWMCLK_CNTL*/) = 0x5A000026; // Source=PLLD and disable usleep(1000); - //ACCESS(CLK_PHYS_BASE + 41*4 /*PWMCLK_DIV*/) = 0x5A002800; - ACCESS(CLK_PHYS_BASE + 41*4 /*PWMCLK_DIV*/) = 0x5A002000; // set PWM div to 2, for 250MHz - ACCESS(CLK_PHYS_BASE + 40*4 /*PWMCLK_CNTL*/) = 0x5A000016; // Source=PLLD and enable + //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(PWM_PHYS_BASE + 0x0 /* CTRL*/) = 0; + ACCESS_BUS_ADDR(PWM_BUS_BASE + 0x0 /* CTRL*/) = 0; usleep(1000); - ACCESS(PWM_PHYS_BASE + 0x4 /* status*/) = -1; // clear errors + 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(PWM_PHYS_BASE + 0x10)=32; - ACCESS(PWM_PHYS_BASE + 0x20)=32; - ACCESS(PWM_PHYS_BASE + 0x0 /* CTRL*/) = -1; //(1<<13 /* Use fifo */) | (1<<10 /* repeat */) | (1<<9 /* serializer */) | (1<<8 /* enable ch */) ; + 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(PWM_PHYS_BASE + 0x8 /* DMAC*/) = (1<<31 /* DMA enable */) | 0x0707; + ACCESS_BUS_ADDR(PWM_BUS_BASE + 0x8 /* DMAC*/) = (1<<31 /* DMA enable */) | 0x0707; //activate dma - struct DMAregs* DMA0 = (struct DMAregs*)&(ACCESS(DMA_PHYS_BASE)); + struct DMAregs* DMA0 = (struct DMAregs*)&(ACCESS_BUS_ADDR(DMA_BUS_BASE)); DMA0->CS =1<<31; // reset DMA0->CONBLK_AD=0; DMA0->TI=0; @@ -465,10 +514,10 @@ void setupDMA( // Set up memory regions to access GPIO // void setup_io( - int & mem_fd, - char * & gpio_mem, - char * & gpio_map, - volatile unsigned * & gpio + int & mem_fd + //char * & gpio_mem, + //char * & gpio_map, + //volatile unsigned * & gpio ) { /* open /dev/mem */ if ((mem_fd = open("/dev/mem", O_RDWR|O_SYNC) ) < 0) { @@ -479,36 +528,37 @@ void setup_io( /* mmap GPIO */ // Allocate MAP block - if ((gpio_mem = (char *)malloc(BLOCK_SIZE + (PAGE_SIZE-1))) == NULL) { - std::cerr << "Error: allocation error" << std::endl; - ABORT (-1); - } + //if ((gpio_mem = (char *)malloc(BLOCK_SIZE + (PAGE_SIZE-1))) == NULL) { + // std::cerr << "Error: allocation error" << std::endl; + // ABORT (-1); + //} // Make sure pointer is on 4K boundary - if ((unsigned long)gpio_mem % PAGE_SIZE) - gpio_mem += PAGE_SIZE - ((unsigned long)gpio_mem % PAGE_SIZE); + //if ((unsigned long)gpio_mem % PAGE_SIZE) + // gpio_mem += PAGE_SIZE - ((unsigned long)gpio_mem % PAGE_SIZE); // Now map it - gpio_map = (char *)mmap( - gpio_mem, - BLOCK_SIZE, - PROT_READ|PROT_WRITE, - MAP_SHARED|MAP_FIXED, - mem_fd, - GPIO_VIRT_BASE - ); + //gpio_map = (char *)mmap( + // gpio_mem, + // BLOCK_SIZE, + // PROT_READ|PROT_WRITE, + // MAP_SHARED|MAP_FIXED, + // mem_fd, + // GPIO_VIRT_BASE + // ); - if ((long)gpio_map < 0) { - std::cerr << "Error: mmap error" << (long int)gpio_map << std::endl; - ABORT (-1); - } + //if ((long)gpio_map < 0) { + // std::cerr << "Error: mmap error" << (long int)gpio_map << std::endl; + // ABORT (-1); + //} // Always use volatile pointer! - gpio = (volatile unsigned *)gpio_map; + //gpio = (volatile unsigned *)gpio_map; } // Not sure why this function is needed as this code only uses GPIO4 and // this function sets gpio 7 through 11 as input... +#if 0 void setup_gpios( volatile unsigned * & gpio ){ @@ -524,11 +574,12 @@ void setup_gpios( // Set GPIO pins 7-11 to output for (g=7; g<=11; g++) { - INP_GPIO(g); // must use INP_GPIO before we can use OUT_GPIO + //INP_GPIO(g); // must use INP_GPIO before we can use OUT_GPIO //OUT_GPIO(g); } } +#endif // Convert std::string to uppercase void to_upper(char *str) @@ -1060,19 +1111,20 @@ int main(const int argc, char * const argv[]) { // Initial configuration int mem_fd; - char *gpio_mem, *gpio_map; - volatile unsigned *gpio = NULL; - setup_io(mem_fd,gpio_mem,gpio_map,gpio); - setup_gpios(gpio); - allof7e = (unsigned *)mmap( + //char *gpio_mem, *gpio_map; + //volatile unsigned *gpio = NULL; + //setup_io(mem_fd,gpio_mem,gpio_map,gpio); + setup_io(mem_fd); + //setup_gpios(gpio); + peri_base_virt = (unsigned *)mmap( NULL, 0x002FFFFF, //len PROT_READ|PROT_WRITE, MAP_SHARED, mem_fd, - BCM2708_PERI_BASE //base + PERI_BASE_PHYS //base ); - if ((long int)allof7e==-1) { + if ((long int)peri_base_virt==-1) { std::cerr << "Error: mmap error!" << std::endl; ABORT(-1); } From 6de039b511d15ca54f96ab70654f565dc2befdb1 Mon Sep 17 00:00:00 2001 From: James Peroulas Date: Sun, 26 Feb 2017 04:25:37 +0000 Subject: [PATCH 09/16] Added lots of comments. Moved mmap out of main loop. Working. --- wspr.cpp | 282 +++++++++++++++++++++++++++---------------------------- 1 file changed, 139 insertions(+), 143 deletions(-) diff --git a/wspr.cpp b/wspr.cpp index e6a1ae4..e6d43c2 100644 --- a/wspr.cpp +++ b/wspr.cpp @@ -204,15 +204,16 @@ struct DMAregs { }; struct PageInfo { - void* p; // physical address - void* v; // virtual address + void* b; // bus address + void* v; // virtual address }; +// Must be global so that exit handlers can access this. static struct { - int handle; /* From mbox_open() */ - unsigned mem_ref; /* From mem_alloc() */ - unsigned bus_addr; /* From mem_lock() */ - unsigned char *virt_addr; /* From mapmem() */ //ha7ilm: originally uint8_t + 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; @@ -227,7 +228,9 @@ void allocMemPool(unsigned numpages) //printf("allocMemoryPool bus_addr=%x virt_addr=%x mem_ref=%x\n",mbox.bus_addr,(unsigned)mbox.virt_addr,mbox.mem_ref); } -void getRealMemPageFromPool(void ** vAddr, void **pAddr) +// 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; @@ -235,45 +238,81 @@ void getRealMemPageFromPool(void ** vAddr, void **pAddr) } unsigned offset = mbox.pool_cnt*4096; *vAddr = (void*)(((unsigned)mbox.virt_addr) + offset); - *pAddr = (void*)(((unsigned)mbox.bus_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) //it will be 0 by default as in .bss - { + 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); } } +void disable_clock() { + // 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; + } + } +} + void txon() { - SETBIT_BUS_ADDR(GPIO_BUS_BASE , 14); - CLRBIT_BUS_ADDR(GPIO_BUS_BASE , 13); - CLRBIT_BUS_ADDR(GPIO_BUS_BASE , 12); + // 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 + // 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 - struct GPCTL setupword = {6/*SRC*/, 1, 0, 0, 0, 3,0x5a}; - ACCESS_BUS_ADDR(CM_GP0CTL_BUS) = *((int*)&setupword); + 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); } void txoff() { - struct GPCTL setupword = {6/*SRC*/, 0, 0, 0, 0, 1,0x5a}; - ACCESS_BUS_ADDR(CM_GP0CTL_BUS) = *((int*)&setupword); + //struct GPCTL setupword = {6/*SRC*/, 0, 0, 0, 0, 1,0x5a}; + //ACCESS_BUS_ADDR(CM_GP0CTL_BUS) = *((int*)&setupword); + disable_clock(); } // Transmit symbol sym for tsym seconds. @@ -327,29 +366,29 @@ void txSym( // 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].p)) usleep(100); - ((struct CB*)(instrs[bufPtr].v))->SOURCE_AD = (long int)constPage.p + f0_idx*4; + 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].p)) usleep(100); + 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].p)) usleep(100); - ((struct CB*)(instrs[bufPtr].v))->SOURCE_AD = (long int)constPage.p + f1_idx*4; + 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].p)) usleep(100); + 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].p); + //printf("",(unsigned)instrs[bufPtr].v,(unsigned)instrs[bufPtr].b); } void unSetupDMA(){ @@ -427,17 +466,10 @@ void setupDMA( struct PageInfo & instrPage, struct PageInfo instrs[] ){ - atexit(unSetupDMA); - atexit(deallocMemPool); - signal (SIGINT, handSig); - signal (SIGTERM, handSig); - signal (SIGHUP, handSig); - signal (SIGQUIT, handSig); - allocMemPool(1025); // Allocate a page of ram for the constants - getRealMemPageFromPool(&constPage.v, &constPage.p); + getRealMemPageFromPool(&constPage.v, &constPage.b); // Create 1024 instructions allocating one page at a time. // Even instructions target the GP0 Clock divider @@ -445,7 +477,7 @@ void setupDMA( int instrCnt = 0; while (instrCnt<1024) { // Allocate a page of ram for the instructions - getRealMemPageFromPool(&instrPage.v, &instrPage.p); + getRealMemPageFromPool(&instrPage.v, &instrPage.b); // make copy instructions // Only create as many instructions as will fit in the recently @@ -455,12 +487,12 @@ void setupDMA( 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].p = (void*)((long int)instrPage.p + sizeof(struct CB)*i); - instr0->SOURCE_AD = (unsigned long int)constPage.p+2048; + 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.p + sizeof(struct CB)*(i+1); + //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; @@ -472,13 +504,13 @@ void setupDMA( instr0->TI = (1<<26/* no wide*/) ; } - if (instrCnt!=0) ((struct CB*)(instrs[instrCnt-1].v))->NEXTCONBK = (long int)instrs[instrCnt].p; + 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].p; + ((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 @@ -505,83 +537,11 @@ void setupDMA( DMA0->CS =1<<31; // reset DMA0->CONBLK_AD=0; DMA0->TI=0; - DMA0->CONBLK_AD = (unsigned long int)(instrPage.p); + DMA0->CONBLK_AD = (unsigned long int)(instrPage.b); DMA0->CS =(1<<0)|(255 <<16); // enable bit = 0, clear end flag = 1, prio=19-16 } - -// -// Set up memory regions to access GPIO -// -void setup_io( - int & mem_fd - //char * & gpio_mem, - //char * & gpio_map, - //volatile unsigned * & gpio -) { - /* 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); - } - - /* mmap GPIO */ - - // Allocate MAP block - //if ((gpio_mem = (char *)malloc(BLOCK_SIZE + (PAGE_SIZE-1))) == NULL) { - // std::cerr << "Error: allocation error" << std::endl; - // ABORT (-1); - //} - - // Make sure pointer is on 4K boundary - //if ((unsigned long)gpio_mem % PAGE_SIZE) - // gpio_mem += PAGE_SIZE - ((unsigned long)gpio_mem % PAGE_SIZE); - - // Now map it - //gpio_map = (char *)mmap( - // gpio_mem, - // BLOCK_SIZE, - // PROT_READ|PROT_WRITE, - // MAP_SHARED|MAP_FIXED, - // mem_fd, - // GPIO_VIRT_BASE - // ); - - //if ((long)gpio_map < 0) { - // std::cerr << "Error: mmap error" << (long int)gpio_map << std::endl; - // ABORT (-1); - //} - - // Always use volatile pointer! - //gpio = (volatile unsigned *)gpio_map; -} - -// Not sure why this function is needed as this code only uses GPIO4 and -// this function sets gpio 7 through 11 as input... -#if 0 -void setup_gpios( - volatile unsigned * & gpio -){ - int g; - // Switch GPIO 7..11 to output mode - - /************************************************************************\ - * You are about to change the GPIO settings of your computer. * - * Mess this up and it will stop working! * - * It might be a good idea to 'sync' before running this program * - * so at least you still have your code changes written to the SD-card! * - \************************************************************************/ - - // Set GPIO pins 7-11 to output - for (g=7; g<=11; g++) { - //INP_GPIO(g); // must use INP_GPIO before we can use OUT_GPIO - //OUT_GPIO(g); - } - -} -#endif - -// Convert std::string to uppercase +// Convert string to uppercase void to_upper(char *str) { while(*str) { @@ -1061,20 +1021,72 @@ void open_mbox() { } } -void unlinkmbox() { +void cleanup() { + disable_clock(); + unSetupDMA(); + deallocMemPool(); unlink(DEVICE_FILE_NAME); unlink(LOCAL_DEVICE_FILE_NAME); -}; +} + +void cleanupAndExit(int sig) { + cleanup(); + printf("Exiting with error; caught signal: %i\n", sig); + exit(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) { + printf("Warning: pthread_setschedparam (increase thread priority) returned non-zero: %i\n", ret); + } +} + +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, + 0x002FFFFF, //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; + 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 + for (int i = 0; i < 64; i++) { + struct sigaction sa; + memset(&sa, 0, sizeof(sa)); + sa.sa_handler = cleanupAndExit; + sigaction(i, &sa, NULL); + } + atexit(cleanup); + setSchedPriority(30); + #ifdef RPI1 std::cout << "Detected Raspberry Pi version 1" << std::endl; #else std::cout << "Detected Raspberry Pi version 2/3" << std::endl; #endif - atexit(unlinkmbox); - // Initialize the RNG srand(time(NULL)); @@ -1110,24 +1122,8 @@ int main(const int argc, char * const argv[]) { int nbands=center_freq_set.size(); // Initial configuration - int mem_fd; - //char *gpio_mem, *gpio_map; - //volatile unsigned *gpio = NULL; - //setup_io(mem_fd,gpio_mem,gpio_map,gpio); - setup_io(mem_fd); - //setup_gpios(gpio); - peri_base_virt = (unsigned *)mmap( - NULL, - 0x002FFFFF, //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; - ABORT(-1); - } + setup_peri_base_virt(peri_base_virt); + // Set up DMA open_mbox(); txon(); struct PageInfo constPage; From 67ca9cfcdfefdc048acaf27603ad615e55517362 Mon Sep 17 00:00:00 2001 From: James Peroulas Date: Sun, 26 Feb 2017 16:35:33 +0000 Subject: [PATCH 10/16] 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); From 516503af7cca91903bca396fa481dea8a31ea8bf Mon Sep 17 00:00:00 2001 From: James Peroulas Date: Sun, 26 Feb 2017 16:49:00 +0000 Subject: [PATCH 11/16] Options are case insensitive. --- gpioclk.cpp | 4 ++-- wspr.cpp | 56 ++++++++++++++++++++++++++++++----------------------- 2 files changed, 34 insertions(+), 26 deletions(-) diff --git a/gpioclk.cpp b/gpioclk.cpp index f457a58..7a3ef5a 100644 --- a/gpioclk.cpp +++ b/gpioclk.cpp @@ -280,9 +280,9 @@ void parse_commandline( ABORT(-1); break; case 's': - if (!strcmp(optarg,"PLLD")) { + if (!strcasecmp(optarg,"PLLD")) { source=PLLD; - } else if (!strcmp(optarg,"XTAL")) { + } else if (!strcasecmp(optarg,"XTAL")) { source=XTAL; } else { cerr << "Error: unrecognized frequency source" << endl; diff --git a/wspr.cpp b/wspr.cpp index 126b94d..141dbc8 100644 --- a/wspr.cpp +++ b/wspr.cpp @@ -267,6 +267,10 @@ void deallocMemPool() { // 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); @@ -405,10 +409,14 @@ void txSym( // Turn off (reset) DMA engine void unSetupDMA(){ - //cout << "Exiting!" << std::endl; - struct DMAregs* DMA0 = (struct DMAregs*)&(ACCESS_BUS_ADDR(DMA_BUS_BASE)); - DMA0->CS =1<<31; // reset dma controller - txoff(); + // 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(); } // Truncate at bit lsb. i.e. set all bits less than lsb to zero. @@ -862,46 +870,46 @@ void parse_commandline( continue; } // Must be a frequency - // First see if it is a std::string. + // First see if it is a string. double parsed_freq; - if (!strcmp(argv[optind],"LF")) { + if (!strcasecmp(argv[optind],"LF")) { parsed_freq=137500.0; - } else if (!strcmp(argv[optind],"LF-15")) { + } else if (!strcasecmp(argv[optind],"LF-15")) { parsed_freq=137612.5; - } else if (!strcmp(argv[optind],"MF")) { + } else if (!strcasecmp(argv[optind],"MF")) { parsed_freq=475700.0; - } else if (!strcmp(argv[optind],"MF-15")) { + } else if (!strcasecmp(argv[optind],"MF-15")) { parsed_freq=475812.5; - } else if (!strcmp(argv[optind],"160m")) { + } else if (!strcasecmp(argv[optind],"160m")) { parsed_freq=1838100.0; - } else if (!strcmp(argv[optind],"160m-15")) { + } else if (!strcasecmp(argv[optind],"160m-15")) { parsed_freq=1838212.5; - } else if (!strcmp(argv[optind],"80m")) { + } else if (!strcasecmp(argv[optind],"80m")) { parsed_freq=3594100.0; - } else if (!strcmp(argv[optind],"60m")) { + } else if (!strcasecmp(argv[optind],"60m")) { parsed_freq=5288700.0; - } else if (!strcmp(argv[optind],"40m")) { + } else if (!strcasecmp(argv[optind],"40m")) { parsed_freq=7040100.0; - } else if (!strcmp(argv[optind],"30m")) { + } else if (!strcasecmp(argv[optind],"30m")) { parsed_freq=10140200.0; - } else if (!strcmp(argv[optind],"20m")) { + } else if (!strcasecmp(argv[optind],"20m")) { parsed_freq=14097100.0; - } else if (!strcmp(argv[optind],"17m")) { + } else if (!strcasecmp(argv[optind],"17m")) { parsed_freq=18106100.0; - } else if (!strcmp(argv[optind],"15m")) { + } else if (!strcasecmp(argv[optind],"15m")) { parsed_freq=21096100.0; - } else if (!strcmp(argv[optind],"12m")) { + } else if (!strcasecmp(argv[optind],"12m")) { parsed_freq=24926100.0; - } else if (!strcmp(argv[optind],"10m")) { + } else if (!strcasecmp(argv[optind],"10m")) { parsed_freq=28126100.0; - } else if (!strcmp(argv[optind],"6m")) { + } else if (!strcasecmp(argv[optind],"6m")) { parsed_freq=50294500.0; - } else if (!strcmp(argv[optind],"4m")) { + } else if (!strcasecmp(argv[optind],"4m")) { parsed_freq=70092500.0; - } else if (!strcmp(argv[optind],"2m")) { + } else if (!strcasecmp(argv[optind],"2m")) { parsed_freq=144490500.0; } else { - // Not a std::string. See if it can be parsed as a double. + // Not a string. See if it can be parsed as a double. char * endp; parsed_freq=strtod(argv[optind],&endp); if ((optarg==endp)||(*endp!='\0')) { From c6d9aaf0364ebd69f55b003daaf4c83d4646e691 Mon Sep 17 00:00:00 2001 From: James Peroulas Date: Sun, 26 Feb 2017 16:52:58 +0000 Subject: [PATCH 12/16] Converted callsign+locator to uppercase. --- wspr.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/wspr.cpp b/wspr.cpp index 141dbc8..d2143b8 100644 --- a/wspr.cpp +++ b/wspr.cpp @@ -41,6 +41,7 @@ #include #include #include +#include #include #include @@ -921,6 +922,10 @@ void parse_commandline( center_freq_set.push_back(parsed_freq); } + // Convert to uppercase + transform(callsign.begin(),callsign.end(),callsign.begin(),::toupper); + transform(locator.begin(),locator.end(),locator.begin(),::toupper); + // Check consistency among command line options. if (ppm&&self_cal) { std::cout << "Warning: ppm value is being ignored!" << std::endl; From ee13d46aaec68183f1f5ae017e2e830a7c46d031 Mon Sep 17 00:00:00 2001 From: James Peroulas Date: Mon, 27 Feb 2017 04:59:12 +0000 Subject: [PATCH 13/16] Corrected documentation. --- BUILD | 20 -------------------- README | 36 +++++++++++++++++++++++++----------- wspr.cpp | 6 +++--- 3 files changed, 28 insertions(+), 34 deletions(-) delete mode 100644 BUILD diff --git a/BUILD b/BUILD deleted file mode 100644 index 1b39a51..0000000 --- a/BUILD +++ /dev/null @@ -1,20 +0,0 @@ -Install required packages: - sudo apt-get install git g++ make grep mawk ntp - -Make sure you are using the latest kernel by updating your system. The latest -kernel includes fixes wich improve NTP ppm measurement accuracy: - sudo apt-get update - sudo apt-get dist-upgrade - -Get code/ compile: - rm -rf WsprryPi - git clone https://github.com/JamesP6000/WsprryPi.git - cd WsprryPi - make - -Install to /usr/local/bin: - sudo make install - -Uninstall: - sudo make uninstall - diff --git a/README b/README index c3390fa..ec1ccdd 100644 --- a/README +++ b/README @@ -9,18 +9,17 @@ Compatible with the original Raspberry Pi and the Raspberry Pi 2/3. ****** Installation / update: ****** - Make sure you are using the latest kernel by updating your system. The latest - kernel includes fixes wich improve NTP ppm measurement accuracy: - sudo apt-get update - sudo apt-get dist-upgrade - Download and compile code: sudo apt-get install git git clone https://github.com/JamesP6000/WsprryPi.git cd WsprryPi make - See the accompanying BUILD file for more details. + Install to /usr/local/bin: + sudo make install + + Uninstall: + sudo make uninstall ****** Usage: (WSPR --help output): @@ -36,8 +35,10 @@ Usage: (WSPR --help output): -p --ppm ppm Known PPM correction to 19.2MHz RPi nominal crystal frequency. -s --self-calibration - Call ntp_adjtime() before every transmission to obtain the PPM error of - the crystal. + Check NTP before every transmission to obtain the PPM error of the + crystal (default setting!). + -f --free-running + Do not use NTP to correct frequency error of RPi crystal. -r --repeat Repeatedly, and in order, transmit on all the specified command line freqs. @@ -102,6 +103,11 @@ Radio licensing / RF: of isolation, e.g. by using a RF transformer, a simple buffer/driver/PA stage, two schottky small signal diodes back to back. + TAPR makes a very nice shield for the Raspberry Pi that is pre-assembled, + performs the appropriate filtering for the 20m band, and also increases + the power output to 20dBm! Just connect your antenna and you're good-to-go! + https://www.tapr.org/kits_20M-wspr-pi.html + ****** TX Timing: ****** @@ -132,6 +138,10 @@ Calibration: Pi has been on for a long time, the crystal's temperature has stabilized, and the NTP control loop has converged. + As of 2017-02, NTP calibration is enabled by default and produces a + frequency error of about 0.1 PPM after the Pi has temperature stabilized + and the NTP loop has converged. + AM calibration: A practical way to calibrate is to tune the transmitter on the same frequency of a medium wave AM broadcast station; keep tuning until zero beat (the @@ -170,7 +180,10 @@ Example usage: Using callsign N9NNN, locator EM10, and TX power 33 dBm, transmit a single WSPR transmission on the 20m band using NTP based frequency offset calibration. - sudo ./wspr --self-calibration N9NNN EM10 33 20m + sudo ./wspr N9NNN EM10 33 20m + + The same as above, but without NTP calibration: + sudo ./wspr --free-running N9NNN EM10 33 20m Transmit a WSPR transmission slightly off-center on 30m every 10 minutes for a total of 7 transmissions, and using a fixed PPM correction value. @@ -178,7 +191,7 @@ Example usage: Transmit repeatedly on 40m, use NTP based frequency offset calibration, and add a random frequency offset to each transmission to minimize collisions - with other transmissions. + with other transmitters. sudo ./wspr --repeat --offset --self-calibration N9NNN EM10 33 40m ****** @@ -215,7 +228,8 @@ Credits: Michael Tatarinov for adding a patch to get PPM info directly from the kernel. - Retzler András (HA7ILM) added support for the RPi2/3. + 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. [1] PiFM code from http://www.icrobotics.co.uk/wiki/index.php/Turning_the_Raspberry_Pi_Into_an_FM_Transmitter diff --git a/wspr.cpp b/wspr.cpp index d2143b8..600248a 100644 --- a/wspr.cpp +++ b/wspr.cpp @@ -1,5 +1,5 @@ -// WSPR transmitter for the Raspberry Pi. See accompanying README and BUILD -// files for descriptions on how to use this code. +// WSPR transmitter for the Raspberry Pi. See accompanying README +// file for a description on how to use this code. // License: // This program is free software: you can redistribute it and/or modify @@ -732,7 +732,7 @@ void print_usage() { std::cout << " for testing only." << std::endl; std::cout << std::endl; std::cout << "Frequencies can be specified either as an absolute TX carrier frequency, or" << std::endl; - std::cout << "using one of the following std::strings. If a std::string is used, the transmission" << std::endl; + std::cout << "using one of the following strings. If a string is used, the transmission" << std::endl; std::cout << "will happen in the middle of the WSPR region of the selected band." << std::endl; std::cout << " LF LF-15 MF MF-15 160m 160m-15 80m 60m 40m 30m 20m 17m 15m 12m 10m 6m 4m 2m" << std::endl; std::cout << "-15 indicates the WSPR-15 region of band ." << std::endl; From 20b9cceaba7dd2920911ef2f821ef2a505e07646 Mon Sep 17 00:00:00 2001 From: James Peroulas Date: Mon, 27 Feb 2017 16:59:42 +0000 Subject: [PATCH 14/16] Comment formatting. --- wspr.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/wspr.cpp b/wspr.cpp index 600248a..647af3d 100644 --- a/wspr.cpp +++ b/wspr.cpp @@ -209,8 +209,8 @@ struct DMAregs { // Virtual and bus addresses of a page of physical memory. struct PageInfo { - void* b; // bus address - void* v; // virtual address + void* b; // bus address + void* v; // virtual address }; // Must be global so that exit handlers can access this. From 469a4b328c0dd7e98548768d17862625c6c02e7d Mon Sep 17 00:00:00 2001 From: James Peroulas Date: Tue, 28 Feb 2017 07:43:13 +0000 Subject: [PATCH 15/16] Changed time formatting to Y-M-D. --- wspr.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wspr.cpp b/wspr.cpp index 647af3d..ca53bb7 100644 --- a/wspr.cpp +++ b/wspr.cpp @@ -1038,7 +1038,7 @@ void timeval_print(struct timeval *tv) { //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 %m-%d-%Y %T", gmtime(&curtime)); + strftime(buffer, 30, "UTC %Y-%m-%d %T", gmtime(&curtime)); printf("%s.%03ld", buffer, (tv->tv_usec+500)/1000); } From ab761d6097bc336c4be17b6db62e0c03a91fa758 Mon Sep 17 00:00:00 2001 From: James Peroulas Date: Tue, 28 Feb 2017 07:56:16 +0000 Subject: [PATCH 16/16] Updated documentation. --- README | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/README b/README index ec1ccdd..9dd1bc2 100644 --- a/README +++ b/README @@ -77,8 +77,14 @@ Radio licensing / RF: of your Raspberry Pi, connect an antenna to the LPF. The GPIO4 and GND pins are found on header P1 pin 7 and 9 respectively, the pin closest to P1 label is pin 1 and its 3rd and 4th neighbour is pin 7 and 9 respectively. See this - link for pin layout: http://elinux.org/RPi_Low-level_peripherals Examples of - low-pass filters can be found here: http://www.gqrp.com/harmonic_filters.pdf + link for pin layout: http://elinux.org/RPi_Low-level_peripherals + + Examples of low-pass filters can be found here: + http://www.gqrp.com/harmonic_filters.pdf + TAPR makes a very nice shield for the Raspberry Pi that is pre-assembled, + performs the appropriate filtering for the 20m band, and also increases + the power output to 20dBm! Just connect your antenna and you're good-to-go! + https://www.tapr.org/kits_20M-wspr-pi.html The expected power output is 10mW (+10dBm) in a 50 Ohm load. This looks neglible, but when connected to a simple dipole antenna this may result in @@ -103,11 +109,6 @@ Radio licensing / RF: of isolation, e.g. by using a RF transformer, a simple buffer/driver/PA stage, two schottky small signal diodes back to back. - TAPR makes a very nice shield for the Raspberry Pi that is pre-assembled, - performs the appropriate filtering for the 20m band, and also increases - the power output to 20dBm! Just connect your antenna and you're good-to-go! - https://www.tapr.org/kits_20M-wspr-pi.html - ****** TX Timing: ****** @@ -123,6 +124,10 @@ TX Timing: ****** Calibration: ****** + As of 2017-02, NTP calibration is enabled by default and produces a + frequency error of about 0.1 PPM after the Pi has temperature stabilized + and the NTP loop has converged. + Frequency calibration is REQUIRED to ensure that the WSPR-2 transmission occurs within the narrow 200 Hz band. The reference crystal on your RPi might have an frequency error (which in addition is temp. dependent -1.3Hz/degC @@ -138,10 +143,6 @@ Calibration: Pi has been on for a long time, the crystal's temperature has stabilized, and the NTP control loop has converged. - As of 2017-02, NTP calibration is enabled by default and produces a - frequency error of about 0.1 PPM after the Pi has temperature stabilized - and the NTP loop has converged. - AM calibration: A practical way to calibrate is to tune the transmitter on the same frequency of a medium wave AM broadcast station; keep tuning until zero beat (the @@ -169,8 +170,7 @@ PWM Peripheral: by editing /etc/modules and commenting out the snd-bcm2835 device. ****** -Example usage: -****** +Example usage: ****** Brief help screen ./wspr --help