diff --git a/.gitignore b/.gitignore index 4b59f9b..4f0ca99 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ wspr gpioclk mailbox.o +nhash.o diff --git a/makefile b/Makefile similarity index 80% rename from makefile rename to Makefile index 0a48b7a..19d7243 100644 --- a/makefile +++ b/Makefile @@ -1,7 +1,7 @@ prefix=/usr/local CFLAGS += -Wall -CXXFLAGS += -D_GLIBCXX_DEBUG -std=c++11 -Wall -Werror -fmax-errors=5 +CXXFLAGS += -D_GLIBCXX_DEBUG -std=c++14 -Wall -Werror -Wno-psabi LDLIBS += -lm ifeq ($(findstring armv6,$(shell uname -m)),armv6) @@ -18,11 +18,14 @@ all: wspr gpioclk mailbox.o: mailbox.c mailbox.h $(CC) $(CFLAGS) -c mailbox.c -wspr: mailbox.o wspr.cpp mailbox.h - $(CXX) $(CXXFLAGS) $(LDFLAGS) $(LDLIBS) $(PI_VERSION) mailbox.o wspr.cpp -owspr +nhash.o: nhash.c nhash.h + $(CC) $(CFLAGS) -c nhash.c + +wspr: mailbox.o nhash.o wspr.cpp mailbox.h + $(CXX) $(CXXFLAGS) $(LDFLAGS) $(LDLIBS) $(PI_VERSION) mailbox.o nhash.o wspr.cpp -o wspr gpioclk: gpioclk.cpp - $(CXX) $(CXXFLAGS) $(LDFLAGS) $(LDLIBS) $(PI_VERSION) gpioclk.cpp -ogpioclk + $(CXX) $(CXXFLAGS) $(LDFLAGS) $(LDLIBS) $(PI_VERSION) gpioclk.cpp -o gpioclk clean: $(RM) *.o gpioclk wspr diff --git a/README b/README index c0424d9..0acb7f2 100644 --- a/README +++ b/README @@ -79,6 +79,8 @@ Usage: (WSPR --help output): Transmission gaps can be created by specifying a TX frequency of 0 + This program supports both 4 and 6 character Maidenhead grid locators. + 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 @@ -202,6 +204,9 @@ Example usage: The same as above, but without NTP calibration: sudo ./wspr --free-running N9NNN EM10 33 20m + Specify a 6 character Maidenhead grid locator with TX power 20 dBm on 20m: + sudo ./wspr K0WBG DM65rc 20 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 ./wspr --repeat --terminate 7 --ppm 43.17 N9NNN EM10 33 10140210 0 0 0 0 @@ -219,6 +224,7 @@ Reference documentation: http://www.scribd.com/doc/101830961/GPIO-Pads-Control2 https://github.com/mgottschlag/vctools/blob/master/vcdb/cm.yaml https://www.kernel.org/doc/Documentation/vm/pagemap.txt + https://physics.princeton.edu/pulsar/k1jt/WSPR_2.0_User.pdf (specifically Appendix B) ****** Credits: @@ -248,6 +254,9 @@ Credits: Retzler AndrĂ¡s (HA7ILM) for the massive changes that were required to incorporate the mailbox code so that the RPi2 and RPi3 could be supported. + Mathias Gibbens (K0WBG) added support for sending 6 character Maidenhead grid + locators to the program as well as performing some code cleanup. + [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/gpioclk.cpp b/gpioclk.cpp index 7a3ef5a..decdecd 100644 --- a/gpioclk.cpp +++ b/gpioclk.cpp @@ -17,36 +17,38 @@ along with this program. If not, see . */ -#include -#include -#include -#include +#include #include #include -#include #include -#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include #include -#include #include -#include -#include -#include #include -#include +#include +#include +#include #include -#include -#include -#include using namespace std; #define ABORT(a) exit(a) // Used for debugging -#define MARK std::cout << "Currently in file: " << __FILE__ << " line: " << __LINE__ << std::endl +#define MARK \ + std::cout << "Currently in file: " << __FILE__ << " line: " << __LINE__ \ + << std::endl // Nominal clock frequencies -#define F_XTAL (19200000.0) +#define F_XTAL (19200000.0) #define F_PLLD_CLK (500000000.0) // Choose proper base address depending on RPI1/RPI2 setting from makefile. @@ -58,161 +60,148 @@ using namespace std; //#pragma message "Raspberry Pi 1 detected." #endif -#define GPIO_BASE (BCM2708_PERI_BASE + 0x200000) /* GPIO controller */ -#define PAGE_SIZE (4*1024) -#define BLOCK_SIZE (4*1024) +#define GPIO_BASE (BCM2708_PERI_BASE + 0x200000) /* GPIO controller */ +#define PAGE_SIZE (4 * 1024) +#define BLOCK_SIZE (4 * 1024) // This must be declared global so that it can be called by the atexit // function. -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 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<divisor_max) { - divisor_actual=divisor_max; + if (divisor_actual > divisor_max) { + divisor_actual = divisor_max; } - if (divisor_actual<2) { - divisor_actual=2; + if (divisor_actual < 2) { + divisor_actual = 2; } // Actual frequency - const double freq_actual=source_freq/divisor_actual; + const double freq_actual = source_freq / divisor_actual; - cout << "Actual frequency produced (nominal): " << setprecision(30) << freq_actual << " Hz" << endl; + cout << "Actual frequency produced (nominal): " << setprecision(30) + << freq_actual << " Hz" << endl; cout << "Actual divisor used: " << setprecision(30) << divisor_actual << endl; // Initial configuration int mem_fd; char *gpio_mem, *gpio_map; - volatile unsigned *gpio = NULL; - setup_io(mem_fd,gpio_mem,gpio_map,gpio); + volatile unsigned *gpio = nullptr; + setup_io(mem_fd, gpio_mem, gpio_map, gpio); setup_gpios(gpio); - allof7e = (unsigned *)mmap( - NULL, - 0x002FFFFF, //len - PROT_READ|PROT_WRITE, - MAP_SHARED, - mem_fd, - BCM2708_PERI_BASE //base - ); - if ((long int)allof7e==-1) { + allof7e = (unsigned *)mmap(nullptr, + 0x002FFFFF, // len + PROT_READ | PROT_WRITE, MAP_SHARED, mem_fd, + BCM2708_PERI_BASE // base + ); + if ((long int)allof7e == -1) { cerr << "Error: mmap error!" << endl; ABORT(-1); } cout << "Press CTRL-C to stop / exit" << endl; atexit(txoff); - signal (SIGINT, handSig); - signal (SIGTERM, handSig); - signal (SIGHUP, handSig); - signal (SIGQUIT, handSig); + signal(SIGINT, handSig); + signal(SIGTERM, handSig); + signal(SIGHUP, handSig); + signal(SIGQUIT, handSig); - txon(source,divisor_actual); + txon(source, divisor_actual); // Wait forever - while (1) { + while (true) { usleep(1000000); } return 0; } - diff --git a/mailbox.c b/mailbox.c index 3f0ece0..be65bdd 100644 --- a/mailbox.c +++ b/mailbox.c @@ -14,7 +14,8 @@ modification, are permitted provided that the following conditions are met: THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND @@ -23,277 +24,270 @@ ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#include -#include -#include -#include -#include #include +#include #include -#include +#include +#include +#include #include +#include +#include #include +#include #include "mailbox.h" -#define PAGE_SIZE (4*1024) - -void *mapmem(unsigned base, unsigned size) -{ - int mem_fd; - unsigned offset = base % PAGE_SIZE; - base = base - offset; - /* open /dev/mem */ - if ((mem_fd = open("/dev/mem", O_RDWR|O_SYNC) ) < 0) { - printf("can't open /dev/mem\nThis program should be run as root. Try prefixing command with: sudo\n"); - exit (-1); - } - void *mem = mmap( - 0, - size, - PROT_READ|PROT_WRITE, - MAP_SHARED/*|MAP_FIXED*/, - mem_fd, - base); +#define PAGE_SIZE (4 * 1024) + +void *mapmem(unsigned base, unsigned size) { + int mem_fd; + unsigned offset = base % PAGE_SIZE; + base = base - offset; + /* open /dev/mem */ + if ((mem_fd = open("/dev/mem", O_RDWR | O_SYNC)) < 0) { + printf("can't open /dev/mem\nThis program should be run as root. Try " + "prefixing command with: sudo\n"); + exit(-1); + } + void *mem = mmap(0, size, PROT_READ | PROT_WRITE, MAP_SHARED /*|MAP_FIXED*/, + mem_fd, base); #ifdef DEBUG - printf("base=0x%x, mem=%p\n", base, mem); + printf("base=0x%x, mem=%p\n", base, mem); #endif - if (mem == MAP_FAILED) { - printf("mmap error %d\n", (int)mem); - exit (-1); - } - close(mem_fd); - return (char *)mem + offset; + if (mem == MAP_FAILED) { + printf("mmap error %d\n", (int)mem); + exit(-1); + } + close(mem_fd); + return (char *)mem + offset; } -void *unmapmem(void *addr, unsigned size) -{ - int s = munmap(addr, size); - if (s != 0) { - printf("munmap error %d\n", s); - exit (-1); - } +void *unmapmem(void *addr, unsigned size) { + int s = munmap(addr, size); + if (s != 0) { + printf("munmap error %d\n", s); + exit(-1); + } - return NULL; + return NULL; } /* * use ioctl to send mbox property message */ -static int mbox_property(int file_desc, void *buf) -{ - int ret_val = ioctl(file_desc, IOCTL_MBOX_PROPERTY, buf); +static int mbox_property(int file_desc, void *buf) { + int ret_val = ioctl(file_desc, IOCTL_MBOX_PROPERTY, buf); - if (ret_val < 0) { - // something wrong somewhere, send some details to stderr - perror("ioctl_set_msg failed"); - } + if (ret_val < 0) { + // something wrong somewhere, send some details to stderr + perror("ioctl_set_msg failed"); + } #ifdef DEBUG - unsigned *p = buf; int i; unsigned size = *(unsigned *)buf; - for (i=0; i= 0) { - //printf("Using mbox device " DEVICE_FILE_NAME ".\n"); - return file_desc; - } - - // Try to create one - 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); - 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); - return file_desc; - } - - printf("Unable to open / create kernel mbox device file, abort!\n"); - exit (-1); + int file_desc; + + // Open a char device file used for communicating with kernel mbox driver. + + // try to use the device node in /dev first (created by kernels 4.1+) + file_desc = open(DEVICE_FILE_NAME, 0); + if (file_desc >= 0) { + // printf("Using mbox device " DEVICE_FILE_NAME ".\n"); + return file_desc; + } + + // Try to create one + 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); + 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); + return file_desc; + } + + printf("Unable to open / create kernel mbox device file, abort!\n"); + exit(-1); } -void mbox_close(int file_desc) { - close(file_desc); -} +void mbox_close(int file_desc) { close(file_desc); } diff --git a/mailbox.h b/mailbox.h index d1e49f2..85eb946 100644 --- a/mailbox.h +++ b/mailbox.h @@ -14,7 +14,8 @@ modification, are permitted provided that the following conditions are met: THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND @@ -23,6 +24,9 @@ ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +#ifndef _MAILBOX_H +#define _MAILBOX_H + #include // Newer kernels (>= 4.1) use major 249, older ones major 100. @@ -36,13 +40,18 @@ int mbox_open(); void mbox_close(int file_desc); unsigned get_version(int file_desc); -unsigned mem_alloc(int file_desc, unsigned size, unsigned align, unsigned flags); +unsigned mem_alloc(int file_desc, unsigned size, unsigned align, + unsigned flags); unsigned mem_free(int file_desc, unsigned handle); unsigned mem_lock(int file_desc, unsigned handle); unsigned mem_unlock(int file_desc, unsigned handle); void *mapmem(unsigned base, unsigned size); void *unmapmem(void *addr, unsigned size); -unsigned execute_code(int file_desc, unsigned code, unsigned r0, unsigned r1, unsigned r2, unsigned r3, unsigned r4, unsigned r5); -unsigned execute_qpu(int file_desc, unsigned num_qpus, unsigned control, unsigned noflush, unsigned timeout); +unsigned execute_code(int file_desc, unsigned code, unsigned r0, unsigned r1, + unsigned r2, unsigned r3, unsigned r4, unsigned r5); +unsigned execute_qpu(int file_desc, unsigned num_qpus, unsigned control, + unsigned noflush, unsigned timeout); unsigned qpu_enable(int file_desc, unsigned enable); + +#endif diff --git a/nhash.c b/nhash.c new file mode 100644 index 0000000..bbce1a2 --- /dev/null +++ b/nhash.c @@ -0,0 +1,285 @@ +/* +------------------------------------------------------------------------------- +lookup3.c, by Bob Jenkins, May 2006, Public Domain. + +These are functions for producing 32-bit hashes for hash table lookup. +hashword(), hashlittle(), hashlittle2(), hashbig(), mix(), and final() +are externally useful functions. Routines to test the hash are included +if SELF_TEST is defined. You can use this free for any purpose. It's in +the public domain. It has no warranty. + +You probably want to use hashlittle(). hashlittle() and hashbig() +hash byte arrays. hashlittle() is is faster than hashbig() on +little-endian machines. Intel and AMD are little-endian machines. +On second thought, you probably want hashlittle2(), which is identical to +hashlittle() except it returns two 32-bit hashes for the price of one. +You could implement hashbig2() if you wanted but I haven't bothered here. + +If you want to find a hash of, say, exactly 7 integers, do + a = i1; b = i2; c = i3; + mix(a,b,c); + a += i4; b += i5; c += i6; + mix(a,b,c); + a += i7; + final(a,b,c); +then use c as the hash value. If you have a variable length array of +4-byte integers to hash, use hashword(). If you have a byte array (like +a character string), use hashlittle(). If you have several byte arrays, or +a mix of things, see the comments above hashlittle(). + +Why is this so big? I read 12 bytes at a time into 3 4-byte integers, +then mix those integers. This is fast (you can do a lot more thorough +mixing with 12*3 instructions on 3 integers than you can with 3 instructions +on 1 byte), but shoehorning those bytes into integers efficiently is messy. +------------------------------------------------------------------------------- +*/ + +#include "nhash.h" + +uint32_t nhash(const void *key, int length, uint32_t initval) { + uint32_t a, b, c; /* internal state */ + union { + const void *ptr; + size_t i; + } u; /* needed for Mac Powerbook G4 */ + + /* Set up the internal state */ + a = b = c = 0xdeadbeef + ((uint32_t)length) + initval; + + u.ptr = key; + if (HASH_LITTLE_ENDIAN && ((u.i & 0x3) == 0)) { + const uint32_t *k = (const uint32_t *)key; /* read 32-bit chunks */ + + /*------ all but last block: aligned reads and affect 32 bits of (a,b,c) */ + while (length > 12) { + a += k[0]; + b += k[1]; + c += k[2]; + mix(a, b, c); + length -= 12; + k += 3; + } + +/*----------------------------- handle the last (probably partial) block */ +/* + * "k[2]&0xffffff" actually reads beyond the end of the string, but + * then masks off the part it's not allowed to read. Because the + * string is aligned, the masked-off tail is in the same word as the + * rest of the string. Every machine with memory protection I've seen + * does it on word boundaries, so is OK with this. But VALGRIND will + * still catch it and complain. The masking trick does make the hash + * noticably faster for short strings (like English words). + */ +#ifndef VALGRIND + + switch (length) { + case 12: + c += k[2]; + b += k[1]; + a += k[0]; + break; + case 11: + c += k[2] & 0xffffff; + b += k[1]; + a += k[0]; + break; + case 10: + c += k[2] & 0xffff; + b += k[1]; + a += k[0]; + break; + case 9: + c += k[2] & 0xff; + b += k[1]; + a += k[0]; + break; + case 8: + b += k[1]; + a += k[0]; + break; + case 7: + b += k[1] & 0xffffff; + a += k[0]; + break; + case 6: + b += k[1] & 0xffff; + a += k[0]; + break; + case 5: + b += k[1] & 0xff; + a += k[0]; + break; + case 4: + a += k[0]; + break; + case 3: + a += k[0] & 0xffffff; + break; + case 2: + a += k[0] & 0xffff; + break; + case 1: + a += k[0] & 0xff; + break; + case 0: + return c; /* zero length strings require no mixing */ + } + +#else /* make valgrind happy */ + const uint8_t *k8; + + k8 = (const uint8_t *)k; + switch (length) { + case 12: + c += k[2]; + b += k[1]; + a += k[0]; + break; + case 11: + c += ((uint32_t)k8[10]) << 16; /* fall through */ + case 10: + c += ((uint32_t)k8[9]) << 8; /* fall through */ + case 9: + c += k8[8]; /* fall through */ + case 8: + b += k[1]; + a += k[0]; + break; + case 7: + b += ((uint32_t)k8[6]) << 16; /* fall through */ + case 6: + b += ((uint32_t)k8[5]) << 8; /* fall through */ + case 5: + b += k8[4]; /* fall through */ + case 4: + a += k[0]; + break; + case 3: + a += ((uint32_t)k8[2]) << 16; /* fall through */ + case 2: + a += ((uint32_t)k8[1]) << 8; /* fall through */ + case 1: + a += k8[0]; + break; + case 0: + return c; + } + +#endif /* !valgrind */ + + } else if (HASH_LITTLE_ENDIAN && ((u.i & 0x1) == 0)) { + const uint16_t *k = (const uint16_t *)key; /* read 16-bit chunks */ + const uint8_t *k8; + + /*--------------- all but last block: aligned reads and different mixing */ + while (length > 12) { + a += k[0] + (((uint32_t)k[1]) << 16); + b += k[2] + (((uint32_t)k[3]) << 16); + c += k[4] + (((uint32_t)k[5]) << 16); + mix(a, b, c); + length -= 12; + k += 6; + } + + /*----------------------------- handle the last (probably partial) block */ + k8 = (const uint8_t *)k; + switch (length) { + case 12: + c += k[4] + (((uint32_t)k[5]) << 16); + b += k[2] + (((uint32_t)k[3]) << 16); + a += k[0] + (((uint32_t)k[1]) << 16); + break; + case 11: + c += ((uint32_t)k8[10]) << 16; /* fall through */ + case 10: + c += k[4]; + b += k[2] + (((uint32_t)k[3]) << 16); + a += k[0] + (((uint32_t)k[1]) << 16); + break; + case 9: + c += k8[8]; /* fall through */ + case 8: + b += k[2] + (((uint32_t)k[3]) << 16); + a += k[0] + (((uint32_t)k[1]) << 16); + break; + case 7: + b += ((uint32_t)k8[6]) << 16; /* fall through */ + case 6: + b += k[2]; + a += k[0] + (((uint32_t)k[1]) << 16); + break; + case 5: + b += k8[4]; /* fall through */ + case 4: + a += k[0] + (((uint32_t)k[1]) << 16); + break; + case 3: + a += ((uint32_t)k8[2]) << 16; /* fall through */ + case 2: + a += k[0]; + break; + case 1: + a += k8[0]; + break; + case 0: + return c; /* zero length requires no mixing */ + } + + } else { /* need to read the key one byte at a time */ + const uint8_t *k = (const uint8_t *)key; + + /*--------------- all but the last block: affect some 32 bits of (a,b,c) */ + while (length > 12) { + a += k[0]; + a += ((uint32_t)k[1]) << 8; + a += ((uint32_t)k[2]) << 16; + a += ((uint32_t)k[3]) << 24; + b += k[4]; + b += ((uint32_t)k[5]) << 8; + b += ((uint32_t)k[6]) << 16; + b += ((uint32_t)k[7]) << 24; + c += k[8]; + c += ((uint32_t)k[9]) << 8; + c += ((uint32_t)k[10]) << 16; + c += ((uint32_t)k[11]) << 24; + mix(a, b, c); + length -= 12; + k += 12; + } + + /*-------------------------------- last block: affect all 32 bits of (c) */ + switch (length) /* all the case statements fall through */ + { + case 12: + c += ((uint32_t)k[11]) << 24; + case 11: + c += ((uint32_t)k[10]) << 16; + case 10: + c += ((uint32_t)k[9]) << 8; + case 9: + c += k[8]; + case 8: + b += ((uint32_t)k[7]) << 24; + case 7: + b += ((uint32_t)k[6]) << 16; + case 6: + b += ((uint32_t)k[5]) << 8; + case 5: + b += k[4]; + case 4: + a += ((uint32_t)k[3]) << 24; + case 3: + a += ((uint32_t)k[2]) << 16; + case 2: + a += ((uint32_t)k[1]) << 8; + case 1: + a += k[0]; + break; + case 0: + return c; + } + } + + final(a, b, c); + return c; +} \ No newline at end of file diff --git a/nhash.h b/nhash.h new file mode 100644 index 0000000..f123774 --- /dev/null +++ b/nhash.h @@ -0,0 +1,197 @@ +/* +------------------------------------------------------------------------------- +lookup3.c, by Bob Jenkins, May 2006, Public Domain. + +These are functions for producing 32-bit hashes for hash table lookup. +hashword(), hashlittle(), hashlittle2(), hashbig(), mix(), and final() +are externally useful functions. Routines to test the hash are included +if SELF_TEST is defined. You can use this free for any purpose. It's in +the public domain. It has no warranty. + +You probably want to use hashlittle(). hashlittle() and hashbig() +hash byte arrays. hashlittle() is is faster than hashbig() on +little-endian machines. Intel and AMD are little-endian machines. +On second thought, you probably want hashlittle2(), which is identical to +hashlittle() except it returns two 32-bit hashes for the price of one. +You could implement hashbig2() if you wanted but I haven't bothered here. + +If you want to find a hash of, say, exactly 7 integers, do + a = i1; b = i2; c = i3; + mix(a,b,c); + a += i4; b += i5; c += i6; + mix(a,b,c); + a += i7; + final(a,b,c); +then use c as the hash value. If you have a variable length array of +4-byte integers to hash, use hashword(). If you have a byte array (like +a character string), use hashlittle(). If you have several byte arrays, or +a mix of things, see the comments above hashlittle(). + +Why is this so big? I read 12 bytes at a time into 3 4-byte integers, +then mix those integers. This is fast (you can do a lot more thorough +mixing with 12*3 instructions on 3 integers than you can with 3 instructions +on 1 byte), but shoehorning those bytes into integers efficiently is messy. +------------------------------------------------------------------------------- +*/ +#ifndef _NHASH_H +#define _NHASH_H + +#define SELF_TEST 1 + +#include /* defines printf for tests */ +#include /* defines time_t for timings in the test */ +#ifdef Win32 +#include "win_stdint.h" /* defines uint32_t etc */ +#else +#include /* defines uint32_t etc */ +#endif +//#include /* attempt to define endianness */ +//#ifdef linux +//# include /* attempt to define endianness */ +//#endif + +#define HASH_LITTLE_ENDIAN 1 + +#define hashsize(n) ((uint32_t)1 << (n)) +#define hashmask(n) (hashsize(n) - 1) +#define rot(x, k) (((x) << (k)) | ((x) >> (32 - (k)))) + +/* +------------------------------------------------------------------------------- +mix -- mix 3 32-bit values reversibly. + +This is reversible, so any information in (a,b,c) before mix() is +still in (a,b,c) after mix(). + +If four pairs of (a,b,c) inputs are run through mix(), or through +mix() in reverse, there are at least 32 bits of the output that +are sometimes the same for one pair and different for another pair. +This was tested for: +* pairs that differed by one bit, by two bits, in any combination + of top bits of (a,b,c), or in any combination of bottom bits of + (a,b,c). +* "differ" is defined as +, -, ^, or ~^. For + and -, I transformed + the output delta to a Gray code (a^(a>>1)) so a string of 1's (as + is commonly produced by subtraction) look like a single 1-bit + difference. +* the base values were pseudorandom, all zero but one bit set, or + all zero plus a counter that starts at zero. + +Some k values for my "a-=c; a^=rot(c,k); c+=b;" arrangement that +satisfy this are + 4 6 8 16 19 4 + 9 15 3 18 27 15 + 14 9 3 7 17 3 +Well, "9 15 3 18 27 15" didn't quite get 32 bits diffing +for "differ" defined as + with a one-bit base and a two-bit delta. I +used http://burtleburtle.net/bob/hash/avalanche.html to choose +the operations, constants, and arrangements of the variables. + +This does not achieve avalanche. There are input bits of (a,b,c) +that fail to affect some output bits of (a,b,c), especially of a. The +most thoroughly mixed value is c, but it doesn't really even achieve +avalanche in c. + +This allows some parallelism. Read-after-writes are good at doubling +the number of bits affected, so the goal of mixing pulls in the opposite +direction as the goal of parallelism. I did what I could. Rotates +seem to cost as much as shifts on every machine I could lay my hands +on, and rotates are much kinder to the top and bottom bits, so I used +rotates. +------------------------------------------------------------------------------- +*/ +#define mix(a, b, c) \ + { \ + (a) -= (c); \ + (a) ^= rot(c, 4); \ + (c) += (b); \ + (b) -= (a); \ + (b) ^= rot(a, 6); \ + (a) += (c); \ + (c) -= (b); \ + (c) ^= rot(b, 8); \ + (b) += (a); \ + (a) -= (c); \ + (a) ^= rot(c, 16); \ + (c) += (b); \ + (b) -= (a); \ + (b) ^= rot(a, 19); \ + (a) += (c); \ + (c) -= (b); \ + (c) ^= rot(b, 4); \ + (b) += (a); \ + } + +/* +------------------------------------------------------------------------------- +final -- final mixing of 3 32-bit values (a,b,c) into c + +Pairs of (a,b,c) values differing in only a few bits will usually +produce values of c that look totally different. This was tested for +* pairs that differed by one bit, by two bits, in any combination + of top bits of (a,b,c), or in any combination of bottom bits of + (a,b,c). +* "differ" is defined as +, -, ^, or ~^. For + and -, I transformed + the output delta to a Gray code (a^(a>>1)) so a string of 1's (as + is commonly produced by subtraction) look like a single 1-bit + difference. +* the base values were pseudorandom, all zero but one bit set, or + all zero plus a counter that starts at zero. + +These constants passed: + 14 11 25 16 4 14 24 + 12 14 25 16 4 14 24 +and these came close: + 4 8 15 26 3 22 24 + 10 8 15 26 3 22 24 + 11 8 15 26 3 22 24 +------------------------------------------------------------------------------- +*/ +#define final(a, b, c) \ + { \ + (c) ^= (b); \ + (c) -= rot(b, 14); \ + (a) ^= (c); \ + (a) -= rot(c, 11); \ + (b) ^= (a); \ + (b) -= rot(a, 25); \ + (c) ^= (b); \ + (c) -= rot(b, 16); \ + (a) ^= (c); \ + (a) -= rot(c, 4); \ + (b) ^= (a); \ + (b) -= rot(a, 14); \ + (c) ^= (b); \ + (c) -= rot(b, 24); \ + } + +/* +------------------------------------------------------------------------------- +hashlittle() -- hash a variable-length key into a 32-bit value + k : the key (the unaligned variable-length array of bytes) + length : the length of the key, counting by bytes + initval : can be any 4-byte value +Returns a 32-bit value. Every bit of the key affects every bit of +the return value. Two keys differing by one or two bits will have +totally different hash values. + +The best hash table sizes are powers of 2. There is no need to do +mod a prime (mod is sooo slow!). If you need less than 32 bits, +use a bitmask. For example, if you need only 10 bits, do + h = (h & hashmask(10)); +In which case, the hash table should have hashsize(10) elements. + +If you are hashing n strings (uint8_t **)k, do it like this: + for (i=0, h=0; i -#include -#include -#include -#include -#include -#include +#include +#include #include #include +#include +#include #include -#include -#include -#include -#include -#include -#include -#include -#include #include -#include -#include -#include #include -#include +#include +#include +#include #include +#include +#include +#include +#include +#include +#include +#include +#include #include +#include +#include +#include +#include #ifdef __cplusplus extern "C" { #include "mailbox.h" +#include "nhash.h" } #endif /* __cplusplus */ - // Note on accessing memory in RPi: // // There are 3 (yes three) address spaces in the Pi: @@ -103,7 +103,9 @@ extern "C" { #define ABORT(a) exit(a) // Used for debugging -#define MARK std::cout << "Currently in file: " << __FILE__ << " line: " << __LINE__ << std::endl +#define MARK \ + std::cout << "Currently in file: " << __FILE__ << " line: " << __LINE__ \ + << std::endl // PLLD clock frequency. // For RPi1, after NTP converges, these is a 2.5 PPM difference between @@ -111,10 +113,10 @@ extern "C" { // the crystal. This 2.5 PPM offset is not present in the RPi2 and RPi3. // This 2.5 PPM offset is compensated for here, but only for the RPi1. #ifdef RPI23 -#define F_PLLD_CLK (500000000.0) +#define F_PLLD_CLK (500000000.0) #else #ifdef RPI1 -#define F_PLLD_CLK (500000000.0*(1-2.500e-6)) +#define F_PLLD_CLK (500000000.0 * (1 - 2.500e-6)) #else #error "RPI version macro is not defined" #endif @@ -126,7 +128,7 @@ extern "C" { #define F_PWM_CLK_INIT (31156186.6125761) // WSRP nominal symbol time -#define WSPR_SYMTIME (8192.0/12000.0) +#define WSPR_SYMTIME (8192.0 / 12000.0) // How much random frequency offset should be added to WSPR transmissions // if the --offset option has been turned on. #define WSPR_RAND_OFFSET 80 @@ -147,15 +149,15 @@ extern "C" { #endif #endif -#define PAGE_SIZE (4*1024) -#define BLOCK_SIZE (4*1024) +#define PAGE_SIZE (4 * 1024) +#define BLOCK_SIZE (4 * 1024) // peri_base_virt is the base virtual address that a userspace program (this // program) can use to read/write to the the physical addresses controlling // the peripherals. This address is mapped at runtime using mmap and /dev/mem. // This must be declared global so that it can be called by the atexit // function. -volatile unsigned *peri_base_virt = NULL; +volatile unsigned *peri_base_virt = nullptr; // Given an address in the bus address space of the peripherals, this // macro calculates the appropriate virtual address to use to access @@ -163,75 +165,77 @@ volatile unsigned *peri_base_virt = NULL; // 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) +#define ACCESS_BUS_ADDR(buss_addr) \ + *(volatile int *)((long int)peri_base_virt + (buss_addr)-0x7e000000) // Given a bus address in the peripheral address space, set or clear a bit. -#define SETBIT_BUS_ADDR(base, bit) ACCESS_BUS_ADDR(base) |= 1<=mbox.pool_size) { +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); + unsigned offset = mbox.pool_cnt * 4096; + *vAddr = (void *)(((unsigned)mbox.virt_addr) + offset); + *bAddr = (void *)(((unsigned)mbox.bus_addr) + offset); + // printf("getRealMemoryPageFromPool bus_addr=%x virt_addr=%x\n", + // (unsigned)*pAddr,(unsigned)*vAddr); mbox.pool_cnt++; } // Free the memory pool void deallocMemPool() { - if(mbox.virt_addr!=NULL) { - unmapmem(mbox.virt_addr, mbox.pool_size*4096); + if (mbox.virt_addr != nullptr) { + unmapmem(mbox.virt_addr, mbox.pool_size * 4096); } - if (mbox.mem_ref!=0) { + if (mbox.mem_ref != 0) { mem_unlock(mbox.handle, mbox.mem_ref); mem_free(mbox.handle, mbox.mem_ref); } @@ -282,19 +289,19 @@ 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) { + if (peri_base_virt == nullptr) { 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); + auto settings = ACCESS_BUS_ADDR(CM_GP0CTL_BUS); // Clear enable bit and add password - settings=(settings&0x7EF)|0x5A000000; + settings = (settings & 0x7EF) | 0x5A000000; // Disable - ACCESS_BUS_ADDR(CM_GP0CTL_BUS) = *((int*)&settings); + 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))) { + if ((ACCESS_BUS_ADDR(CM_GP0CTL_BUS) & (1 << 7)) == 0) { break; } } @@ -314,34 +321,36 @@ void txon() { // 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 + 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 disable_clock(); // Set clock source as PLLD. - struct GPCTL setupword = {6/*SRC*/, 0, 0, 0, 0, 3,0x5a}; + 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); + setupword = {6 /*SRC*/, 1, 0, 0, 0, 3, 0x5a}; + ACCESS_BUS_ADDR(CM_GP0CTL_BUS) = *((int *)&setupword); } // Turn transmitter on void txoff() { - //struct GPCTL setupword = {6/*SRC*/, 0, 0, 0, 0, 1,0x5a}; - //ACCESS_BUS_ADDR(CM_GP0CTL_BUS) = *((int*)&setupword); + // struct GPCTL setupword = {6/*SRC*/, 0, 0, 0, 0, 1,0x5a}; + // ACCESS_BUS_ADDR(CM_GP0CTL_BUS) = *((int*)&setupword); disable_clock(); } @@ -351,155 +360,160 @@ void txoff() { // Upon entering this function at the beginning of a WSPR transmission, we // do not know which DMA table entry is being processed by the DMA engine. #define PWM_CLOCKS_PER_ITER_NOMINAL 1000 -void txSym( - const int & sym_num, - const double & center_freq, - const double & tone_spacing, - const double & tsym, - const std::vector & dma_table_freq, - const double & f_pwm_clk, - struct PageInfo instrs[], - struct PageInfo & constPage, - int & bufPtr -) { - const int f0_idx=sym_num*2; - const int f1_idx=f0_idx+1; - const double f0_freq=dma_table_freq[f0_idx]; - const double f1_freq=dma_table_freq[f1_idx]; - const double tone_freq=center_freq-1.5*tone_spacing+sym_num*tone_spacing; +void txSym(const int &sym_num, const double ¢er_freq, + const double &tone_spacing, const double &tsym, + const std::vector &dma_table_freq, const double &f_pwm_clk, + struct PageInfo instrs[], struct PageInfo &constPage, int &bufPtr) { + const int f0_idx = sym_num * 2; + const int f1_idx = f0_idx + 1; + const double f0_freq = dma_table_freq[f0_idx]; + const double f1_freq = dma_table_freq[f1_idx]; + const double tone_freq = + center_freq - 1.5 * tone_spacing + sym_num * tone_spacing; // Double check... - assert((tone_freq>=f0_freq)&&(tone_freq<=f1_freq)); - const double f0_ratio=1.0-(tone_freq-f0_freq)/(f1_freq-f0_freq); - //cout << "f0_ratio = " << f0_ratio << std::endl; - assert ((f0_ratio>=0)&&(f0_ratio<=1)); - const long int n_pwmclk_per_sym=round(f_pwm_clk*tsym); - - long int n_pwmclk_transmitted=0; - long int n_f0_transmitted=0; - //printf("",(unsigned)&instrs[bufPtr]); - while (n_pwmclk_transmitted= f0_freq) && (tone_freq <= f1_freq)); + const double f0_ratio = 1.0 - (tone_freq - f0_freq) / (f1_freq - f0_freq); + // cout << "f0_ratio = " << f0_ratio << std::endl; + assert((f0_ratio >= 0) && (f0_ratio <= 1)); + const long int n_pwmclk_per_sym = round(f_pwm_clk * tsym); + + long int n_pwmclk_transmitted = 0; + long int n_f0_transmitted = 0; + // printf("",(unsigned)&instrs[bufPtr]); + while (n_pwmclk_transmitted < n_pwmclk_per_sym) { // Number of PWM clocks for this iteration - long int n_pwmclk=PWM_CLOCKS_PER_ITER_NOMINAL; + long int n_pwmclk = PWM_CLOCKS_PER_ITER_NOMINAL; // Iterations may produce spurs around the main peak based on the iteration // frequency. Randomize the iteration period so as to spread this peak // around. - n_pwmclk+=round((rand()/((double)RAND_MAX+1.0)-.5)*n_pwmclk)*1; - if (n_pwmclk_transmitted+n_pwmclk>n_pwmclk_per_sym) { - n_pwmclk=n_pwmclk_per_sym-n_pwmclk_transmitted; + n_pwmclk += round((rand() / ((double)RAND_MAX + 1.0) - .5) * n_pwmclk) * 1; + if (n_pwmclk_transmitted + n_pwmclk > n_pwmclk_per_sym) { + n_pwmclk = n_pwmclk_per_sym - n_pwmclk_transmitted; } // Calculate number of clocks to transmit f0 during this iteration so // that the long term average is as close to f0_ratio as possible. - const long int n_f0=round(f0_ratio*(n_pwmclk_transmitted+n_pwmclk))-n_f0_transmitted; - const long int n_f1=n_pwmclk-n_f0; + const long int n_f0 = + round(f0_ratio * (n_pwmclk_transmitted + n_pwmclk)) - n_f0_transmitted; + const long int n_f1 = n_pwmclk - n_f0; // Configure the transmission for this iteration // Set GPIO pin to transmit f0 bufPtr++; - while( ACCESS_BUS_ADDR(DMA_BUS_BASE + 0x04 /* CurBlock*/) == (long int)(instrs[bufPtr].b)) usleep(100); - ((struct CB*)(instrs[bufPtr].v))->SOURCE_AD = (long int)constPage.b + f0_idx*4; + while (ACCESS_BUS_ADDR(DMA_BUS_BASE + 0x04 /* CurBlock*/) == + (long int)(instrs[bufPtr].b)) { + usleep(100); + } + ((struct CB *)(instrs[bufPtr].v))->SOURCE_AD = + (long int)constPage.b + f0_idx * 4; // Wait for n_f0 PWM clocks bufPtr++; - while( ACCESS_BUS_ADDR(DMA_BUS_BASE + 0x04 /* CurBlock*/) == (long int)(instrs[bufPtr].b)) usleep(100); - ((struct CB*)(instrs[bufPtr].v))->TXFR_LEN = n_f0; + while (ACCESS_BUS_ADDR(DMA_BUS_BASE + 0x04 /* CurBlock*/) == + (long int)(instrs[bufPtr].b)) { + usleep(100); + } + ((struct CB *)(instrs[bufPtr].v))->TXFR_LEN = n_f0; // Set GPIO pin to transmit f1 bufPtr++; - while( ACCESS_BUS_ADDR(DMA_BUS_BASE + 0x04 /* CurBlock*/) == (long int)(instrs[bufPtr].b)) usleep(100); - ((struct CB*)(instrs[bufPtr].v))->SOURCE_AD = (long int)constPage.b + f1_idx*4; + while (ACCESS_BUS_ADDR(DMA_BUS_BASE + 0x04 /* CurBlock*/) == + (long int)(instrs[bufPtr].b)) { + usleep(100); + } + ((struct CB *)(instrs[bufPtr].v))->SOURCE_AD = + (long int)constPage.b + f1_idx * 4; // Wait for n_f1 PWM clocks - bufPtr=(bufPtr+1) % (1024); - while( ACCESS_BUS_ADDR(DMA_BUS_BASE + 0x04 /* CurBlock*/) == (long int)(instrs[bufPtr].b)) usleep(100); - ((struct CB*)(instrs[bufPtr].v))->TXFR_LEN = n_f1; + bufPtr = (bufPtr + 1) % (1024); + while (ACCESS_BUS_ADDR(DMA_BUS_BASE + 0x04 /* CurBlock*/) == + (long int)(instrs[bufPtr].b)) { + usleep(100); + } + ((struct CB *)(instrs[bufPtr].v))->TXFR_LEN = n_f1; // Update counters - n_pwmclk_transmitted+=n_pwmclk; - n_f0_transmitted+=n_f0; + n_pwmclk_transmitted += n_pwmclk; + n_f0_transmitted += n_f0; } - //printf("",(unsigned)instrs[bufPtr].v,(unsigned)instrs[bufPtr].b); + // printf("",(unsigned)instrs[bufPtr].v,(unsigned)instrs[bufPtr].b); } // Turn off (reset) DMA engine -void unSetupDMA(){ +void unSetupDMA() { // Check if mapping has been set up yet. - if (peri_base_virt==NULL) { + if (peri_base_virt == nullptr) { return; } - //cout << "Exiting!" << std::endl; - struct DMAregs* DMA0 = (struct DMAregs*)&(ACCESS_BUS_ADDR(DMA_BUS_BASE)); - DMA0->CS =1<<31; // reset dma controller + // 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. -double bit_trunc( - const double & d, - const int & lsb -) { - return floor(d/pow(2.0,lsb))*pow(2.0,lsb); +double bit_trunc(const double &d, const int &lsb) { + return floor(d / pow(2.0, lsb)) * pow(2.0, lsb); } // Program the tuning words into the DMA table. -void setupDMATab( - const double & center_freq_desired, - const double & tone_spacing, - const double & plld_actual_freq, - std::vector & dma_table_freq, - double & center_freq_actual, - struct PageInfo & constPage -){ +void setupDMATab(const double ¢er_freq_desired, const double &tone_spacing, + const double &plld_actual_freq, + std::vector &dma_table_freq, + double ¢er_freq_actual, struct PageInfo &constPage) { // Make sure that all the WSPR tones can be produced solely by // varying the fractional part of the frequency divider. - center_freq_actual=center_freq_desired; - double div_lo=bit_trunc(plld_actual_freq/(center_freq_desired-1.5*tone_spacing),-12)+pow(2.0,-12); - double div_hi=bit_trunc(plld_actual_freq/(center_freq_desired+1.5*tone_spacing),-12); - if (floor(div_lo)!=floor(div_hi)) { - center_freq_actual=plld_actual_freq/floor(div_lo)-1.6*tone_spacing; + center_freq_actual = center_freq_desired; + double div_lo = + bit_trunc(plld_actual_freq / (center_freq_desired - 1.5 * tone_spacing), + -12) + + pow(2.0, -12); + double div_hi = bit_trunc( + plld_actual_freq / (center_freq_desired + 1.5 * tone_spacing), -12); + if (floor(div_lo) != floor(div_hi)) { + center_freq_actual = plld_actual_freq / floor(div_lo) - 1.6 * tone_spacing; std::stringstream temp; - temp << std::setprecision(6) << std::fixed << " Warning: center frequency has been changed to " << center_freq_actual/1e6 << " MHz" << std::endl; + temp << std::setprecision(6) << std::fixed + << " Warning: center frequency has been changed to " + << center_freq_actual / 1e6 << " MHz" << std::endl; std::cout << temp.str(); std::cout << " because of hardware limitations!" << std::endl; } // Create DMA table of tuning words. WSPR tone i will use entries 2*i and // 2*i+1 to generate the appropriate tone. - double tone0_freq=center_freq_actual-1.5*tone_spacing; - std::vector tuning_word(1024); - for (int i=0;i<8;i++) { - double tone_freq=tone0_freq+(i>>1)*tone_spacing; - double div=bit_trunc(plld_actual_freq/tone_freq,-12); - if (i%2==0) { - div=div+pow(2.0,-12); + double tone0_freq = center_freq_actual - 1.5 * tone_spacing; + std::vector tuning_word(1024); + for (int i = 0; i < 8; i++) { + double tone_freq = tone0_freq + (i >> 1) * tone_spacing; + double div = bit_trunc(plld_actual_freq / tone_freq, -12); + if (i % 2 == 0) { + div = div + pow(2.0, -12); } - tuning_word[i]=((int)(div*pow(2.0,12))); + tuning_word[i] = ((int)(div * pow(2.0, 12))); } // Fill the remaining table, just in case... - for (int i=8;i<1024;i++) { - double div=500+i; - tuning_word[i]=((int)(div*pow(2.0,12))); + for (int i = 8; i < 1024; i++) { + double div = 500 + i; + tuning_word[i] = ((int)(div * pow(2.0, 12))); } // Program the table dma_table_freq.resize(1024); - for (int i=0;i<1024;i++) { - dma_table_freq[i]=plld_actual_freq/(tuning_word[i]/pow(2.0,12)); - ((int*)(constPage.v))[i] = (0x5a<<24)+tuning_word[i]; - if ((i%2==0)&&(i<8)) { - assert((tuning_word[i]&(~0xfff))==(tuning_word[i+1]&(~0xfff))); + for (int i = 0; i < 1024; i++) { + dma_table_freq[i] = plld_actual_freq / (tuning_word[i] / pow(2.0, 12)); + ((int *)(constPage.v))[i] = (0x5a << 24) + tuning_word[i]; + if ((i % 2 == 0) && (i < 8)) { + assert((tuning_word[i] & (~0xfff)) == (tuning_word[i + 1] & (~0xfff))); } } - } // Create the memory structures needed by the DMA engine and perform initial // clock configuration. -void setupDMA( - struct PageInfo & constPage, - struct PageInfo & instrPage, - struct PageInfo instrs[] -){ +void setupDMA(struct PageInfo &constPage, struct PageInfo &instrPage, + struct PageInfo instrs[]) { allocMemPool(1025); // Allocate a page of ram for the constants @@ -509,7 +523,7 @@ void setupDMA( // Even instructions target the GP0 Clock divider // Odd instructions target the PWM FIFO int instrCnt = 0; - while (instrCnt<1024) { + while (instrCnt < 1024) { // Allocate a page of ram for the instructions getRealMemPageFromPool(&instrPage.v, &instrPage.b); @@ -517,197 +531,244 @@ void setupDMA( // 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; + 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 */; + 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->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) { + if ((i % 2) != 0) { instr0->DEST_AD = CM_GP0DIV_BUS; instr0->STRIDE = 4; - instr0->TI = (1<<26/* no wide*/) ; + instr0->TI = (1 << 26 /* no wide*/); } - if (instrCnt!=0) ((struct CB*)(instrs[instrCnt-1].v))->NEXTCONBK = (long int)instrs[instrCnt].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; + ((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 + 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 + // 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 + 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 */) ; + 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; + ACCESS_BUS_ADDR(PWM_BUS_BASE + 0x8 /* DMAC*/) = + (1 << 31 /* DMA enable */) | 0x0707; + + // activate dma + struct DMAregs *DMA0 = (struct DMAregs *)&(ACCESS_BUS_ADDR(DMA_BUS_BASE)); + DMA0->CS = 1 << 31; // reset + DMA0->CONBLK_AD = 0; + DMA0->TI = 0; DMA0->CONBLK_AD = (unsigned long int)(instrPage.b); - DMA0->CS =(1<<0)|(255 <<16); // enable bit = 0, clear end flag = 1, prio=19-16 -} - -// Convert string to uppercase -void to_upper( - char *str -) { - while(*str) { - *str = toupper(*str); - str++; - } + DMA0->CS = + (1 << 0) | (255 << 16); // enable bit = 0, clear end flag = 1, prio=19-16 } // 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]; +// Assumes that both callsign and locator are uppercase. This is done in +// parse_commandline(), but needs to be ensured if calling this function +// manually. +void wspr(const char *call, const char *l_pre, + const int dbm, // EIRP in + // dBm={0,3,7,10,13,17,20,23,27,30,33,37,40,43,47,50,53,57,60} + unsigned char *symbols) { + // pack 'prefix' in nadd, 'call' in n1, and 'grid' and 'dbm' in n2 + char *c, buf[17]; 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; + buf[16] = '\0'; + c = buf; + unsigned long ng = 0, ih = 0, nadd = 0; + unsigned long n1, n2; + + if (strnlen(l_pre, 6) == 6) { + // When sending a 6 character grid, the callsign is hashed, then the grid + // wrapped around one character and used as the callsign. + ih = nhash(c, strnlen(c, 16), 146) & 0x7fff; // 15 bit mask + + c[0] = l_pre[1]; + c[1] = l_pre[2]; + c[2] = l_pre[3]; + c[3] = l_pre[4]; + c[4] = l_pre[5]; + c[5] = l_pre[0]; + c[6] = '\0'; + } + + if (strchr(c, '/') != nullptr) { // 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 i = + (isdigit(c[2]) != 0 + ? 2 + : isdigit(c[1]) != 0 + ? 1 + : 0); // last prefix digit of de-suffixed/de-prefixed callsign + int n = strlen(c) - i - 1; // 2nd part of call len + 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 ((nadd == 0u) && strnlen(l_pre, 6) == 4) { + // Encode the 4 character location + ng = 180 * (179 - 10 * (l_pre[0] - 'A') - (l_pre[2] - '0')) + + 10 * (l_pre[1] - 'A') + (l_pre[3] - '0'); + } + int corr[] = {0, -1, 1, 0, -1, 2, 1, 0, -1, 1}; + int p = dbm > 60 ? 60 : dbm < 0 ? 0 : dbm + corr[dbm % 10]; + if (strnlen(l_pre, 6) == 4) { + n2 = (ng << 7) | (p + 64 + nadd); + } else { + n2 = (ih << 7) | (-(p + 1) + 64); } - int p = atoi(dbm); //EIRP in dBm={0,3,7,10,13,17,20,23,27,30,33,37,40,43,47,50,53,57,60} - int corr[]={0,-1,1,0,-1,2,1,0,-1,1}; - p=p>60?60:p<0?0:p+corr[p%10]; - unsigned long n2=(ng<<7)|(p+64+nadd); // pack n1,n2,zero-tail into 50 bits char packed[11] = { - static_cast(n1>>20), - static_cast(n1>>12), - static_cast(n1>>4), - static_cast(((n1&0x0f)<<4)|((n2>>18)&0x0f)), - static_cast(n2>>10), - static_cast(n2>>2), - static_cast((n2&0x03)<<6), - 0, - 0, - 0, - 0 - }; + static_cast(n1 >> 20), + static_cast(n1 >> 12), + static_cast(n1 >> 4), + static_cast(((n1 & 0x0f) << 4) | ((n2 >> 18) & 0x0f)), + static_cast(n2 >> 10), + static_cast(n2 >> 2), + static_cast((n2 & 0x03) << 6), + 0, + 0, + 0, + 0}; // convolutional encoding K=32, r=1/2, Layland-Lushbaugh polynomials int k = 0; - int j,s; + int j, s; int nstate = 0; unsigned char symbol[176]; - for(j=0;j!=sizeof(packed);j++){ - for(i=7;i>=0;i--){ - unsigned long poly[2] = { 0xf2d05351L, 0xe4613c47L }; - nstate = (nstate<<1) | ((packed[j]>>i)&1); - for(s=0;s!=2;s++){ //convolve - unsigned long n = nstate & poly[s]; - int even = 0; // even := parity(n) - while(n){ - even = 1 - even; - n = n & (n - 1); - } - symbol[k] = even; - k++; + for (j = 0; j != sizeof(packed); j++) { + for (i = 7; i >= 0; i--) { + unsigned long poly[2] = {0xf2d05351L, 0xe4613c47L}; + nstate = (nstate << 1) | ((packed[j] >> i) & 1); + for (s = 0; s != 2; s++) { // convolve + unsigned long n = nstate & poly[s]; + int even = 0; // even := parity(n) + while (n != 0u) { + 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 + const unsigned char sync_symbols[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] = sync_symbols[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(;;){ + struct tm *ptm; + for (;;) { time(&t); ptm = gmtime(&t); - if((ptm->tm_min % minute) == 0 && ptm->tm_sec == 0) break; + if ((ptm->tm_min % minute) == 0 && ptm->tm_sec == 0) { + break; + } usleep(1000); } usleep(1000000); // wait another second @@ -715,7 +776,8 @@ void wait_every( void print_usage() { std::cout << "Usage:" << std::endl; - std::cout << " wspr [options] callsign locator tx_pwr_dBm f1 ..." << 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; @@ -723,211 +785,235 @@ void print_usage() { 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 + << " Known PPM correction to 19.2MHz RPi nominal crystal frequency." + << std::endl; std::cout << " -s --self-calibration" << std::endl; - std::cout << " Check NTP before every transmission to obtain the PPM error of the" << 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 << " 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 << " 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 << " 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 << " 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 << " +/- " << 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 + << " 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 + << " 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 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 << "Frequencies can be specified either as an absolute TX carrier " + "frequency, or" + << 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; std::cout << std::endl; - std::cout << "Transmission gaps can be created by specifying a TX frequency of 0" << std::endl; + std::cout + << "Transmission gaps can be created by specifying a TX frequency of 0" + << std::endl; + std::cout << std::endl; + std::cout << "This program supports both 4 and 6 character Maidenhead grid " + "locators." + << std::endl; } void parse_commandline( - // Inputs - const int & argc, - char * const argv[], - // Outputs - std::string & callsign, - std::string & locator, - std::string & tx_power, - std::vector & center_freq_set, - double & ppm, - bool & self_cal, - bool & repeat, - bool & random_offset, - double & test_tone, - bool & no_delay, - mode_type & mode, - int & terminate -) { + // Inputs + const int &argc, char *const argv[], + // Outputs + std::string &callsign, std::string &locator, int &tx_power, + std::vector ¢er_freq_set, double &ppm, bool &self_cal, + bool &repeat, bool &random_offset, double &test_tone, bool &no_delay, + mode_type &mode, int &terminate) { // Default values - ppm=0; - self_cal=true; - repeat=false; - random_offset=false; - test_tone=NAN; - no_delay=false; - mode=WSPR; - terminate=-1; + tx_power = -1; + ppm = 0; + self_cal = true; + repeat = false; + random_offset = false; + test_tone = NAN; + no_delay = false; + mode = WSPR; + terminate = -1; static struct option long_options[] = { - {"help", no_argument, 0, 'h'}, - {"ppm", required_argument, 0, 'p'}, - {"self-calibration", no_argument, 0, 's'}, - {"free-running", no_argument, 0, 'f'}, - {"repeat", no_argument, 0, 'r'}, - {"terminate", required_argument, 0, 'x'}, - {"offset", no_argument, 0, 'o'}, - {"test-tone", required_argument, 0, 't'}, - {"no-delay", no_argument, 0, 'n'}, - {0, 0, 0, 0} - }; + {"help", no_argument, nullptr, 'h'}, + {"ppm", required_argument, nullptr, 'p'}, + {"self-calibration", no_argument, nullptr, 's'}, + {"free-running", no_argument, nullptr, 'f'}, + {"repeat", no_argument, nullptr, 'r'}, + {"terminate", required_argument, nullptr, 'x'}, + {"offset", no_argument, nullptr, 'o'}, + {"test-tone", required_argument, nullptr, 't'}, + {"no-delay", no_argument, nullptr, 'n'}, + {nullptr, 0, nullptr, 0}}; while (true) { /* getopt_long stores the option index here. */ int option_index = 0; - int c = getopt_long (argc, argv, "hp:sfrx:ot:n", - long_options, &option_index); - if (c == -1) + int c = + getopt_long(argc, argv, "hp:sfrx:ot:n", long_options, &option_index); + if (c == -1) { break; + } switch (c) { - char * endp; - case 0: - // Code should only get here if a long option was given a non-null - // flag value. - std::cout << "Check code!" << std::endl; + char *endp; + case 0: + // Code should only get here if a long option was given a non-null + // flag value. + std::cout << "Check code!" << std::endl; + ABORT(-1); + break; + case 'h': + print_usage(); + ABORT(-1); + break; + case 'p': + ppm = strtod(optarg, &endp); + if ((optarg == endp) || (*endp != '\0')) { + std::cerr << "Error: could not parse ppm value" << std::endl; ABORT(-1); - break; - case 'h': - print_usage(); + } + break; + case 's': + self_cal = true; + break; + case 'f': + self_cal = false; + break; + case 'r': + repeat = true; + break; + case 'x': + terminate = strtol(optarg, &endp, 10); + if ((optarg == endp) || (*endp != '\0')) { + std::cerr << "Error: could not parse termination argument" << std::endl; ABORT(-1); - break; - case 'p': - ppm=strtod(optarg,&endp); - if ((optarg==endp)||(*endp!='\0')) { - std::cerr << "Error: could not parse ppm value" << std::endl; - ABORT(-1); - } - break; - case 's': - self_cal=true; - break; - case 'f': - self_cal=false; - break; - case 'r': - repeat=true; - break; - case 'x': - terminate=strtol(optarg,&endp,10); - if ((optarg==endp)||(*endp!='\0')) { - std::cerr << "Error: could not parse termination argument" << std::endl; - ABORT(-1); - } - if (terminate<1) { - std::cerr << "Error: termination parameter must be >= 1" << std::endl; - ABORT(-1); - } - break; - case 'o': - random_offset=true; - break; - case 't': - test_tone=strtod(optarg,&endp); - mode=TONE; - if ((optarg==endp)||(*endp!='\0')) { - std::cerr << "Error: could not parse test tone frequency" << std::endl; - ABORT(-1); - } - break; - case 'n': - no_delay=true; - break; - case '?': - /* getopt_long already printed an error message. */ + } + if (terminate < 1) { + std::cerr << "Error: termination parameter must be >= 1" << std::endl; ABORT(-1); - default: + } + break; + case 'o': + random_offset = true; + break; + case 't': + test_tone = strtod(optarg, &endp); + mode = TONE; + if ((optarg == endp) || (*endp != '\0')) { + std::cerr << "Error: could not parse test tone frequency" << std::endl; ABORT(-1); + } + break; + case 'n': + no_delay = true; + break; + case '?': + /* getopt_long already printed an error message. */ + ABORT(-1); + default: + ABORT(-1); } - } // Parse the non-option parameters - unsigned int n_free_args=0; - while (optind0) { - temp << " TX will stop after " << terminate << " transmissions." << std::endl; + if (terminate > 0) { + temp << " TX will stop after " << terminate << " transmissions." + << std::endl; } else if (repeat) { - temp << " Transmissions will continue forever until stopped with CTRL-C" << std::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 transmissions" << std::endl; + temp << " A small random frequency offset will be added to all " + "transmissions" + << std::endl; } - if (temp.str().length()) { + if (temp.str().length() != 0u) { std::cout << "Extra options:" << std::endl; std::cout << temp.str(); } std::cout << std::endl; } else { std::stringstream temp; - temp << std::setprecision(6) << std::fixed << "A test tone will be generated at frequency " << test_tone/1e6 << " MHz" << std::endl; + temp << std::setprecision(6) << std::fixed + << "A test tone will be generated at frequency " << test_tone / 1e6 + << " MHz" << std::endl; std::cout << temp.str(); if (self_cal) { - 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; + std::cout << "NTP will be used to calibrate the tone frequency" + << std::endl; + } else if (ppm != 0.0) { + std::cout << "PPM value to be used to generate the tone: " << ppm + << std::endl; } std::cout << std::endl; } } // Call ntp_adjtime() to obtain the latest calibration coefficient. -void update_ppm( - double & ppm -) { +void update_ppm(double &ppm) { struct timex ntx; int status; double ppm_new; @@ -1018,41 +1119,45 @@ void update_ppm( status = ntp_adjtime(&ntx); if (status != TIME_OK) { - //cerr << "Error: clock not synchronized" << std::endl; - //return; + // cerr << "Error: clock not synchronized" << std::endl; + // return; } - ppm_new = (double)ntx.freq/(double)(1 << 16); /* frequency scale */ - if (abs(ppm_new)>200) { - std::cerr << "Warning: absolute ppm value is greater than 200 and is being ignored!" << std::endl; + ppm_new = (double)ntx.freq / (double)(1 << 16); /* frequency scale */ + if (abs(ppm_new) > 200) { + std::cerr << "Warning: absolute ppm value is greater than 200 and is being " + "ignored!" + << std::endl; } else { - if (ppm!=ppm_new) { + if (ppm != ppm_new) { std::cout << " Obtained new ppm value: " << ppm_new << std::endl; } - ppm=ppm_new; + ppm = ppm_new; } } /* Return 1 if the difference is negative, otherwise 0. */ // From StackOverflow: // http://stackoverflow.com/questions/1468596/c-programming-calculate-elapsed-time-in-milliseconds-unix -int timeval_subtract(struct timeval *result, struct timeval *t2, struct timeval *t1) { - long int diff = (t2->tv_usec + 1000000 * t2->tv_sec) - (t1->tv_usec + 1000000 * t1->tv_sec); - result->tv_sec = diff / 1000000; - result->tv_usec = diff % 1000000; - - return (diff<0); +int timeval_subtract(struct timeval *result, struct timeval *t2, + struct timeval *t1) { + long int diff = (t2->tv_usec + 1000000 * t2->tv_sec) - + (t1->tv_usec + 1000000 * t1->tv_sec); + result->tv_sec = diff / 1000000; + result->tv_usec = diff % 1000000; + + return static_cast(diff < 0); } void timeval_print(struct timeval *tv) { - char buffer[30]; - time_t curtime; - - //printf("%ld.%06ld", tv->tv_sec, tv->tv_usec); - curtime = tv->tv_sec; - //strftime(buffer, 30, "%m-%d-%Y %T", localtime(&curtime)); - strftime(buffer, 30, "UTC %Y-%m-%d %T", gmtime(&curtime)); - printf("%s.%03ld", buffer, (tv->tv_usec+500)/1000); + char buffer[30]; + time_t curtime; + + // printf("%ld.%06ld", tv->tv_sec, tv->tv_usec); + curtime = tv->tv_sec; + // strftime(buffer, 30, "%m-%d-%Y %T", localtime(&curtime)); + strftime(buffer, 30, "UTC %Y-%m-%d %T", gmtime(&curtime)); + printf("%s.%03ld", buffer, (tv->tv_usec + 500) / 1000); } // Create the mbox special files and open mbox. @@ -1080,50 +1185,47 @@ void cleanupAndExit(int sig) { } 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; + sp.sched_priority = priority; int ret = pthread_setschedparam(pthread_self(), SCHED_FIFO, &sp); - if (ret) { - std::cerr << "Warning: pthread_setschedparam (increase thread priority) returned non-zero: " << ret << std::endl; + if (ret != 0) { + 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 -) { +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) { + if ((mem_fd = open("/dev/mem", O_RDWR | O_SYNC)) < 0) { std::cerr << "Error: can't open /dev/mem" << std::endl; - ABORT (-1); + ABORT(-1); } - peri_base_virt = (unsigned *)mmap( - NULL, - 0x01000000, //len - PROT_READ|PROT_WRITE, - MAP_SHARED, - mem_fd, - PERI_BASE_PHYS //base - ); - if ((long int)peri_base_virt==-1) { + peri_base_virt = (unsigned *)mmap(nullptr, + 0x01000000, // len + PROT_READ | PROT_WRITE, MAP_SHARED, mem_fd, + PERI_BASE_PHYS // base + ); + if ((long int)peri_base_virt == -1) { std::cerr << "Error: peri_base_virt mmap error!" << std::endl; ABORT(-1); } close(mem_fd); } -int main(const int argc, char * const argv[]) { - //catch all signals (like ctrl+c, ctrl+z, ...) to ensure DMA is disabled +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); + sigaction(i, &sa, nullptr); } atexit(cleanup); setSchedPriority(30); @@ -1139,13 +1241,13 @@ int main(const int argc, char * const argv[]) { #endif // Initialize the RNG - srand(time(NULL)); + srand(time(nullptr)); // Parse arguments std::string callsign; std::string locator; - std::string tx_power; - std::vector center_freq_set; + int tx_power; + std::vector center_freq_set; double ppm; bool self_cal; bool repeat; @@ -1154,23 +1256,10 @@ int main(const int argc, char * const argv[]) { bool no_delay; mode_type mode; int terminate; - parse_commandline( - argc, - argv, - callsign, - locator, - tx_power, - center_freq_set, - ppm, - self_cal, - repeat, - random_offset, - test_tone, - no_delay, - mode, - terminate - ); - int nbands=center_freq_set.size(); + parse_commandline(argc, argv, callsign, locator, tx_power, center_freq_set, + ppm, self_cal, repeat, random_offset, test_tone, no_delay, + mode, terminate); + int nbands = center_freq_set.size(); // Initial configuration struct PageInfo constPage; @@ -1180,44 +1269,53 @@ int main(const int argc, char * const argv[]) { // Set up DMA open_mbox(); txon(); - setupDMA(constPage,instrPage,instrs); + setupDMA(constPage, instrPage, instrs); txoff(); - if (mode==TONE) { + if (mode == TONE) { // Test tone mode... double wspr_symtime = WSPR_SYMTIME; - double tone_spacing=1.0/wspr_symtime; + double tone_spacing = 1.0 / wspr_symtime; std::stringstream temp; - temp << std::setprecision(6) << std::fixed << "Transmitting test tone on frequency " << test_tone/1.0e6 << " MHz" << std::endl; + temp << std::setprecision(6) << std::fixed + << "Transmitting test tone on frequency " << test_tone / 1.0e6 + << " MHz" << std::endl; std::cout << temp.str(); std::cout << "Press CTRL-C to exit!" << std::endl; txon(); - int bufPtr=0; - std::vector dma_table_freq; + int bufPtr = 0; + std::vector dma_table_freq; // Set to non-zero value to ensure setupDMATab is called at least once. - double ppm_prev=123456; + double ppm_prev = 123456; double center_freq_actual; while (true) { if (self_cal) { update_ppm(ppm); } - if (ppm!=ppm_prev) { - setupDMATab(test_tone+1.5*tone_spacing,tone_spacing,F_PLLD_CLK*(1-ppm/1e6),dma_table_freq,center_freq_actual,constPage); - //cout << std::setprecision(30) << dma_table_freq[0] << std::endl; - //cout << std::setprecision(30) << dma_table_freq[1] << std::endl; - //cout << std::setprecision(30) << dma_table_freq[2] << std::endl; - //cout << std::setprecision(30) << dma_table_freq[3] << std::endl; - if (center_freq_actual!=test_tone+1.5*tone_spacing) { - std::cout << " Warning: because of hardware limitations, test tone will be transmitted on" << std::endl; + if (ppm != ppm_prev) { + setupDMATab(test_tone + 1.5 * tone_spacing, tone_spacing, + F_PLLD_CLK * (1 - ppm / 1e6), dma_table_freq, + center_freq_actual, constPage); + // cout << std::setprecision(30) << dma_table_freq[0] << std::endl; + // cout << std::setprecision(30) << dma_table_freq[1] << std::endl; + // cout << std::setprecision(30) << dma_table_freq[2] << std::endl; + // cout << std::setprecision(30) << dma_table_freq[3] << std::endl; + if (center_freq_actual != test_tone + 1.5 * tone_spacing) { + std::cout << " Warning: because of hardware limitations, test tone " + "will be transmitted on" + << std::endl; std::stringstream temp; - temp << std::setprecision(6) << std::fixed << " frequency: " << (center_freq_actual-1.5*tone_spacing)/1e6 << " MHz" << std::endl; + 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; + ppm_prev = ppm; } - txSym(0, center_freq_actual, tone_spacing, 60, dma_table_freq, F_PWM_CLK_INIT, instrs, constPage, bufPtr); + txSym(0, center_freq_actual, tone_spacing, 60, dma_table_freq, + F_PWM_CLK_INIT, instrs, constPage, bufPtr); } // Should never get here... @@ -1226,49 +1324,70 @@ int main(const int argc, char * const argv[]) { // WSPR mode // Create WSPR symbols - unsigned char symbols[162]; - wspr(callsign.c_str(), locator.c_str(), tx_power.c_str(), symbols); - /* - printf("WSPR codeblock: "); - for (int i = 0; i < (signed)(sizeof(symbols)/sizeof(*symbols)); i++) { + unsigned char symbols_grid4[162]; + unsigned char symbols_grid6[162]; + wspr(callsign.c_str(), locator.substr(0, 4).c_str(), tx_power, + symbols_grid4); + if (locator.size() == 6) { + wspr(callsign.c_str(), locator.c_str(), tx_power, symbols_grid6); + } + + /*printf("WSPR codeblock (for grid %s): ", locator.substr(0, 4).c_str()); + for (int i = 0; + i < (signed)(sizeof(symbols_grid4) / sizeof(*symbols_grid4)); i++) { if (i) { - std::cout << ","; + printf(","); } - printf("%d", symbols[i]); + printf("%d", symbols_grid4[i]); } printf("\n"); - */ + if (locator.size() == 6) { + printf("WSPR codeblock (for grid %s): ", locator.c_str()); + for (int i = 0; + i < (signed)(sizeof(symbols_grid6) / sizeof(*symbols_grid6)); i++) { + if (i) { + printf(","); + } + printf("%d", symbols_grid6[i]); + } + printf("\n"); + }*/ std::cout << "Ready to transmit (setup complete)..." << std::endl; - int band=0; - int n_tx=0; - for(;;) { + int band = 0; + int n_tx = 0; + for (;;) { // Calculate WSPR parameters for this transmission double center_freq_desired; center_freq_desired = center_freq_set[band]; bool wspr15 = - (center_freq_desired > 137600 && center_freq_desired < 137625) || \ - (center_freq_desired > 475800 && center_freq_desired < 475825) || \ - (center_freq_desired > 1838200 && center_freq_desired < 1838225); + (center_freq_desired > 137600 && center_freq_desired < 137625) || + (center_freq_desired > 475800 && center_freq_desired < 475825) || + (center_freq_desired > 1838200 && center_freq_desired < 1838225); double wspr_symtime = (wspr15) ? 8.0 * WSPR_SYMTIME : WSPR_SYMTIME; - double tone_spacing=1.0/wspr_symtime; + double tone_spacing = 1.0 / wspr_symtime; // Add random offset - if ((center_freq_desired!=0)&&random_offset) { - center_freq_desired+=(2.0*rand()/((double)RAND_MAX+1.0)-1.0)*(wspr15?WSPR15_RAND_OFFSET:WSPR_RAND_OFFSET); + if ((center_freq_desired != 0) && random_offset) { + center_freq_desired += (2.0 * rand() / ((double)RAND_MAX + 1.0) - 1.0) * + (wspr15 ? WSPR15_RAND_OFFSET : WSPR_RAND_OFFSET); } // Status message before transmission std::stringstream temp; temp << std::setprecision(6) << std::fixed; - temp << "Desired center frequency for " << (wspr15?"WSPR-15":"WSPR") << " transmission: "<< center_freq_desired/1e6 << " MHz" << std::endl; + temp << "Desired center frequency for " << (wspr15 ? "WSPR-15" : "WSPR") + << " transmission: " << center_freq_desired / 1e6 << " MHz" + << std::endl; std::cout << temp.str(); // Wait for WSPR transmission window to arrive. if (no_delay) { - std::cout << " Transmitting immediately (not waiting for WSPR window)" << std::endl; + std::cout << " Transmitting immediately (not waiting for WSPR window)" + << std::endl; } else { - std::cout << " Waiting for next WSPR transmission window..." << std::endl; + std::cout << " Waiting for next WSPR transmission window..." + << std::endl; wait_every((wspr15) ? 15 : 2); } @@ -1278,40 +1397,50 @@ int main(const int argc, char * const argv[]) { } // Create the DMA table for this center frequency - std::vector dma_table_freq; + std::vector dma_table_freq; double center_freq_actual; - if (center_freq_desired) { - setupDMATab(center_freq_desired,tone_spacing,F_PLLD_CLK*(1-ppm/1e6),dma_table_freq,center_freq_actual,constPage); + if (center_freq_desired != 0.0) { + setupDMATab(center_freq_desired, tone_spacing, + F_PLLD_CLK * (1 - ppm / 1e6), dma_table_freq, + center_freq_actual, constPage); } else { - center_freq_actual=center_freq_desired; + center_freq_actual = center_freq_desired; } // Send the message! - //cout << "TX started!" << std::endl; - if (center_freq_actual){ + // cout << "TX started!" << std::endl; + if (center_freq_actual != 0.0) { // Print a status message right before transmission begins. struct timeval tvBegin, tvEnd, tvDiff; - gettimeofday(&tvBegin, NULL); + gettimeofday(&tvBegin, nullptr); std::cout << " TX started at: "; timeval_print(&tvBegin); std::cout << std::endl; struct timeval sym_start; struct timeval diff; - int bufPtr=0; + int bufPtr = 0; txon(); for (int i = 0; i < 162; i++) { - gettimeofday(&sym_start,NULL); + gettimeofday(&sym_start, nullptr); timeval_subtract(&diff, &sym_start, &tvBegin); - double elapsed=diff.tv_sec+diff.tv_usec/1e6; - //elapsed=(i)*wspr_symtime; - double sched_end=(i+1)*wspr_symtime; - //cout << "symbol " << i << " " << wspr_symtime << std::endl; - //cout << sched_end-elapsed << std::endl; - double this_sym=sched_end-elapsed; - this_sym=(this_sym<.2)?.2:this_sym; - this_sym=(this_sym>2*wspr_symtime)?2*wspr_symtime:this_sym; - txSym(symbols[i], center_freq_actual, tone_spacing, sched_end-elapsed, dma_table_freq, F_PWM_CLK_INIT, instrs, constPage, bufPtr); + double elapsed = diff.tv_sec + diff.tv_usec / 1e6; + // elapsed=(i)*wspr_symtime; + double sched_end = (i + 1) * wspr_symtime; + // cout << "symbol " << i << " " << wspr_symtime << std::endl; + // cout << sched_end-elapsed << std::endl; + if (locator.size() == 4 || n_tx % 2 == 0) { // If grid is only 4 + // characters or an even + // transmission number + txSym(symbols_grid4[i], center_freq_actual, tone_spacing, + sched_end - elapsed, dma_table_freq, F_PWM_CLK_INIT, instrs, + constPage, bufPtr); + } else { // Locator with 6 characters alternate sending hashed (type + // 3) message + txSym(symbols_grid6[i], center_freq_actual, tone_spacing, + sched_end - elapsed, dma_table_freq, F_PWM_CLK_INIT, instrs, + constPage, bufPtr); + } } n_tx++; @@ -1319,11 +1448,12 @@ int main(const int argc, char * const argv[]) { txoff(); // End timestamp - gettimeofday(&tvEnd, NULL); + gettimeofday(&tvEnd, nullptr); 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); + printf(" (%ld.%03ld s)\n", tvDiff.tv_sec, + (tvDiff.tv_usec + 500) / 1000); } else { std::cout << " Skipping transmission" << std::endl; @@ -1331,17 +1461,15 @@ int main(const int argc, char * const argv[]) { } // Advance to next band - band=(band+1)%nbands; - if ((band==0)&&!repeat) { + band = (band + 1) % nbands; + if ((band == 0) && !repeat) { break; } - if ((terminate>0)&&(n_tx>=terminate)) { + if ((terminate > 0) && (n_tx >= terminate)) { break; } - } } return 0; } -