├── .DS_Store ├── AUTHORS ├── BUGS ├── COPYING ├── ChangeLog ├── INSTALL ├── Makefile ├── README ├── THANKS ├── database ├── dg6fl.all.txt └── dg6fl.txt ├── harmonic_filters.pdf ├── wiring.png ├── wiring.sch ├── wiring.txt ├── wiring.xlsx └── wspr.c /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/8cH9azbsFifZ/WsprryPi/3e487c397d0ebf0b4914537d67f6a34859e5a8d8/.DS_Store -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | Guido Ten Dolle, PE1NZZ 2 | Initial verison of the software 3 | Daniel Ankersi, MD1CLV https://github.com/DanAnkers 4 | Documentation 5 | Gerolf Ziegenhain, DG6FL 6 | Integration, Documentation 7 | -------------------------------------------------------------------------------- /BUGS: -------------------------------------------------------------------------------- 1 | Issues as reported on https://github.com/threeme3/WsprryPi 2 | Two users were reporting that the program never stops transmitting, even 3 | when intervals for disabled tx are programmed. The problem was in both 4 | cases fixed by flashing a new image on the SD card with a freshly downloaded 5 | image: 2013-02-09-wheezy-raspbian.zip. No apt-get upgrade or firmware 6 | upgrade was performed. After this WsprryPi TX was running successfully. 7 | 8 | One user reported his RPi died while in WsprryPi service caused by excessive 9 | RF voltage (90V) on GPIO4 created by a 100 watts AM transmitter 50ft away from 10 | the antenna. After the damage exessive current was consumed by RPi (1.1A from 11 | 5V supply), caused by short-circuiting in the 3.3V logic of the BCM2835 SOC. 12 | On his replacement RPi, he is planning to add galvanic isolation and buffering. 13 | 14 | 15 | -------------------------------------------------------------------------------- /COPYING: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc., 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | Preamble 10 | 11 | The licenses for most software are designed to take away your 12 | freedom to share and change it. By contrast, the GNU General Public 13 | License is intended to guarantee your freedom to share and change free 14 | software--to make sure the software is free for all its users. This 15 | General Public License applies to most of the Free Software 16 | Foundation's software and to any other program whose authors commit to 17 | using it. (Some other Free Software Foundation software is covered by 18 | the GNU Lesser General Public License instead.) You can apply it to 19 | your programs, too. 20 | 21 | When we speak of free software, we are referring to freedom, not 22 | price. Our General Public Licenses are designed to make sure that you 23 | have the freedom to distribute copies of free software (and charge for 24 | this service if you wish), that you receive source code or can get it 25 | if you want it, that you can change the software or use pieces of it 26 | in new free programs; and that you know you can do these things. 27 | 28 | To protect your rights, we need to make restrictions that forbid 29 | anyone to deny you these rights or to ask you to surrender the rights. 30 | These restrictions translate to certain responsibilities for you if you 31 | distribute copies of the software, or if you modify it. 32 | 33 | For example, if you distribute copies of such a program, whether 34 | gratis or for a fee, you must give the recipients all the rights that 35 | you have. You must make sure that they, too, receive or can get the 36 | source code. And you must show them these terms so they know their 37 | rights. 38 | 39 | We protect your rights with two steps: (1) copyright the software, and 40 | (2) offer you this license which gives you legal permission to copy, 41 | distribute and/or modify the software. 42 | 43 | Also, for each author's protection and ours, we want to make certain 44 | that everyone understands that there is no warranty for this free 45 | software. If the software is modified by someone else and passed on, we 46 | want its recipients to know that what they have is not the original, so 47 | that any problems introduced by others will not reflect on the original 48 | authors' reputations. 49 | 50 | Finally, any free program is threatened constantly by software 51 | patents. We wish to avoid the danger that redistributors of a free 52 | program will individually obtain patent licenses, in effect making the 53 | program proprietary. To prevent this, we have made it clear that any 54 | patent must be licensed for everyone's free use or not licensed at all. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | GNU GENERAL PUBLIC LICENSE 60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 61 | 62 | 0. This License applies to any program or other work which contains 63 | a notice placed by the copyright holder saying it may be distributed 64 | under the terms of this General Public License. The "Program", below, 65 | refers to any such program or work, and a "work based on the Program" 66 | means either the Program or any derivative work under copyright law: 67 | that is to say, a work containing the Program or a portion of it, 68 | either verbatim or with modifications and/or translated into another 69 | language. (Hereinafter, translation is included without limitation in 70 | the term "modification".) Each licensee is addressed as "you". 71 | 72 | Activities other than copying, distribution and modification are not 73 | covered by this License; they are outside its scope. The act of 74 | running the Program is not restricted, and the output from the Program 75 | is covered only if its contents constitute a work based on the 76 | Program (independent of having been made by running the Program). 77 | Whether that is true depends on what the Program does. 78 | 79 | 1. You may copy and distribute verbatim copies of the Program's 80 | source code as you receive it, in any medium, provided that you 81 | conspicuously and appropriately publish on each copy an appropriate 82 | copyright notice and disclaimer of warranty; keep intact all the 83 | notices that refer to this License and to the absence of any warranty; 84 | and give any other recipients of the Program a copy of this License 85 | along with the Program. 86 | 87 | You may charge a fee for the physical act of transferring a copy, and 88 | you may at your option offer warranty protection in exchange for a fee. 89 | 90 | 2. You may modify your copy or copies of the Program or any portion 91 | of it, thus forming a work based on the Program, and copy and 92 | distribute such modifications or work under the terms of Section 1 93 | above, provided that you also meet all of these conditions: 94 | 95 | a) You must cause the modified files to carry prominent notices 96 | stating that you changed the files and the date of any change. 97 | 98 | b) You must cause any work that you distribute or publish, that in 99 | whole or in part contains or is derived from the Program or any 100 | part thereof, to be licensed as a whole at no charge to all third 101 | parties under the terms of this License. 102 | 103 | c) If the modified program normally reads commands interactively 104 | when run, you must cause it, when started running for such 105 | interactive use in the most ordinary way, to print or display an 106 | announcement including an appropriate copyright notice and a 107 | notice that there is no warranty (or else, saying that you provide 108 | a warranty) and that users may redistribute the program under 109 | these conditions, and telling the user how to view a copy of this 110 | License. (Exception: if the Program itself is interactive but 111 | does not normally print such an announcement, your work based on 112 | the Program is not required to print an announcement.) 113 | 114 | These requirements apply to the modified work as a whole. If 115 | identifiable sections of that work are not derived from the Program, 116 | and can be reasonably considered independent and separate works in 117 | themselves, then this License, and its terms, do not apply to those 118 | sections when you distribute them as separate works. But when you 119 | distribute the same sections as part of a whole which is a work based 120 | on the Program, the distribution of the whole must be on the terms of 121 | this License, whose permissions for other licensees extend to the 122 | entire whole, and thus to each and every part regardless of who wrote it. 123 | 124 | Thus, it is not the intent of this section to claim rights or contest 125 | your rights to work written entirely by you; rather, the intent is to 126 | exercise the right to control the distribution of derivative or 127 | collective works based on the Program. 128 | 129 | In addition, mere aggregation of another work not based on the Program 130 | with the Program (or with a work based on the Program) on a volume of 131 | a storage or distribution medium does not bring the other work under 132 | the scope of this License. 133 | 134 | 3. You may copy and distribute the Program (or a work based on it, 135 | under Section 2) in object code or executable form under the terms of 136 | Sections 1 and 2 above provided that you also do one of the following: 137 | 138 | a) Accompany it with the complete corresponding machine-readable 139 | source code, which must be distributed under the terms of Sections 140 | 1 and 2 above on a medium customarily used for software interchange; or, 141 | 142 | b) Accompany it with a written offer, valid for at least three 143 | years, to give any third party, for a charge no more than your 144 | cost of physically performing source distribution, a complete 145 | machine-readable copy of the corresponding source code, to be 146 | distributed under the terms of Sections 1 and 2 above on a medium 147 | customarily used for software interchange; or, 148 | 149 | c) Accompany it with the information you received as to the offer 150 | to distribute corresponding source code. (This alternative is 151 | allowed only for noncommercial distribution and only if you 152 | received the program in object code or executable form with such 153 | an offer, in accord with Subsection b above.) 154 | 155 | The source code for a work means the preferred form of the work for 156 | making modifications to it. For an executable work, complete source 157 | code means all the source code for all modules it contains, plus any 158 | associated interface definition files, plus the scripts used to 159 | control compilation and installation of the executable. However, as a 160 | special exception, the source code distributed need not include 161 | anything that is normally distributed (in either source or binary 162 | form) with the major components (compiler, kernel, and so on) of the 163 | operating system on which the executable runs, unless that component 164 | itself accompanies the executable. 165 | 166 | If distribution of executable or object code is made by offering 167 | access to copy from a designated place, then offering equivalent 168 | access to copy the source code from the same place counts as 169 | distribution of the source code, even though third parties are not 170 | compelled to copy the source along with the object code. 171 | 172 | 4. You may not copy, modify, sublicense, or distribute the Program 173 | except as expressly provided under this License. Any attempt 174 | otherwise to copy, modify, sublicense or distribute the Program is 175 | void, and will automatically terminate your rights under this License. 176 | However, parties who have received copies, or rights, from you under 177 | this License will not have their licenses terminated so long as such 178 | parties remain in full compliance. 179 | 180 | 5. You are not required to accept this License, since you have not 181 | signed it. However, nothing else grants you permission to modify or 182 | distribute the Program or its derivative works. These actions are 183 | prohibited by law if you do not accept this License. Therefore, by 184 | modifying or distributing the Program (or any work based on the 185 | Program), you indicate your acceptance of this License to do so, and 186 | all its terms and conditions for copying, distributing or modifying 187 | the Program or works based on it. 188 | 189 | 6. Each time you redistribute the Program (or any work based on the 190 | Program), the recipient automatically receives a license from the 191 | original licensor to copy, distribute or modify the Program subject to 192 | these terms and conditions. You may not impose any further 193 | restrictions on the recipients' exercise of the rights granted herein. 194 | You are not responsible for enforcing compliance by third parties to 195 | this License. 196 | 197 | 7. If, as a consequence of a court judgment or allegation of patent 198 | infringement or for any other reason (not limited to patent issues), 199 | conditions are imposed on you (whether by court order, agreement or 200 | otherwise) that contradict the conditions of this License, they do not 201 | excuse you from the conditions of this License. If you cannot 202 | distribute so as to satisfy simultaneously your obligations under this 203 | License and any other pertinent obligations, then as a consequence you 204 | may not distribute the Program at all. For example, if a patent 205 | license would not permit royalty-free redistribution of the Program by 206 | all those who receive copies directly or indirectly through you, then 207 | the only way you could satisfy both it and this License would be to 208 | refrain entirely from distribution of the Program. 209 | 210 | If any portion of this section is held invalid or unenforceable under 211 | any particular circumstance, the balance of the section is intended to 212 | apply and the section as a whole is intended to apply in other 213 | circumstances. 214 | 215 | It is not the purpose of this section to induce you to infringe any 216 | patents or other property right claims or to contest validity of any 217 | such claims; this section has the sole purpose of protecting the 218 | integrity of the free software distribution system, which is 219 | implemented by public license practices. Many people have made 220 | generous contributions to the wide range of software distributed 221 | through that system in reliance on consistent application of that 222 | system; it is up to the author/donor to decide if he or she is willing 223 | to distribute software through any other system and a licensee cannot 224 | impose that choice. 225 | 226 | This section is intended to make thoroughly clear what is believed to 227 | be a consequence of the rest of this License. 228 | 229 | 8. If the distribution and/or use of the Program is restricted in 230 | certain countries either by patents or by copyrighted interfaces, the 231 | original copyright holder who places the Program under this License 232 | may add an explicit geographical distribution limitation excluding 233 | those countries, so that distribution is permitted only in or among 234 | countries not thus excluded. In such case, this License incorporates 235 | the limitation as if written in the body of this License. 236 | 237 | 9. The Free Software Foundation may publish revised and/or new versions 238 | of the General Public License from time to time. Such new versions will 239 | be similar in spirit to the present version, but may differ in detail to 240 | address new problems or concerns. 241 | 242 | Each version is given a distinguishing version number. If the Program 243 | specifies a version number of this License which applies to it and "any 244 | later version", you have the option of following the terms and conditions 245 | either of that version or of any later version published by the Free 246 | Software Foundation. If the Program does not specify a version number of 247 | this License, you may choose any version ever published by the Free Software 248 | Foundation. 249 | 250 | 10. If you wish to incorporate parts of the Program into other free 251 | programs whose distribution conditions are different, write to the author 252 | to ask for permission. For software which is copyrighted by the Free 253 | Software Foundation, write to the Free Software Foundation; we sometimes 254 | make exceptions for this. Our decision will be guided by the two goals 255 | of preserving the free status of all derivatives of our free software and 256 | of promoting the sharing and reuse of software generally. 257 | 258 | NO WARRANTY 259 | 260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 268 | REPAIR OR CORRECTION. 269 | 270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 278 | POSSIBILITY OF SUCH DAMAGES. 279 | 280 | END OF TERMS AND CONDITIONS 281 | -------------------------------------------------------------------------------- /ChangeLog: -------------------------------------------------------------------------------- 1 | 2013-05-30 Gerolf Ziegenhain, DG6FL 2 | * Added wiring schematic 3 | * Added bandpass filter values 4 | 5 | 2013-04-09 Gerolf Ziegenhain, DG6FL 6 | * Split README into standard AUTHORS, THANKS, ChangeLog, INSTALL, COPYING, 7 | * Added Makefile 8 | -------------------------------------------------------------------------------- /INSTALL: -------------------------------------------------------------------------------- 1 | Prerequisites 2 | ============= 3 | * Have a RasPi 4 | * Install Raspian: 2013-02-09-wheezy-raspbian.img 5 | 6 | 7 | Preparation 8 | =========== 9 | Install compiler and git 10 | apt-get install gcc git 11 | 12 | Prepare the WsprryPi repository 13 | git clone https://github.com/8cH9azbsFifZ/WsprryPi.git 14 | cd WsprryPi 15 | 16 | 17 | Build 18 | ===== 19 | make 20 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | all: 2 | gcc -D _BSD_SOURCE -lm -std=c99 wspr.c -owspr 3 | 4 | clean: 5 | rm wspr 6 | 7 | install: 8 | cp wspr /usr/local/sbin/ 9 | 10 | uninstall: 11 | rm /usr/local/sbin/wspr 12 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | Raspberry Pi Bareback LF/MF/HF/VHF WSPR Transmitter 2 | 3 | Makes a very simple WSPR beacon from your RasberryPi by connecting GPIO 4 | port to Antanna (and LPF), operates on LF, MF, HF and VHF bands from 5 | 0 to 250 MHz. 6 | In order to transmit legally, a HAM Radio License is REQUIRED for running 7 | this experiment. 8 | 9 | 10 | How to Use 11 | ========== 12 | 0) Install Raspbian (12) 13 | 1) Follow the instructions given in INSTALL to build and install the software 14 | 2) Connect bandpassfilter and antenna (cf. below) 15 | 3) Calibrate (cf. below) 16 | 17 | Filter and Antenna 18 | ================== 19 | Connect a low pass filter (9) to the PINs (2,11) 7 and 9 as described in 20 | wiring.png. The expected power output is 10mW (+10dBm) in a 50 Ohm load. 21 | This looks neglible, but when connected to a simple dipole antenna this may 22 | result in reception reports ranging up to several thousands of kilometers. 23 | 24 | DO NOT expose GPIO4 to voltages or currents that are above the specified 25 | Absolute Maximum limits. GPIO4 outputs a digital clock in 3V3 logic, with a 26 | maximum current of 16mA. As there is no current protection available and 27 | a DC component of 1.6V, DO NOT short-circuit or place a resistive (dummy) load 28 | straight on the GPIO4 pin, as it may draw too much current. Instead, use a 29 | decoupling capacitor to remove DC component when connecting the output 30 | dummy loads, transformers, antennas, etc. DO NOT expose GPIO4 to electro- 31 | static voltages or voltages exceeding the 0 to 3.3V logic range; connecting an 32 | antenna directly to GPIO4 may damage your RPi due to transient voltages such as 33 | lightning or static buildup as well as RF from other transmitters operating into 34 | nearby antennas. Therefore it is RECOMMENDED to add some form of isolation, e.g. 35 | by using a RF transformer, a simple buffer/driver/PA stage, two schottky small 36 | signal diodes back to back. 37 | 38 | 39 | Power Supply 40 | ============ 41 | As the Raspberry Pi does not attenuate ripple and noise components from the 42 | 5V USB power supply, it is RECOMMENDED to use a regulated supply that has 43 | sufficient ripple supression. Supply ripple might be seen as mixing products 44 | products centered around the transmit carrier typically at 100/120Hz. 45 | 46 | 47 | Software Description 48 | ==================== 49 | This software is using system time to determine the start of a WSPR 50 | transmissions, so keep the system time synchronised within 1sec precision, 51 | i.e. use NTP network time synchronisation or set time manually with date 52 | command. A WSPR broadcast starts on even minute and takes 2 minutes for WSPR-2 53 | or starts at :00,:15,:30,:45 and takes 15 minutes for WSPR-15. It contains 54 | a callsign, 4-digit Maidenhead square locator and transmission power. 55 | Reception reports can be viewed on Weak Signal Propagation Reporter Network (10). 56 | 57 | 58 | Usage 59 | ===== 60 | sudo ./wspr <[prefix/]callsign[/suffix]> [ ...] 61 | e.g.: sudo ./wspr PA/K1JT JO21 10 7040074 0 0 10140174 0 0 62 | where 0 frequency represents a interval for which TX is disabled, 63 | wspr-2 or wspr-15 mode selection based on specified frequency. 64 | 65 | 66 | WSPR Frequencies (1) 67 | ==================== 68 | WSPR is used on the following frequencies (local restriction may apply): 69 | LF 137400 - 137600 70 | 137600 - 137625 (WSPR-15) 71 | MF 475600 - 475800 72 | 475800 - 475825 (WSPR-15) 73 | 160m 1838000 - 1838200 74 | 1838200 - 1838225 (WSPR-15) 75 | 80m 3594000 - 3594200 76 | 60m 5288600 - 5288800 77 | 40m 7040000 - 7040200 78 | 30m 10140100 - 10140300 79 | 20m 14097000 - 14097200 80 | 17m 18106000 - 18106200 81 | 15m 21096000 - 21096200 82 | 12m 24926000 - 24926200 83 | 10m 28126000 - 28126200 84 | 6m 50294400 - 50294600 85 | 4m 70092400 - 70092600 86 | 2m 144490400 -144490600 87 | 88 | 89 | Calibration 90 | =========== 91 | Frequency calibration is REQUIRED to ensure that the WSPR-2 transmission occurs 92 | within the 200 Hz narrow band. The reference crystal on your RPi might have 93 | an frequency error (which in addition is temp. dependent -1.3Hz/degC @10MHz). 94 | To calibrate, the frequency might be manually corrected on the command line 95 | or by changing the F_XTAL value in the code. 96 | 97 | A practical way to calibrate is to tune the transmitter on the same frequency 98 | of a medium wave AM broadcast station or a CW transmitter at hand; keep tuning 99 | with USB or LSB until zero beat (the constant audio tone disappears when the 100 | transmitter is exactly on the same frequency as the broadcast station), and 101 | determine the frequency difference with the broadcast station. This is the 102 | frequency error that can be applied for correction while tuning on a WSPR 103 | frequency. 104 | 105 | Then you can do the math for the offset from your terminal using shell. If 106 | the offset is -1100 Hz and you want to tune to 10140200, then: 107 | ./wspr K1JT JO21da 10 $((10140200-1100)) 108 | 109 | NB: Do not overclock your RPi as it may make the clock unreliable due to a 110 | dynamic clocking feature. 111 | 112 | 113 | Check reception 114 | =============== 115 | CALLSIGN=W1AW 116 | wget http://wsprnet.org/drupal/wsprnet/activity -O - 2>&1 |grep -i $CALLSIGN && echo $CALLSIGN found on wsprnet.org 117 | 118 | 119 | Reference documentation 120 | ======================= 121 | [1] WSPR Frequencies: http://wsprnet.org/drupal/node/218 122 | [2] http://www.raspberrypi.org/wp-content/uploads/2012/02/BCM2835-ARM-Peripherals.pdf 123 | [3] http://www.scribd.com/doc/127599939/BCM2835-Audio-clocks 124 | [4] http://www.scribd.com/doc/101830961/GPIO-Pads-Control2 125 | [5] https://github.com/mgottschlag/vctools/blob/master/vcdb/cm.yaml 126 | [6] https://www.kernel.org/doc/Documentation/vm/pagemap.txt 127 | [7] http://www.gqrp.com/harmonic_filters.pdf 128 | [8] Raspberry Pi Foundation: http://www.raspberrypi.org/ 129 | [9] Dobbs, G., G3RJV: A Complete Do-It-Yourself Kit... with just a few simple calculations. http://www.gqrp.com/harmonic_filters.pdf 130 | [10] WSPRnet: Weak Signal Propagation Reporter Network. http://wsprnet.org/drupal/ 131 | [11] Raspi Pinout: http://elinux.org/RPi_Low-level_peripherals 132 | [12] Raspbian http://www.raspbian.org/ 133 | 134 | 135 | 136 | Code Quality 137 | ============ 138 | This is experimental code. 139 | 140 | 141 | -------------------------------------------------------------------------------- /THANKS: -------------------------------------------------------------------------------- 1 | Oliver Mattos and Oskar Weigl 2 | Credits goes to Oliver Mattos and Oskar Weigl who implemented PiFM [1] 3 | based on the idea of exploiting RPi DPLL as FM transmitter. Dan MD1CLV 4 | combined this effort with WSPR encoding algorithm from F8CHK, resulting 5 | in WsprryPi a WSPR beacon for LF and MF bands. Guido PE1NNZ extended 6 | this effort with DMA based PWM modulation of fractional divider that was 7 | part of PiFM, allowing to operate the WSPR beacon also on HF and VHF bands. 8 | In addition time-synchronisation and double amount of power output was 9 | implemented. 10 | 11 | [1] PiFM code from http://www.icrobotics.co.uk/wiki/index.php/Turning_the_Raspberry_Pi_Into_an_FM_Transmitter 12 | 13 | -------------------------------------------------------------------------------- /database/dg6fl.txt: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | WSPR Spots 8 | 9 | 16 | 17 | 18 |
19 | WSPR Spot Database 20 |
21 |

