diff --git a/.gitignore b/.gitignore index b8bd026..4eeaedb 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ +PiCW + # Compiled Object files *.slo *.lo diff --git a/BUILD b/BUILD new file mode 100644 index 0000000..3b20f80 --- /dev/null +++ b/BUILD @@ -0,0 +1,22 @@ +Install required packages: + sudo apt-get install git g++-4.7 make grep mawk ntp +Note that this code requires g++-4.7 which is not installed by default in +Raspbian! + +Make sure you are using the latest kernel by updating your system. The latest +kernel includes fixes wich improve NTP ppm measurement accuracy: + sudo apt-get update + sudo apt-get dist-upgrade + +Get code/ compile: + rm -rf PiCW + git clone https://github.com/JamesP6000/PiCW.git + cd PiCW + make + +Install to /usr/local/bin: + sudo make install + +Uninstall: + sudo make uninstall + diff --git a/ISSUES b/ISSUES new file mode 100644 index 0000000..59dc7aa --- /dev/null +++ b/ISSUES @@ -0,0 +1,15 @@ +Issues From original WsprryPi code: + + Two users were reporting that the program never stops transmitting, even + when intervals for disabled tx are programmed. The problem was in both + cases fixed by flashing a new image on the SD card with a freshly downloaded + image: 2013-02-09-wheezy-raspbian.zip. No apt-get upgrade or firmware + upgrade was performed. After this WsprryPi TX was running successfully. + + One user reported his RPi died while in WsprryPi service caused by excessive + RF voltage (90V) on GPIO4 created by a 100 watts AM transmitter 50ft away + from the antenna. After the damage exessive current was consumed by RPi (1.1A + from 5V supply), caused by short-circuiting in the 3.3V logic of the BCM2835 + SOC. On his replacement RPi, he is planning to add galvanic isolation and + buffering. + diff --git a/PiCW.cpp b/PiCW.cpp index 188607f..052969a 100644 --- a/PiCW.cpp +++ b/PiCW.cpp @@ -1,22 +1,23 @@ // See accompanying README and BUILD files for descriptions on how to use this // code. -/* -License: - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 2 of the License, or - (at your option) any later version. +// License: +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . - -*/ +// Authors: +// Oliver Mattos, Oskar Weigl, Dan Ankers (MD1CLV), Guido (PE1NNZ), +// Michael Tatarinov, James Peroulas (AB0JP) #include #include @@ -102,22 +103,6 @@ volatile unsigned *allof7e = NULL; #define DMABASE (0x7E007000) #define PWMBASE (0x7e20C000) /* PWM controller */ -// g++ 4.7 in c++11 mode had trouble with this struct. I removed it -// and used bit shifts to create the word to be written. -/* -struct GPCTL { - char SRC : 4; - char ENAB : 1; - char KILL : 1; - char : 1; - char BUSY : 1; - char FLIP : 1; - char MASH : 2; - unsigned int : 13; - char PASSWD : 8; -}; -*/ - struct CB { volatile unsigned int TI; volatile unsigned int SOURCE_AD; @@ -172,64 +157,6 @@ void freeRealMemPage(void* vAddr) { free(vAddr); } -void txon() -{ - SETBIT(GPFSEL0 , 14); - CLRBIT(GPFSEL0 , 13); - CLRBIT(GPFSEL0 , 12); - - // Set GPIO drive strength, more info: http://www.scribd.com/doc/101830961/GPIO-Pads-Control2 - //ACCESS(PADS_GPIO_0_27) = 0x5a000018 + 0; //2mA -3.4dBm - //ACCESS(PADS_GPIO_0_27) = 0x5a000018 + 1; //4mA +2.1dBm - //ACCESS(PADS_GPIO_0_27) = 0x5a000018 + 2; //6mA +4.9dBm - //ACCESS(PADS_GPIO_0_27) = 0x5a000018 + 3; //8mA +6.6dBm(default) - //ACCESS(PADS_GPIO_0_27) = 0x5a000018 + 4; //10mA +8.2dBm - //ACCESS(PADS_GPIO_0_27) = 0x5a000018 + 5; //12mA +9.2dBm - //ACCESS(PADS_GPIO_0_27) = 0x5a000018 + 6; //14mA +10.0dBm - ACCESS(PADS_GPIO_0_27) = 0x5a000018 + 7; //16mA +10.6dBm - - //struct GPCTL setupword = {6/*SRC*/, 1, 0, 0, 0, 3,0x5a}; - //ACCESS(CM_GP0CTL) = *((int*)&setupword); - ACCESS(CM_GP0CTL) = - // PW - (0x5a<<24) | - // MASH - (3<<9) | - // Flip - (0<<8) | - // Busy - (0<<7) | - // Kill - (0<<5) | - // Enable - (1<<4) | - // SRC - (6<<0) - ; -} - -void txoff() -{ - //struct GPCTL setupword = {6/*SRC*/, 0, 0, 0, 0, 1,0x5a}; - //ACCESS(CM_GP0CTL) = *((int*)&setupword); - ACCESS(CM_GP0CTL) = - // PW - (0x5a<<24) | - // MASH - (1<<9) | - // Flip - (0<<8) | - // Busy - (0<<7) | - // Kill - (0<<5) | - // Enable - (0<<4) | - // SRC - (6<<0) - ; -} - // Transmit tone tone_freq for tsym seconds. // // TODO: @@ -306,7 +233,23 @@ void unSetupDMA(){ //printf("exiting\n"); struct DMAregs* DMA0 = (struct DMAregs*)&(ACCESS(DMABASE)); DMA0->CS =1<<31; // reset dma controller - txoff(); + // Turn off GPIO clock + ACCESS(CM_GP0CTL) = + // PW + (0x5a<<24) | + // MASH + (1<<9) | + // Flip + (0<<8) | + // Busy + (0<<7) | + // Kill + (0<<5) | + // Enable + (0<<4) | + // SRC + (6<<0) + ; } void handSig(const int h) { @@ -440,7 +383,6 @@ void setupDMA( DMA0->CS =(1<<0)|(255 <<16); // enable bit = 0, clear end flag = 1, prio=19-16 } - // // Set up memory regions to access GPIO // @@ -512,21 +454,23 @@ void setup_gpios( void print_usage() { std::cout << "Usage:" << std::endl; - std::cout << " PiCW [options] \"MORSE TEXT TO SEND\"" << std::endl; + std::cout << " PiCW [options] \"text to send in Morse code\"" << std::endl; std::cout << std::endl; std::cout << "Options:" << std::endl; std::cout << " -h --help" << std::endl; std::cout << " Print out this help screen." << std::endl; std::cout << " -f --freq f" << std::endl; - std::cout << " Specify the frequency to be used for the transmission" << std::endl; + std::cout << " Specify the frequency to be used for the transmission." << std::endl; std::cout << " -w --wpm w" << std::endl; - std::cout << " Specify the transmission speed in Words Per Minute" << std::endl; + std::cout << " Specify the transmission speed in Words Per Minute (default 20 WPM)." << std::endl; std::cout << " -p --ppm ppm" << std::endl; std::cout << " Known PPM correction to 19.2MHz RPi nominal crystal frequency." << std::endl; std::cout << " -s --self-calibration" << std::endl; std::cout << " Call ntp_adjtime() periodically to obtain the PPM error of the crystal." << std::endl; std::cout << " -d --ditdit" << std::endl; - std::cout << " Transmit an endless series of dits. Can be used to measure TX spectrum" << std::endl; + std::cout << " Transmit an endless series of dits. Can be used to measure TX spectrum." << std::endl; + std::cout << " -t --test-tone" << std::endl; + std::cout << " Continuously transmit a test tone at the requested frequency." << std::endl; } void parse_commandline( @@ -539,7 +483,8 @@ void parse_commandline( double & ppm, bool & self_cal, std::string & str, - bool & ditdit + bool & ditdit, + bool & test_tone ) { // Default values tone_freq=NAN; @@ -548,6 +493,7 @@ void parse_commandline( self_cal=false; str=""; ditdit=false; + test_tone=false; static struct option long_options[] = { {"help", no_argument, 0, 'h'}, @@ -556,6 +502,7 @@ void parse_commandline( {"ppm", required_argument, 0, 'p'}, {"self-calibration", no_argument, 0, 's'}, {"ditdit", no_argument, 0, 'd'}, + {"test-tone", no_argument, 0, 't'}, {0, 0, 0, 0} }; @@ -606,6 +553,9 @@ void parse_commandline( case 'd': ditdit=true; break; + case 't': + test_tone=true; + break; case '?': /* getopt_long already printed an error message. */ ABORT(-1); @@ -633,12 +583,17 @@ void parse_commandline( ABORT(-1); } if ((!str.empty())&&ditdit) { - MARK; - std::cout << str << std::endl; - MARK; std::cerr << "Error: cannot transmit text when ditdit mode is requested" << std::endl; ABORT(-1); } + if ((!str.empty())&&test_tone) { + std::cerr << "Error: cannot transmit text when test-tone mode is requested" << std::endl; + ABORT(-1); + } + if (test_tone&&ditdit) { + std::cerr << "Error: cannot request test-tone and ditdit modes at the same time" << std::endl; + ABORT(-1); + } // Print a summary of the parsed options std::cout << "PiCW parsed command line options:" << std::endl; @@ -647,7 +602,9 @@ void parse_commandline( temp << tone_freq/1e6 << " MHz"; std::cout << " TX frequency: " << temp.str() << std::endl; temp.str(""); - std::cout << " WPM: " << wpm << std::endl; + if (!test_tone) { + std::cout << " WPM: " << wpm << std::endl; + } if (self_cal) { temp << " ntp_adjtime() will be used to periodically calibrate the transmission frequency" << std::endl; } else if (ppm) { @@ -655,6 +612,8 @@ void parse_commandline( } if (ditdit) { std::cout << "Will transmit an endless series of dits. CTRL-C to exit." << std::endl; + } else if (test_tone) { + std::cout << "Will transmit continuous tone on frequency. CTRL-C to exit." << std::endl; } else { std::cout << "Message to be sent:" << std::endl; std::cout << '"' << str << '"' << std::endl; @@ -746,12 +705,15 @@ void tone_main( } } +// The rise and fall ramps are stored as collections of time/value pairs. class time_value { public: std::chrono::duration time; unsigned int value; }; +// Rectangular ramp that simply goes high or low in the middle of the ramp +// period. void rectangle( const double & width_secs, std::vector & rise, @@ -787,24 +749,7 @@ void rectangle( } } -// Raised cosine pulse shapes. -// Rise: -//y=(-cos(t*pi)+1)/2; -//2*y-1=-cos(t*pi); -//1-2*y=cos(t*pi); -//acos(1-2*y)=t*pi; -//acos(1-2*y)/pi=t; -// Fall: -//y=1-(-cos(t*pi)+1)/2; -//y-1=-(-cos(t*pi)+1)/2; -//2*y-2=-(-cos(t*pi)+1); -//2-2*y=-cos(t*pi)+1; -//1-2*y=-cos(t*pi); -//2*y-1=cos(t*pi); -//acos(2*y-1)=t*pi; -//acos(2*y-1)/pi=t; -// Instead of uniform sampling of the x axis, we're using uniform -// sampling of the y axis. +// Raised cosine rise/ fall ramps. void raised_cosine( const double & width_secs, std::vector & rise, @@ -852,8 +797,7 @@ void set_current( value=8; } if (value==0) { - //struct GPCTL setupword = {6/*SRC*/, 0, 0, 0, 0, 1,0x5a}; - //ACCESS(CM_GP0CTL) = *((int*)&setupword); + // Turn off output ACCESS(CM_GP0CTL) = // PW (0x5a<<24) | @@ -871,9 +815,9 @@ void set_current( (6<<0) ; } else { + // Set drive strength ACCESS(PADS_GPIO_0_27) = 0x5a000018 + ((value - 1)&0x7); - //struct GPCTL setupword = {6/*SRC*/, 1, 0, 0, 0, 3,0x5a}; - //ACCESS(CM_GP0CTL) = *((int*)&setupword); + // Turn on output ACCESS(CM_GP0CTL) = // PW (0x5a<<24) | @@ -913,7 +857,7 @@ void send_dit_dah( const std::chrono::duration jitter_rise(dis(gen)); const std::chrono::duration jitter_fall(dis(gen)); - // Calculate the rise and fall ramps. + // Calculate the rise and fall ramps, if needed. static bool initialized=false; static std::chrono::duration ramp_time_prev(0); static std::vector rise; @@ -935,7 +879,7 @@ void send_dit_dah( initialized=true; } - // Pulse will be timed relative to the current time. + // Dit or dah pulse will be timed relative to the current time. std::chrono::high_resolution_clock::time_point ref=std::chrono::high_resolution_clock::now(); // Delay the rising ramp. @@ -978,8 +922,21 @@ void am_main( std::map & morse_table, std::atomic & wpm, std::atomic & busy, - const bool & ditdit + const bool & ditdit, + const bool & test_tone ) { + // In the case of a test tone, set the drive strength to maximum and + // turn on output. Nothing else. + if (test_tone) { + set_current(8); + while (true) { + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + if (terminate) { + return; + } + } + } + bool prev_char_whitespace=true; std::chrono::time_point > earliest_tx_time=std::chrono::high_resolution_clock::now(); @@ -1043,14 +1000,12 @@ void am_main( } else { tx_pattern=morse_table[tx_char]; } - bool printed=false; for (unsigned int t=0;t (3*dot_duration_sec); - + earliest_tx_time+=std::chrono::duration (2*dot_duration_sec); } } +// Initialize the morse code table. void morse_table_init( std::map & morse_table ) { @@ -1127,16 +1082,14 @@ void morse_table_init( } int main(const int argc, char * const argv[]) { - // Initialize the RNG - //srand(time(NULL)); - // Parse arguments double freq_init; double wpm_init; double ppm_init; bool self_cal; - bool ditdit; std::string str; + bool ditdit; + bool test_tone; parse_commandline( argc, argv, @@ -1145,7 +1098,8 @@ int main(const int argc, char * const argv[]) { ppm_init, self_cal, str, - ditdit + ditdit, + test_tone ); // Initial configuration @@ -1167,53 +1121,14 @@ int main(const int argc, char * const argv[]) { ABORT(-1); } - //txon(); + // Configure GPIO4 SETBIT(GPFSEL0 , 14); CLRBIT(GPFSEL0 , 13); CLRBIT(GPFSEL0 , 12); - // Set GPIO drive strength, more info: http://www.scribd.com/doc/101830961/GPIO-Pads-Control2 - /* - ACCESS(PADS_GPIO_0_27) = 0x5a000018 + 0; //2mA -3.4dBm - ACCESS(CM_GP0CTL) = - // PW - (0x5a<<24) | - // MASH - (3<<9) | - // Flip - (0<<8) | - // Busy - (0<<7) | - // Kill - (0<<5) | - // Enable - (0<<4) | - // SRC - (6<<0) - ; - */ struct PageInfo constPage; struct PageInfo instrPage; struct PageInfo instrs[1024]; setupDMA(constPage,instrPage,instrs); - //txoff(); - /* - ACCESS(CM_GP0CTL) = - // PW - (0x5a<<24) | - // MASH - (1<<9) | - // Flip - (0<<8) | - // Busy - (0<<7) | - // Kill - (0<<5) | - // Enable - (0<<4) | - // SRC - (6<<0) - ; - */ // Morse code table. std::map morse_table; @@ -1242,7 +1157,6 @@ int main(const int argc, char * const argv[]) { while (!tone_thread_ready) { std::this_thread::sleep_for(std::chrono::milliseconds(100)); } - //std::this_thread::sleep_for(std::chrono::milliseconds(1000)); // Start AM thread std::atomic terminate_am_thread; @@ -1260,7 +1174,8 @@ int main(const int argc, char * const argv[]) { std::ref(morse_table), std::ref(wpm), std::ref(am_thread_busy), - ditdit + ditdit, + test_tone ); // Push text into AM thread @@ -1275,8 +1190,8 @@ int main(const int argc, char * const argv[]) { queue_signal.notify_one(); } - // In ditdit mode, can only exit using ctrl-c. - while (ditdit) { + // In ditdit or test-tone mode, can only exit using ctrl-c. + while (ditdit||test_tone) { std::this_thread::sleep_for(std::chrono::milliseconds(200)); } diff --git a/README b/README new file mode 100644 index 0000000..e15f866 --- /dev/null +++ b/README @@ -0,0 +1,169 @@ +Raspberry Pi bareback LF/MF/HF/VHF CW (Morse code) transmitter + +Makes a very simple Morse Code transmitter from your RasberryPi by connecting +GPIO port 4 to Antenna (and LPF). Operates on LF, MF, HF and VHF bands from 0 +to 250 MHz. + +****** +Installation / update: +****** + Simple instructions assuming you have all the prerequisites installed: + git clone https://github.com/JamesP6000/PiCW.git + cd PiCW + make + + See the accompanying BUILD file for more details. + +****** +Example usage: +****** + Brief help screen + PiCW --help + + Send the Morse code message "TEST DE N9NNN" on carrier frequency 10.140 MHz + using the default rate of 20 WPM: + sudo PiCW --freq 10.140e6 TEST DE N9NNN + + As above, but this time use NTP to calibrate the TX frequency: + sudo PiCW --freq 10.140e6 --self-calibration TEST DE N9NNN + + Transmit an endless series of dits at 60 WPM. Can be used to measure the + worst case frequency domain performance of the transmitter. + sudo PiCW --freq 10.140e6 --ditdit --wpm 60 + +****** +"PiCW --help" output: +****** + Usage: + PiCW [options] "text to send in Morse code" + + Options: + -h --help + Print out this help screen. + -f --freq f + Specify the frequency to be used for the transmission. + -w --wpm w + Specify the transmission speed in Words Per Minute (default 20 WPM). + -p --ppm ppm + Known PPM correction to 19.2MHz RPi nominal crystal frequency. + -s --self-calibration + Call ntp_adjtime() periodically to obtain the PPM error of the crystal. + -d --ditdit + Transmit an endless series of dits. Can be used to measure TX spectrum. + -t --test-tone + Continuously transmit a test tone at the requested frequency. + +****** +Radio licensing / RF: +****** + In order to transmit legally, a HAM Radio License is REQUIRED for running + this experiment. The output is a square wave so a low pass filter is REQUIRED. + Connect a low-pass filter (via decoupling C) to GPIO4 (GPCLK0) and a ground + pin of your Raspberry Pi, then connect an antenna to the LPF. The GPIO4 and + GND pins are found on header P1 pin 7 and 9 respectively, the pin closest to + P1 label is pin 1 and its 3rd and 4th neighbour is pin 7 and 9 respectively. + See this link for pin layout: http://elinux.org/RPi_Low-level_peripherals + Examples of low-pass filters can be found here: + http://www.gqrp.com/harmonic_filters.pdf + + The expected power output is 10mW (+10dBm) in a 50 Ohm load. This looks + neglible, but when connected to a simple dipole antenna this may result in + reception reports ranging up to several thousands of kilometers. + + As the Raspberry Pi does not attenuate ripple and noise components from the + 5V USB power supply, it is RECOMMENDED to use a regulated supply that has + sufficient ripple supression. Supply ripple might be seen as mixing products + centered around the transmit carrier typically at 100/120Hz. + + DO NOT expose GPIO4 to voltages or currents that are above the specified + Absolute Maximum limits. GPIO4 outputs a digital clock in 3V3 logic, with a + maximum current of 16mA. As there is no current protection available and a DC + component of 1.6V, DO NOT short-circuit or place a resistive (dummy) load + straight on the GPIO4 pin, as it may draw too much current. Instead, use a + decoupling capacitor to remove DC component when connecting the output to + dummy loads, transformers, antennas, etc. DO NOT expose GPIO4 to electro- + static voltages or voltages exceeding the 0 to 3.3V logic range. Connecting + an antenna directly to GPIO4 may damage your RPi due to transient voltages + such as lightning or static buildup as well as RF from other transmitters + operating into nearby antennas. Therefore it is RECOMMENDED to add some form + of isolation, e.g. by using a RF transformer, a simple buffer/driver/PA + stage, two schottky small signal diodes back to back. + +****** +Calibration: +****** + Frequency calibration is HIGHLY recommended to ensure that your + transmissions lie within the CW band you are targetting. + + NTP calibration: + NTP automatically tracks and calculates a PPM frequency correction. If your + Pi is connected to the internet and you are running NTP, you can use the + --self-calibration option to have PiCW periodically querry NTP for the latest + frequency correction. Some residual frequency error may still be present + due to delays in the NTP measurement loop. This method works best if your + Pi has been on for a long time, the crystal's temperature has stabilized, + and the NTP control loop has converged. + + AM calibration: + A practical way to calibrate is to tune the transmitter on the same frequency + of a medium wave AM broadcast station. Keep tuning until you zero beat (the + constant audio tone disappears when the transmitter is exactly on the same + frequency as the broadcast station), and determine the frequency difference + with the broadcast station. This is the frequency error that can be applied + for correction while tuning on a WSPR frequency. + + Suppose your local AM radio station is at 780kHz. Use the --test-tone option + to produce different tones around 780kHz (eg 780100 Hz) until you can + successfully zero beat the AM station. If the zero beat tone specified on the + command line is F, calculate the PPM correction required as: + ppm=(F/780000-1)*1e6 In the future, specify this value as the argument to the + --ppm option on the command line. + +****** +PWM Peripheral: +****** + The code uses the RPi PWM peripheral to time the frequency transitions + of the output clock. This peripheral is also used by the RPi sound system + and hence any sound events that occur during transmission will + interfere with CW transmissions. Sound can be permanently disabled + by editing /etc/modules and commenting out the snd-bcm2835 device. + +****** +Reference documentation: +****** + http://www.raspberrypi.org/wp-content/uploads/2012/02/BCM2835-ARM-Peripherals.pdf + http://www.scribd.com/doc/127599939/BCM2835-Audio-clocks + 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 + +****** +History/Credits: +****** + Credits go to Oliver Mattos and Oskar Weigl who implemented PiFM based on + the idea of exploiting RPi DPLL as FM transmitter. + http://www.icrobotics.co.uk/wiki/index.php/Turning_the_Raspberry_Pi_Into_an_FM_Transmitter + + Dan MD1CLV combined this effort with WSPR encoding algorithm from F8CHK, + resulting in WsprryPi a WSPR beacon for LF and MF bands. + https://github.com/DanAnkers/WsprryPi + + Guido PE1NNZ extended this effort with DMA based PWM + modulation of fractional divider that was part of PiFM, allowing to operate + the WSPR beacon also on HF and VHF bands. In addition time-synchronisation + and double amount of power output was implemented. + https://github.com/threeme3/WsprryPi + + James Peroulas added several command line options, a + makefile, improved frequency generation precision so as to be able to + precisely generate a tone at a fraction of a Hz, and a self calibration + feature where the code attempts to derrive frequency calibration information + from an installed NTP deamon. + https://github.com/JamesP6000/WsprryPi + + Michael Tatarinov for adding a patch to get PPM info directly from the + kernel. + + James Peroulas created PiCW. + https://github.com/JamesP6000/PiCW + diff --git a/makefile b/makefile index 9926ac6..60c6f0f 100644 --- a/makefile +++ b/makefile @@ -3,15 +3,13 @@ prefix=/usr/local all: PiCW PiCW: PiCW.cpp - g++-4.7 -D_GLIBCXX_DEBUG -std=c++11 -Wall -Werror -fmax-errors=5 -lm PiCW.cpp -oPiCW -pthread + g++-4.7 -D_GLIBCXX_DEBUG -std=c++11 -Wall -Werror -fmax-errors=5 -lm PiCW.cpp -pthread -oPiCW -#.PHONY: install -#install: wspr -# install -m 0755 wspr $(prefix)/bin -# install -m 0755 gpioclk $(prefix)/bin +.PHONY: install +install: PiCW + install -m 0755 PiCW $(prefix)/bin -#.PHONY: uninstall -#uninstall: -# rm -f $(prefix)/bin/wspr -# rm -f $(prefix)/bin/gpioclk +.PHONY: uninstall +uninstall: + rm -f $(prefix)/bin/PiCW diff --git a/pulse_shape.m b/pulse_shape.m index 910ba6d..2ae89ee 100644 --- a/pulse_shape.m +++ b/pulse_shape.m @@ -1,9 +1,13 @@ +% Octave/ Matlab code used to explore the frequency domain effects of various +% pulse shaping functions. + pkg load signal fs=1e6; wpm=60; ramp_excess=.3; +% Derive some values period=1200/wpm*.001*2; ramp_time=period/2*ramp_excess; flat_time=period/2*(1-ramp_excess); @@ -13,6 +17,7 @@ n_flat=round(flat_time*fs); printf('on_time = %5.2f ms\n',period/2*1000); printf('ramp = %5.2f ms\n',ramp_time*1000); +% Different ramp functions %ramp_func=@(x)x; %ramp=ramp_func(linspace(0,1,n_ramp)); ramp_func=@(x)(-cos(x*pi)+1)/2; @@ -30,12 +35,15 @@ ramp=ramp_func(linspace(0,1,n_ramp)); ramp=ramp-ramp(1); ramp=ramp/max(ramp)*1; +% Create signal sig=[ramp ones(1,n_flat) fliplr(ramp) zeros(1,n_samp-n_flat-2*n_ramp)]; %sig=sig-mean(sig); +% Quantize sig=round(sig*8)/8; sig=sig+randn(1,length(sig))/10000; +% Plot frequency response f=linspace(0,2*fs,n_samp+1); f=f(1:end-1); m=abs((fft(sig))).^2; @@ -44,6 +52,7 @@ plot(f,10*log10(m)); xlim([0 2000]); ylim([-80 0]); +% Calculate occupied bandwidth tot_pwr=sum(m(1:floor(length(m)/2))); bw_percentage=0.999; for obw=1:length(f) @@ -52,5 +61,6 @@ for obw=1:length(f) end end +% Occupied bandwidth is from -2*fs/n_samp*obw to +2*fs/n_samp*obw. fprintf('TX %5.2f%% cutoff frequency offset: %6.2f Hz\n',bw_percentage*100,2*fs/n_samp*obw);