Added documentation.

master
James Peroulas 2015-01-15 13:01:46 -08:00
rodzic 804afa8e29
commit 74bda60502
7 zmienionych plików z 319 dodań i 188 usunięć

2
.gitignore vendored
Wyświetl plik

@ -1,3 +1,5 @@
PiCW
# Compiled Object files
*.slo
*.lo

22
BUILD 100644
Wyświetl plik

@ -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

15
ISSUES 100644
Wyświetl plik

@ -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.

273
PiCW.cpp
Wyświetl plik

@ -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 <http://www.gnu.org/licenses/>.
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 <http://www.gnu.org/licenses/>.
*/
// Authors:
// Oliver Mattos, Oskar Weigl, Dan Ankers (MD1CLV), Guido (PE1NNZ),
// Michael Tatarinov, James Peroulas (AB0JP)
#include <map>
#include <deque>
@ -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 <double> 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 <time_value> & 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 <time_value> & 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 <double> jitter_rise(dis(gen));
const std::chrono::duration <double> 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 <double> ramp_time_prev(0);
static std::vector <time_value> 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 <char,std::string> & morse_table,
std::atomic <double> & wpm,
std::atomic <bool> & 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 <std::chrono::high_resolution_clock,std::chrono::duration <double>> 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<tx_pattern.length();t++) {
std::this_thread::sleep_until(earliest_tx_time);
if (terminate) {
return;
}
if ((!ditdit)&&(!printed)) {
printed=true;
if ((!ditdit)&&(t==0)) {
std::cout << tx_char;
std::cout.flush();
}
@ -1065,11 +1020,11 @@ void am_main(
t=0;
}
}
earliest_tx_time+=std::chrono::duration <double> (3*dot_duration_sec);
earliest_tx_time+=std::chrono::duration <double> (2*dot_duration_sec);
}
}
// Initialize the morse code table.
void morse_table_init(
std::map <char,std::string> & 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 <char,std::string> 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 <bool> 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));
}

169
README 100644
Wyświetl plik

@ -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 <pe1nnz@amsat.org> 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 <james@peroulas.com> 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 <james@peroulas.com> created PiCW.
https://github.com/JamesP6000/PiCW

Wyświetl plik

@ -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

Wyświetl plik

@ -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);