22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 |
WSPRnet.org HomeSked/Chat pageMapsDB statistics5071909 spots in the database
31 |

32 | 33 | 34 | 83 | 96 | 97 |
35 | Display options

36 |

37 | 38 | Band: 39 | 59 |
60 | Number of spots:
61 | Search for call:
62 | Show spots heard by:
63 | Sort by: 64 | 76 | Reverse order
77 | Find unique calls 78 | Find unique reporters
79 | 80 | 81 |
82 |
84 | Upload your log!

85 |

86 | Your callsign:
87 | Your grid: (6 chars preferred)
88 | 89 | Location of ALL_MEPT.TXT:
90 | (will be in same directory as WSPR.EXE, max size 10M)
91 | Show verbose parser output
92 | 93 |
94 |

How can I automate this? 95 |

98 |

99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 |
PowerReportedDistance
DateCallFrequencySNRDriftGriddBmW
bylockmmi
 2013-04-22 05:02  DG6FL  10.140238  -21  1  JO40cb  +10  0.010  W4AC  EL86  7869  4890 
 2013-04-15 04:44  DG6FL  10.140239  -23  0  JO40cb  +10  0.010  N3ZM  FM29cs  6358  3951 
 2013-04-16 23:38  DG6FL  10.140250  -23  1  JO40cb  +10  0.010  LA9JO  JP99gb  2184  1357 
 2013-04-24 04:08  DG6FL  10.140252  -22  1  JO40cb  +10  0.010  OH8GKP  KP24rt  1924  1196 
 2013-04-17 20:36  DG6FL  10.140249  -24  0  JO40cb  +10  0.010  LB9YE  JP54pu  1655  1028 
 2013-04-20 12:46  DG6FL  10.140245  -25  0  JO40cb  +10  0.010  OH5ZA  KP21ni  1633  1015 
 2013-04-18 20:02  DG6FL  10.140246  -22  1  JO40cb  +10  0.010  EI7GSB  IO52qd  1195  743 
 2013-04-14 21:56  DG6FL  10.140243  -22  1  JO40cb  +10  0.010  EI9GNB  IO51sv  1182  734 
 2013-04-21 16:30  DG6FL  10.140246  -17  0  JO40cb  +10  0.010  MI3LDO  IO64gg  1163  723 
 2013-04-19 20:32  DG6FL  10.140253  -25  1  JO40cb  +10  0.010  LA1QDA  JP20qh  1154  717 
 2013-04-15 12:10  DG6FL  10.140227  -24  0  JO40cb  +10  0.010  LA3RK  JO59hw  1109  689 
 2013-04-26 11:34  DG6FL  7.040147  -17  1  JO40cb  +10  0.010  GM7KFS  IO75mu  1089  677 
 2013-04-18 20:04  DG6FL  10.140242  -20  2  JO40cb  +10  0.010  LA1IC  JO59fs  1089  677 
 2013-04-14 07:50  DG6FL  10.140207  -24  1  JO40cb  +10  0.010  EI3KF  IO63qi  1083  673 
 2013-04-24 13:22  DG6FL  10.140228  -25  1  JO40cb  +10  0.010  LA5GOA  JO29oi  1051  653 
 2013-04-14 20:06  DG6FL  10.140234  -23  1  JO40cb  +10  0.010  LA3JJ\A  JO59bh  1036  644 
 2013-04-20 13:36  DG6FL  10.140242  -24  0  JO40cb  +10  0.010  LA3JJ  JO59bh  1036  644 
 2013-04-14 13:54  DG6FL  10.140231  -26  1  JO40cb  +10  0.010  GM3NKG  IO85ar  1029  639 
 2013-04-26 18:42  DG6FL  10.140240  -25  1  JO40cb  +10  0.010  SA6BSS  JO68  985  612 
 2013-04-15 18:24  DG6FL  10.140250  -24  1  JO40cb  +10  0.010  M1AVV  IO84ic  904  562 
 2013-04-15 15:52  DG6FL  10.140230  -25  0  JO40cb  +10  0.010  G4DND  IO80bo  859  534 
 2013-04-15 09:40  DG6FL  10.140229  -20  1  JO40cb  +10  0.010  G4DDND  IO80bo  859  534 
 2013-04-20 17:30  DG6FL  10.140262  -26  2  JO40cb  +10  0.010  M1SWB  IO83mk  854  531 
 2013-04-21 17:58  DG6FL  10.140241  -22  0  JO40cb  +10  0.010  M0DEV  IO82op  815  506 
 2013-04-23 15:10  DG6FL  10.140240  -11  1  JO40cb  +10  0.010  M0DCM  IO82wl  767  477 
 2013-04-22 10:52  DG6FL  10.140238  -22  2  JO40cb  +10  0.010  G0IMX  IO92ao  760  472 
 2013-04-15 07:04  DG6FL  10.140248  -24  1  JO40cb  +10  0.010  M0ITF  IO81wv  751  467 
 2013-04-21 16:48  DG6FL  10.140241  -18  1  JO40cb  +10  0.010  G4KZV  IO92bk  749  465 
 2013-04-16 17:06  DG6FL  10.140231  -23  1  JO40cb  +10  0.010  F8KGW  IN98ec  748  465 
 2013-04-14 19:20  DG6FL  10.140235  -21  0  JO40cb  +10  0.010  G0MGX  IO93ga  744  462 
 2013-04-13 17:30  DG6FL  10.140198  -15  1  JO40cb  +10  0.010  G6HUI  IO81wl  743  462 
 2013-04-23 19:26  DG6FL  10.140253  -12  1  JO40cb  +10  0.010  M0ARM  IO80vp  741  460 
 2013-04-20 17:20  DG6FL  10.140241  -22  -1  JO40cb  +10  0.010  G0HZG  IO92ho  723  449 
 2013-04-24 15:58  DG6FL  10.140240  -24  1  JO40cb  +10  0.010  G0FCH  IO91cn  721  448 
 2013-04-18 09:28  DG6FL  10.140253  -27  0  JO40cb  +10  0.010  G0WFS  IO92jn  710  441 
 2013-04-20 11:42  DG6FL  10.140237  -21  0  JO40cb  +10  0.010  IW1RGP  JN33vt  696  432 
 2013-04-20 17:56  DG6FL  10.140249  -16  2  JO40cb  +10  0.010  F1VMV  JN24kh  687  427 
 2013-04-16 17:04  DG6FL  10.140232  -23  1  JO40cb  +10  0.010  M0POQ  IO91ij  684  425 
 2013-04-22 12:28  DG6FL  10.140240  -28  1  JO40cb  +10  0.010  G0MQW  IO91ml  663  412 
 2013-04-16 19:20  DG6FL  10.140257  -25  1  JO40cb  +10  0.010  G8YTR  IO90lv  661  411 
 2013-04-24 14:42  DG6FL  10.140239  -22  0  JO40cb  +10  0.010  G7RPG  IO90lt  660  410 
 2013-04-21 19:02  DG6FL  10.140223  -23  1  JO40cb  +10  0.010  OE6WSF  JN76ts  658  409 
 2013-04-21 11:44  DG6FL  10.140267  -21  1  JO40cb  +10  0.010  G4LOP  JO03ce  652  405 
 2013-04-21 19:02  DG6FL  10.140235  -17  0  JO40cb  +10  0.010  OZ7IT  JO65df  637  396 
 2013-04-21 12:34  DG6FL  10.140248  -25  1  JO40cb  +10  0.010  IZ4PSG  JN54kp  635  395 
 2013-04-14 11:00  DG6FL  10.140221  -15  1  JO40cb  +10  0.010  OV1MK  JO65bb  616  383 
 2013-04-21 12:02  DG6FL  10.140255  -24  2  JO40cb  +10  0.010  G8CRB  JO02bf  613  381 
 2013-04-15 08:10  DG6FL  10.140246  -24  1  JO40cb  +10  0.010  G3YSX  IO91wg  602  374 
 2013-04-21 11:40  DG6FL  10.140246  -28  1  JO40cb  +10  0.010  M3GWO  IO90xt  590  367 
 2013-04-18 11:38  DG6FL  10.140241  -15  1  JO40cb  +10  0.010  G0TTV  JO02rn  545  339 
 2013-04-21 11:18  DG6FL  10.140237  -21  1  JO40cb  +10  0.010  DF2LV  JO44rs  530  329 
 2013-04-13 13:18  DG6FL  10.140119  -14  1  JO40cb  +10  0.010  DC7NEU  JO63wd  520  323 
 2013-04-21 11:24  DG6FL  10.140282  -26  2  JO40cb  +10  0.010  IW2NYX  JN45km  507  315 
 2013-04-24 20:12  DG6FL  10.140251  -23  0  JO40cb  +10  0.010  F6BIA  JN18dq  455  283 
 2013-04-15 10:42  DG6FL  10.140236  -21  1  JO40cb  +10  0.010  PA1SDB  JO33kh  373  232 
 2013-04-24 17:12  DG6FL  10.140237  -13  1  JO40cb  +10  0.010  ON4SNW  JO21ce  309  192 
 2013-04-24 16:58  DG6FL  10.140253  -14  1  JO40cb  +10  0.010  ON7KO  JO21ce  309  192 
 2013-04-18 13:16  DG6FL  10.140247  -22  1  JO40cb  +10  0.010  ON7KB  JO21ei  306  190 
 2013-04-15 08:18  DG6FL  10.140240  -21  1  JO40cb  +10  0.010  PI4THT  JO32kf  258  160 
 2013-04-24 14:56  DG6FL  10.140240  -24  0  JO40cb  +10  0.010  DJ2RD  JO40cc  8  5 
181 |

182 |

CSV/XML download disabled until fixed

183 |
184 |

185 | Processing took 10 milliseconds 186 | 187 | 188 | -------------------------------------------------------------------------------- /harmonic_filters.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/8cH9azbsFifZ/WsprryPi/3e487c397d0ebf0b4914537d67f6a34859e5a8d8/harmonic_filters.pdf -------------------------------------------------------------------------------- /wiring.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/8cH9azbsFifZ/WsprryPi/3e487c397d0ebf0b4914537d67f6a34859e5a8d8/wiring.png -------------------------------------------------------------------------------- /wiring.sch: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/8cH9azbsFifZ/WsprryPi/3e487c397d0ebf0b4914537d67f6a34859e5a8d8/wiring.sch -------------------------------------------------------------------------------- /wiring.txt: -------------------------------------------------------------------------------- 1 | Band C1,4 (pF) C2,3 (pF) Cores L1-3 L1,3 turns L2 turns 2 | 160m 820 2200 T50-2 30 34 3 | 80m 470 1200 T37-2 25 27 4 | 40m 270 680 T37-6 19 21 5 | 30m 270 560 T37-6 19 20 6 | 20m 180 390 T37-6 16 17 7 | 17m 110 270 T37-6 13 15 8 | 15m 82 220 T37-6 12 14 9 | 12m 100 220 T37-6 12 13 10 | 10m 56 150 T37-6 10 11 11 | 12 | 13 | -------------------------------------------------------------------------------- /wiring.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/8cH9azbsFifZ/WsprryPi/3e487c397d0ebf0b4914537d67f6a34859e5a8d8/wiring.xlsx -------------------------------------------------------------------------------- /wspr.c: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Raspberry Pi bareback LF/MF/HF/VHF WSPR transmitter 4 | 5 | Makes a very simple WSPR beacon from your RasberryPi by connecting GPIO 6 | port to Antanna (and LPF), operates on LF, MF, HF and VHF bands from 7 | 0 to 250 MHz. 8 | 9 | 10 | 11 | License: 12 | This program is free software: you can redistribute it and/or modify 13 | it under the terms of the GNU General Public License as published by 14 | the Free Software Foundation, either version 2 of the License, or 15 | (at your option) any later version. 16 | 17 | This program is distributed in the hope that it will be useful, 18 | but WITHOUT ANY WARRANTY; without even the implied warranty of 19 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 20 | GNU General Public License for more details. 21 | 22 | You should have received a copy of the GNU General Public License 23 | along with this program. If not, see . 24 | 25 | */ 26 | 27 | #define _POSIX_SOURCE 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | #include 43 | #include 44 | #include 45 | #include 46 | 47 | #define EXIT_SUCCESS 0 48 | #define EXIT_FAILURE 1 49 | #define DAEMON_NAME "wspr" 50 | #define RUN_AS_USER "root" 51 | 52 | #define F_XTAL (19229581.050215044276577479844352) // calibrated 19.2MHz XTAL frequency 53 | #define F_PLLD_CLK (26.0 * F_XTAL) // 500MHz PLLD reference clock 54 | 55 | #define T_SECOND 1000000 56 | #define T_01MSECOND 100 57 | #define T_1MSECOND 1000 58 | 59 | #define N_ITER 1400 // number of PWM operations per symbol; larger values gives less spurs at the cost of frequency resolution; e.g. use 22500 for HF usage up to 30MHz, 12000 up to 50MHz, 1600 for VHF usage up to 144 Mhz, F_PWM_CLK needs to be adjusted when changing N_ITER 60 | //#define F_PWM_CLK (31500000.0) // 31.5MHz PWM clock use with N_ITER=22500 61 | #define F_PWM_CLK (33970588.235294117647058823529413) // 31.5MHz calibrated PWM clock use with N_ITER=1400 62 | 63 | #define WSPR_SYMTIME (8192.0/12000.0) // symbol time 64 | 65 | #define POLYNOM_1 0xf2d05351 // polynoms for 66 | #define POLYNOM_2 0xe4613c47 // parity generator 67 | 68 | /* RF code: */ 69 | 70 | #define BCM2708_PERI_BASE 0x20000000 71 | #define GPIO_BASE (BCM2708_PERI_BASE + 0x200000) /* GPIO controller */ 72 | #define PAGE_SIZE (4*1024) 73 | #define BLOCK_SIZE (4*1024) 74 | 75 | int mem_fd; 76 | char *gpio_mem, *gpio_map; 77 | char *spi0_mem, *spi0_map; 78 | 79 | 80 | // I/O access 81 | volatile unsigned *gpio = NULL; 82 | volatile unsigned *allof7e = NULL; 83 | 84 | // GPIO setup macros. Always use INP_GPIO(x) before using OUT_GPIO(x) or SET_GPIO_ALT(x,y) 85 | #define INP_GPIO(g) *(gpio+((g)/10)) &= ~(7<<(((g)%10)*3)) 86 | #define OUT_GPIO(g) *(gpio+((g)/10)) |= (1<<(((g)%10)*3)) 87 | #define SET_GPIO_ALT(g,a) *(gpio+(((g)/10))) |= (((a)<=3?(a)+4:(a)==4?3:2)<<(((g)%10)*3)) 88 | 89 | #define GPIO_SET *(gpio+7) // sets bits which are 1 ignores bits which are 0 90 | #define GPIO_CLR *(gpio+10) // clears bits which are 1 ignores bits which are 0 91 | #define GPIO_GET *(gpio+13) // sets bits which are 1 ignores bits which are 0 92 | 93 | #define ACCESS(base) *(volatile int*)((int)allof7e+base-0x7e000000) 94 | #define SETBIT(base, bit) ACCESS(base) |= 1<SOURCE_AD = (int)constPage.p + (i-1)*4; 235 | 236 | bufPtr++; 237 | while( ACCESS(DMABASE + 0x04 /* CurBlock*/) == (int)(instrs[bufPtr].p)) usleep(T_01MSECOND); 238 | ((struct CB*)(instrs[bufPtr].v))->TXFR_LEN = clocksPerIter-fracval; 239 | 240 | bufPtr++; 241 | while( ACCESS(DMABASE + 0x04 /* CurBlock*/) == (int)(instrs[bufPtr].p)) usleep(T_01MSECOND); 242 | ((struct CB*)(instrs[bufPtr].v))->SOURCE_AD = (int)constPage.p + (i+1)*4; 243 | 244 | bufPtr=(bufPtr+1) % (1024); 245 | while( ACCESS(DMABASE + 0x04 /* CurBlock*/) == (int)(instrs[bufPtr].p)) usleep(T_01MSECOND); 246 | ((struct CB*)(instrs[bufPtr].v))->TXFR_LEN = fracval; 247 | } 248 | } 249 | 250 | void unSetupDMA(){ 251 | printf("exiting\n"); 252 | struct DMAregs* DMA0 = (struct DMAregs*)&(ACCESS(DMABASE)); 253 | DMA0->CS =1<<31; // reset dma controller 254 | txoff(); 255 | } 256 | 257 | void handSig() { 258 | exit(0); 259 | } 260 | void setupDMATab( float centerFreq, double symOffset, double tsym, int nsym ){ 261 | // make data page contents - it's essientially 1024 different commands for the 262 | // DMA controller to send to the clock module at the correct time. 263 | int i; 264 | for(i=1; i<1023; i+=3){ 265 | double freq = centerFreq + ((double)(-511 + i))*symOffset/3.0; 266 | double divisor = F_PLLD_CLK/freq; 267 | unsigned long integer_part = (unsigned long) divisor; 268 | unsigned long fractional_part = (divisor - integer_part) * (1 << 12); 269 | unsigned long tuning_word = (0x5a << 24) + integer_part * (1 << 12) + fractional_part; 270 | if(fractional_part == 0 || fractional_part == 1023){ 271 | if((-511 + i) >= 0 && (-511 + i) <= (nsym * 3)) 272 | printf("warning: symbol %u unusable because fractional divider is out of range, try near frequency.\n", i/3); 273 | } 274 | ((int*)(constPage.v))[i-1] = tuning_word - 1; 275 | ((int*)(constPage.v))[i] = tuning_word; 276 | ((int*)(constPage.v))[i+1] = tuning_word + 1; 277 | double actual_freq = F_PLLD_CLK/((double)integer_part + (double)fractional_part/(double)(1<<12)); 278 | double freq_corr = freq - actual_freq; 279 | double delta = F_PLLD_CLK/((double)integer_part + (double)fractional_part/(double)(1<<12)) - F_PLLD_CLK/((double)integer_part + ((double)fractional_part+1.0)/(double)(1<<12)); 280 | int clocksPerIter = (int)((F_PWM_CLK/((double)N_ITER)) * tsym); 281 | double resolution = 2.0 * delta / ((double)clocksPerIter); 282 | if(resolution > symOffset ){ 283 | printf("warning: PWM/PLL fractional divider has not enough resolution: %fHz while %fHz is required, try lower frequency or decrease N_ITER in code to achieve more resolution.\n", resolution, symOffset); 284 | exit(0); 285 | } 286 | fracs[i] = freq_corr/delta; 287 | //printf("i=%u f=%f fa=%f corr=%f delta=%f percfrac=%f int=%u frac=%u tuning_word=%u resolution=%fmHz\n", i, freq, actual_freq, freq_corr, delta, fracs[i], integer_part, fractional_part, tuning_word, resolution *1000); 288 | } 289 | } 290 | 291 | void setupDMA(){ 292 | atexit(unSetupDMA); 293 | signal (SIGINT, handSig); 294 | signal (SIGTERM, handSig); 295 | signal (SIGHUP, handSig); 296 | signal (SIGQUIT, handSig); 297 | 298 | // allocate a few pages of ram 299 | getRealMemPage(&constPage.v, &constPage.p); 300 | 301 | int instrCnt = 0; 302 | 303 | while (instrCnt<1024) { 304 | getRealMemPage(&instrPage.v, &instrPage.p); 305 | 306 | // make copy instructions 307 | struct CB* instr0= (struct CB*)instrPage.v; 308 | int i; 309 | for (i=0; i<4096/sizeof(struct CB); i++) { 310 | instrs[instrCnt].v = (void*)((int)instrPage.v + sizeof(struct CB)*i); 311 | instrs[instrCnt].p = (void*)((int)instrPage.p + sizeof(struct CB)*i); 312 | instr0->SOURCE_AD = (unsigned int)constPage.p+2048; 313 | instr0->DEST_AD = PWMBASE+0x18 /* FIF1 */; 314 | instr0->TXFR_LEN = 4; 315 | instr0->STRIDE = 0; 316 | //instr0->NEXTCONBK = (int)instrPage.p + sizeof(struct CB)*(i+1); 317 | instr0->TI = (1/* DREQ */<<6) | (5 /* PWM */<<16) | (1<<26/* no wide*/) ; 318 | instr0->RES1 = 0; 319 | instr0->RES2 = 0; 320 | 321 | if (i%2) { 322 | instr0->DEST_AD = CM_GP0DIV; 323 | instr0->STRIDE = 4; 324 | instr0->TI = (1<<26/* no wide*/) ; 325 | } 326 | 327 | if (instrCnt!=0) ((struct CB*)(instrs[instrCnt-1].v))->NEXTCONBK = (int)instrs[instrCnt].p; 328 | instr0++; 329 | instrCnt++; 330 | } 331 | } 332 | ((struct CB*)(instrs[1023].v))->NEXTCONBK = (int)instrs[0].p; 333 | 334 | // set up a clock for the PWM 335 | ACCESS(CLKBASE + 40*4 /*PWMCLK_CNTL*/) = 0x5A000026; // Source=PLLD and disable 336 | usleep(T_1MSECOND); 337 | // ACCESS(CLKBASE + 41*4 /*PWMCLK_DIV*/) = 0x5A002800; 338 | ACCESS(CLKBASE + 41*4 /*PWMCLK_DIV*/) = 0x5A002000; // set PWM div to 2, for 250MHz 339 | ACCESS(CLKBASE + 40*4 /*PWMCLK_CNTL*/) = 0x5A000016; // Source=PLLD and enable 340 | usleep(T_1MSECOND); 341 | 342 | // set up pwm 343 | ACCESS(PWMBASE + 0x0 /* CTRL*/) = 0; 344 | usleep(T_1MSECOND); 345 | ACCESS(PWMBASE + 0x4 /* status*/) = -1; // clear errors 346 | usleep(T_1MSECOND); 347 | ACCESS(PWMBASE + 0x0 /* CTRL*/) = -1; //(1<<13 /* Use fifo */) | (1<<10 /* repeat */) | (1<<9 /* serializer */) | (1<<8 /* enable ch */) ; 348 | usleep(T_1MSECOND); 349 | ACCESS(PWMBASE + 0x8 /* DMAC*/) = (1<<31 /* DMA enable */) | 0x0707; 350 | 351 | //activate dma 352 | struct DMAregs* DMA0 = (struct DMAregs*)&(ACCESS(DMABASE)); 353 | DMA0->CS =1<<31; // reset 354 | DMA0->CONBLK_AD=0; 355 | DMA0->TI=0; 356 | DMA0->CONBLK_AD = (unsigned int)(instrPage.p); 357 | DMA0->CS =(1<<0)|(255 <<16); // enable bit = 0, clear end flag = 1, prio=19-16 358 | } 359 | 360 | 361 | // 362 | // Set up a memory regions to access GPIO 363 | // 364 | void setup_io() 365 | { 366 | /* open /dev/mem */ 367 | if ((mem_fd = open("/dev/mem", O_RDWR|O_SYNC) ) < 0) { 368 | printf("can't open /dev/mem \n"); 369 | exit (-1); 370 | } 371 | 372 | /* mmap GPIO */ 373 | 374 | // Allocate MAP block 375 | if ((gpio_mem = malloc(BLOCK_SIZE + (PAGE_SIZE-1))) == NULL) { 376 | printf("allocation error \n"); 377 | exit (-1); 378 | } 379 | 380 | // Make sure pointer is on 4K boundary 381 | if ((unsigned long)gpio_mem % PAGE_SIZE) 382 | gpio_mem += PAGE_SIZE - ((unsigned long)gpio_mem % PAGE_SIZE); 383 | 384 | // Now map it 385 | gpio_map = (unsigned char *)mmap( 386 | gpio_mem, 387 | BLOCK_SIZE, 388 | PROT_READ|PROT_WRITE, 389 | MAP_SHARED|MAP_FIXED, 390 | mem_fd, 391 | GPIO_BASE 392 | ); 393 | 394 | if ((long)gpio_map < 0) { 395 | printf("mmap error %d\n", (int)gpio_map); 396 | exit (-1); 397 | } 398 | 399 | // Always use volatile pointer! 400 | gpio = (volatile unsigned *)gpio_map; 401 | 402 | 403 | } 404 | 405 | void setup_gpios() 406 | { 407 | int g; 408 | // Switch GPIO 7..11 to output mode 409 | 410 | /************************************************************************\ 411 | * You are about to change the GPIO settings of your computer. * 412 | * Mess this up and it will stop working! * 413 | * It might be a good idea to 'sync' before running this program * 414 | * so at least you still have your code changes written to the SD-card! * 415 | \************************************************************************/ 416 | 417 | // Set GPIO pins 7-11 to output 418 | for (g=7; g<=11; g++) { 419 | INP_GPIO(g); // must use INP_GPIO before we can use OUT_GPIO 420 | //OUT_GPIO(g); 421 | } 422 | 423 | } 424 | 425 | void strupr(char *str) 426 | { while(*str) 427 | { 428 | *str = toupper(*str); 429 | str++; 430 | } 431 | } 432 | 433 | void wspr(char* call, char* l, char* dbm, unsigned char* symbols) 434 | { 435 | // pack prefix in nadd, call in n1, grid, dbm in n2 436 | char* c, buf[16]; 437 | strncpy(buf, call, 16); 438 | c=buf; 439 | strupr(c); 440 | unsigned long ng,nadd=0; 441 | 442 | if(strchr(c, '/')){ //prefix-suffix 443 | nadd=2; 444 | int i=strchr(c, '/')-c; //stroke position 445 | int n=strlen(c)-i-1; //suffix len, prefix-call len 446 | c[i]='\0'; 447 | 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 448 | if(n==2) ng=60000+26+10*(c[i+1]-'0')+(c[i+2]-'0'); // suffix /10 to /99 449 | if(n>2){ // prefix EA8/, right align 450 | ng=(i<3?36:c[i-3]>='0'&&c[i-3]<='9'?c[i-3]-'0':c[i-3]-'A'+10); 451 | ng=37*ng+(i<2?36:c[i-2]>='0'&&c[i-2]<='9'?c[i-2]-'0':c[i-2]-'A'+10); 452 | ng=37*ng+(i<1?36:c[i-1]>='0'&&c[i-1]<='9'?c[i-1]-'0':c[i-1]-'A'+10); 453 | if(ng<32768) nadd=1; else ng=ng-32768; 454 | c=c+i+1; 455 | } 456 | } 457 | 458 | int i=(isdigit(c[2])?2:isdigit(c[1])?1:0); //last prefix digit of de-suffixed/de-prefixed callsign 459 | int n=strlen(c)-i-1; //2nd part of call len 460 | unsigned long n1; 461 | n1=(i<2?36:c[i-2]>='0'&&c[i-2]<='9'?c[i-2]-'0':c[i-2]-'A'+10); 462 | n1=36*n1+(i<1?36:c[i-1]>='0'&&c[i-1]<='9'?c[i-1]-'0':c[i-1]-'A'+10); 463 | n1=10*n1+c[i]-'0'; 464 | n1=27*n1+(n<1?26:c[i+1]-'A'); 465 | n1=27*n1+(n<2?26:c[i+2]-'A'); 466 | n1=27*n1+(n<3?26:c[i+3]-'A'); 467 | 468 | //if(rand() % 2) nadd=0; 469 | if(!nadd){ 470 | strupr(l); //grid square Maidenhead locator (uppercase) 471 | ng=180*(179-10*(l[0]-'A')-(l[2]-'0'))+10*(l[1]-'A')+(l[3]-'0'); 472 | } 473 | 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} 474 | int corr[]={0,-1,1,0,-1,2,1,0,-1,1}; 475 | p=p>60?60:p<0?0:p+corr[p%10]; 476 | unsigned long n2=(ng<<7)|(p+64+nadd); 477 | 478 | // pack n1,n2,zero-tail into 50 bits 479 | char packed[11] = {n1>>20, n1>>12, n1>>4, ((n1&0x0f)<<4)|((n2>>18)&0x0f), 480 | n2>>10, n2>>2, (n2&0x03)<<6, 0, 0, 0, 0}; 481 | 482 | // convolutional encoding K=32, r=1/2, Layland-Lushbaugh polynomials 483 | int k = 0; 484 | int j,s; 485 | int nstate = 0; 486 | unsigned char symbol[176]; 487 | for(j=0;j!=sizeof(packed);j++){ 488 | for(i=7;i>=0;i--){ 489 | unsigned long poly[2] = { 0xf2d05351L, 0xe4613c47L }; 490 | nstate = (nstate<<1) | ((packed[j]>>i)&1); 491 | for(s=0;s!=2;s++){ //convolve 492 | unsigned long n = nstate & poly[s]; 493 | int even = 0; // even := parity(n) 494 | while(n){ 495 | even = 1 - even; 496 | n = n & (n - 1); 497 | } 498 | symbol[k] = even; 499 | k++; 500 | } 501 | } 502 | } 503 | 504 | // interleave symbols 505 | const unsigned char npr3[162] = { 506 | 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, 507 | 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, 508 | 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, 509 | 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, 510 | 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, 511 | 0,0 }; 512 | for(i=0;i!=162;i++){ 513 | // j0 := bit reversed_values_smaller_than_161[i] 514 | unsigned char j0; 515 | p=-1; 516 | for(k=0;p!=i;k++){ 517 | for(j=0;j!=8;j++) // j0:=bit_reverse(k) 518 | j0 = ((k>>j)&1)|(j0<<1); 519 | if(j0<162) 520 | p++; 521 | } 522 | symbols[j0]=npr3[j0]|symbol[i]<<1; //interleave and add sync vector 523 | } 524 | } 525 | 526 | void wait_every(int minute) 527 | { 528 | time_t t; 529 | struct tm* ptm; 530 | for(;;){ 531 | time(&t); 532 | ptm = gmtime(&t); 533 | if((ptm->tm_min % minute) == 0 && ptm->tm_sec == 0) break; 534 | usleep(T_1MSECOND); 535 | } 536 | usleep(T_SECOND); // wait another second 537 | } 538 | 539 | // http://www-theorie.physik.unizh.ch/~dpotter/howto/daemonize 540 | static void child_handler(int signum) 541 | { 542 | switch(signum) { 543 | case SIGALRM: exit(EXIT_FAILURE); break; 544 | case SIGUSR1: exit(EXIT_SUCCESS); break; 545 | case SIGCHLD: exit(EXIT_FAILURE); break; 546 | } 547 | } 548 | 549 | // http://www-theorie.physik.unizh.ch/~dpotter/howto/daemonize 550 | static void daemonize( const char *lockfile ) 551 | { 552 | pid_t pid, sid, parent; 553 | int lfp = -1; 554 | 555 | /* already a daemon */ 556 | if ( getppid() == 1 ) return; 557 | 558 | /* Create the lock file as the current user */ 559 | if ( lockfile && lockfile[0] ) { 560 | lfp = open(lockfile,O_RDWR|O_CREAT,0640); 561 | if ( lfp < 0 ) { 562 | syslog( LOG_ERR, "unable to create lock file %s, code=%d (%s)", 563 | lockfile, errno, strerror(errno) ); 564 | exit(EXIT_FAILURE); 565 | } 566 | } 567 | 568 | /* Drop user if there is one, and we were run as root */ 569 | if ( getuid() == 0 || geteuid() == 0 ) { 570 | struct passwd *pw = getpwnam(RUN_AS_USER); 571 | if ( pw ) { 572 | syslog( LOG_NOTICE, "setting user to " RUN_AS_USER ); 573 | setuid( pw->pw_uid ); 574 | } 575 | } 576 | 577 | /* Trap signals that we expect to receive */ 578 | signal(SIGCHLD,child_handler); 579 | signal(SIGUSR1,child_handler); 580 | signal(SIGALRM,child_handler); 581 | 582 | /* Fork off the parent process */ 583 | pid = fork(); 584 | if (pid < 0) { 585 | syslog( LOG_ERR, "unable to fork daemon, code=%d (%s)", 586 | errno, strerror(errno) ); 587 | exit(EXIT_FAILURE); 588 | } 589 | /* If we got a good PID, then we can exit the parent process. */ 590 | if (pid > 0) { 591 | 592 | /* Wait for confirmation from the child via SIGTERM or SIGCHLD, or 593 | for two seconds to elapse (SIGALRM). pause() should not return. */ 594 | alarm(2); 595 | pause(); 596 | 597 | exit(EXIT_FAILURE); 598 | } 599 | 600 | /* At this point we are executing as the child process */ 601 | parent = getppid(); 602 | 603 | /* Cancel certain signals */ 604 | signal(SIGCHLD,SIG_DFL); /* A child process dies */ 605 | signal(SIGTSTP,SIG_IGN); /* Various TTY signals */ 606 | signal(SIGTTOU,SIG_IGN); 607 | signal(SIGTTIN,SIG_IGN); 608 | signal(SIGHUP, SIG_IGN); /* Ignore hangup signal */ 609 | signal(SIGTERM,SIG_DFL); /* Die on SIGTERM */ 610 | 611 | /* Change the file mode mask */ 612 | umask(0); 613 | 614 | /* Create a new SID for the child process */ 615 | sid = setsid(); 616 | if (sid < 0) { 617 | syslog( LOG_ERR, "unable to create a new session, code %d (%s)", 618 | errno, strerror(errno) ); 619 | exit(EXIT_FAILURE); 620 | } 621 | 622 | /* Change the current working directory. This prevents the current 623 | directory from being locked; hence not being able to remove it. */ 624 | if ((chdir("/")) < 0) { 625 | syslog( LOG_ERR, "unable to change directory to %s, code %d (%s)", 626 | "/", errno, strerror(errno) ); 627 | exit(EXIT_FAILURE); 628 | } 629 | 630 | /* Redirect standard files to /dev/null */ 631 | freopen( "/dev/null", "r", stdin); 632 | freopen( "/dev/null", "w", stdout); 633 | freopen( "/dev/null", "w", stderr); 634 | 635 | /* Tell the parent process that we are A-okay */ 636 | kill( parent, SIGUSR1 ); 637 | } 638 | 639 | 640 | 641 | int main(int argc, char *argv[]) 642 | { 643 | unsigned char symbols[162]; 644 | int i; 645 | double centre_freq; 646 | int wspr15; 647 | double wspr_symtime; 648 | int nbands = argc - 4; 649 | int band = 0; 650 | int tune_mode = 0; 651 | 652 | /* Initialize the logging interface */ 653 | openlog( DAEMON_NAME, LOG_PID, LOG_LOCAL5 ); 654 | syslog( LOG_INFO, "starting" ); 655 | 656 | /* Commandline Stuff */ 657 | if(argc < 5){ 658 | printf("Usage: wspr <[prefix/]callsign[/A-Z,/0-9,/00-99]> [ ...]\n"); 659 | printf("\te.g.: ./wspr K1JT/P JO21 10 7040074 0 0 10140174 0 0\n"); 660 | return 1; 661 | } 662 | 663 | // argv[1]=callsign, argv[2]=locator, argv[3]=power(dBm) 664 | // negative dBm will setup constant tx for tuning 665 | if (atoi(argv[3]) < 0) 666 | { 667 | printf("Tune mode\n"); 668 | tune_mode = 1; 669 | } 670 | 671 | wspr(argv[1], argv[2], argv[3], symbols); 672 | printf("Symbols: "); 673 | for (i = 0; i < sizeof(symbols)/sizeof(*symbols); i++) 674 | printf("%d,", symbols[i]); 675 | printf("\n"); 676 | 677 | setup_io(); 678 | setup_gpios(); 679 | txon(); 680 | setupDMA(); 681 | 682 | printf("Ready for transmit...\n"); 683 | 684 | /* Daemonize */ 685 | //daemonize( "/var/lock/" DAEMON_NAME ); 686 | 687 | for(;;) 688 | { 689 | txoff(); 690 | centre_freq = atof(argv[band + 4]); 691 | wspr15 = (centre_freq > 137600 && centre_freq < 137625) || \ 692 | (centre_freq > 475800 && centre_freq < 475825) || \ 693 | (centre_freq > 1838200 && centre_freq < 1838225); 694 | wspr_symtime = (wspr15) ? 8.0 * WSPR_SYMTIME : WSPR_SYMTIME; 695 | band++; 696 | if(band >= nbands) 697 | band = 0; 698 | if(centre_freq) setupDMATab(centre_freq, 1.0/wspr_symtime, wspr_symtime, 4); 699 | if (tune_mode == 0) 700 | { 701 | wait_every((wspr15) ? 15 : 2); 702 | } 703 | time_t t; 704 | time(&t); 705 | char buf[256]; 706 | strcpy(buf,ctime(&t)); 707 | buf[strlen(buf)-1]='\0'; 708 | printf("%s - %s@%f\n", buf, (wspr15)?"wspr-15":"wspr-2", centre_freq); 709 | if(centre_freq){ 710 | txon(); 711 | for (i = 0; i < 162; i++) { 712 | txSym(symbols[i], wspr_symtime); 713 | //txSym(atoi(argv[5]), wspr_symtime); 714 | } 715 | } 716 | } 717 | 718 | /* Finish up */ 719 | syslog( LOG_NOTICE, "terminated" ); 720 | closelog(); 721 | 722 | return 0; 723 | } 724 | --------------------------------------------------------------------------------