kopia lustrzana https://github.com/markondej/fm_transmitter
commit
c78045961a
15
README.md
15
README.md
|
@ -1,8 +1,9 @@
|
|||
# fm_transmitter
|
||||
Use Raspberry Pi as FM transmitter. Works on any Raspberry Pi board.
|
||||
Use Raspberry Pi as FM transmitter. Works on every Raspberry Pi board.
|
||||
|
||||
This project uses the general clock output to produce frequency modulated radio communication. It is based on idea originaly presented by [Oliver Mattos and Oskar Weigl](http://icrobotics.co.uk/wiki/index.php/Turning_the_Raspberry_Pi_Into_an_FM_Transmitter) on [PiFM project](http://icrobotics.co.uk/wiki/index.php/Turning_the_Raspberry_Pi_Into_an_FM_Transmitter).
|
||||
|
||||
Just get an FM receiver and connect 20 - 40 cm plain wire to GPIO4 (PIN 7 on Raspberry Pi GPIO header) to act as an antena.
|
||||
## How to use it
|
||||
To use this project You will have to build it. First, clone this repository, then use "make" command as shown below:
|
||||
```
|
||||
|
@ -17,11 +18,11 @@ sudo ./fm_transmitter -f 102.0 acoustic_guitar_duet.wav
|
|||
Where:
|
||||
* -f frequency - Specifies the frequency in MHz, 100.0 by default if not passed
|
||||
* acoustic_guitar_duet.wav - Sample WAVE file, You can use your own
|
||||
|
||||
Other options:
|
||||
* -d dma_channel - Specifies the used DMA channel (0 by default), pass 255 in order to disable DMA and use CPU
|
||||
* -d dma_channel - Specifies DMA channel to be used (0 by default), type 255 to disable DMA transfer, CPU will be used instead
|
||||
* -b bandwidth - Specifies the bandwidth in kHz, 100 by default
|
||||
* -r - Loops the playback
|
||||
|
||||
After transmission has begun, simply tune an FM receiver to chosen frequency, You should hear the playback.
|
||||
### Supported audio formats
|
||||
You can transmitt uncompressed WAVE (.wav) files directly or read audio data from stdin, eg.:
|
||||
```
|
||||
|
@ -38,14 +39,12 @@ In order to use a USB microphone input use arecord command, eg.:
|
|||
arecord -D hw:1,0 -c1 -d 0 -r 22050 -f S16_LE | sudo ./fm_transmitter -f 100.6 -
|
||||
```
|
||||
In case of performance drop down use ```plughw:1,0``` instead of ```hw:1,0```.
|
||||
|
||||
## Legal note
|
||||
Please keep in mind that transmitting on certain frequencies without special permissions may be illegal in your country.
|
||||
|
||||
## New features
|
||||
* DMA peripheral support
|
||||
* works on any Raspberry Pi model
|
||||
* Allows custom frequency and bandwidth settings
|
||||
* works on every Raspberry Pi model
|
||||
* reads mono and stereo files
|
||||
* reads data from stdin
|
||||
|
||||
Included sample audio was created by [graham_makes](https://freesound.org/people/graham_makes/sounds/449409/) and published on [freesound.org](https://freesound.org/)
|
13
main.cpp
13
main.cpp
|
@ -54,13 +54,14 @@ void sigIntHandler(int sigNum)
|
|||
int main(int argc, char** argv)
|
||||
{
|
||||
double frequency = 100.0;
|
||||
double bandwidth = 100.0;
|
||||
unsigned short dmaChannel = 0;
|
||||
bool loop = false;
|
||||
string filename;
|
||||
bool showUsage = true;
|
||||
int opt, filesOffset;
|
||||
|
||||
while ((opt = getopt(argc, argv, "rf:d:v")) != -1) {
|
||||
while ((opt = getopt(argc, argv, "rf:d:b:v")) != -1) {
|
||||
switch (opt) {
|
||||
case 'r':
|
||||
loop = true;
|
||||
|
@ -71,6 +72,9 @@ int main(int argc, char** argv)
|
|||
case 'd':
|
||||
dmaChannel = ::atof(optarg);
|
||||
break;
|
||||
case 'b':
|
||||
bandwidth = ::atof(optarg);
|
||||
break;
|
||||
case 'v':
|
||||
cout << EXECUTABLE << " version: " << VERSION << endl;
|
||||
return 0;
|
||||
|
@ -81,11 +85,12 @@ int main(int argc, char** argv)
|
|||
showUsage = false;
|
||||
}
|
||||
if (showUsage) {
|
||||
cout << "Usage: " << EXECUTABLE << " [-f <frequency>] [-d <dma_channel>] [-r] <file>" << endl;
|
||||
cout << "Usage: " << EXECUTABLE << " [-f <frequency>] [-b <bandwidth>] [-d <dma_channel>] [-r] <file>" << endl;
|
||||
return 0;
|
||||
}
|
||||
|
||||
signal(SIGINT, sigIntHandler);
|
||||
signal(SIGTSTP, sigIntHandler);
|
||||
|
||||
try {
|
||||
transmitter = &Transmitter::getInstance();
|
||||
|
@ -96,11 +101,13 @@ int main(int argc, char** argv)
|
|||
}
|
||||
WaveReader reader(filename != "-" ? filename : string(), play);
|
||||
PCMWaveHeader header = reader.getHeader();
|
||||
cout << "Broadcasting on " << frequency << " MHz with "
|
||||
<< bandwidth << " kHz bandwidth" << endl;
|
||||
cout << "Playing: " << reader.getFilename() << ", "
|
||||
<< header.sampleRate << " Hz, "
|
||||
<< header.bitsPerSample << " bits, "
|
||||
<< ((header.channels > 0x01) ? "stereo" : "mono") << endl;
|
||||
transmitter->play(reader, frequency, dmaChannel, optind < argc);
|
||||
transmitter->play(reader, frequency, bandwidth, dmaChannel, optind < argc);
|
||||
} while (play && (optind < argc));
|
||||
} catch (exception &error) {
|
||||
cout << "Error: " << error.what() << endl;
|
||||
|
|
6
makefile
6
makefile
|
@ -1,11 +1,11 @@
|
|||
EXECUTABLE = fm_transmitter
|
||||
VERSION = 0.9.1
|
||||
|
||||
FLAGS = -Wall -fexceptions -pthread -O3 -fpermissive -fno-strict-aliasing
|
||||
FLAGS = -Wall -O3
|
||||
LIBS = -lm
|
||||
|
||||
all: main.o mailbox.o error_reporter.o sample.o preemp.o wave_reader.o transmitter.o
|
||||
g++ $(FLAGS) -L/opt/vc/lib -lm -lbcm_host -o $(EXECUTABLE) main.o mailbox.o sample.o preemp.o error_reporter.o wave_reader.o transmitter.o
|
||||
g++ -L/opt/vc/lib -lm -lpthread -lbcm_host -o $(EXECUTABLE) main.o mailbox.o sample.o preemp.o error_reporter.o wave_reader.o transmitter.o
|
||||
|
||||
mailbox.o: mailbox.c mailbox.h
|
||||
g++ $(FLAGS) -c mailbox.c
|
||||
|
@ -23,7 +23,7 @@ wave_reader.o: wave_reader.cpp wave_reader.h
|
|||
g++ $(FLAGS) -c wave_reader.cpp
|
||||
|
||||
transmitter.o: transmitter.cpp transmitter.h
|
||||
g++ $(FLAGS) -I/opt/vc/include -c transmitter.cpp
|
||||
g++ $(FLAGS) -fno-strict-aliasing -I/opt/vc/include -c transmitter.cpp
|
||||
|
||||
main.o: main.cpp
|
||||
g++ $(FLAGS) -DVERSION=\"$(VERSION)\" -DEXECUTABLE=\"$(EXECUTABLE)\" -c main.cpp
|
||||
|
|
|
@ -178,7 +178,7 @@ unsigned Transmitter::getAddress(volatile void *object)
|
|||
return (memSize) ? memAddress + ((unsigned)object - (unsigned)memAllocated) : 0x00000000;
|
||||
}
|
||||
|
||||
void Transmitter::play(WaveReader &reader, double frequency, unsigned char dmaChannel, bool preserveCarrierOnExit)
|
||||
void Transmitter::play(WaveReader &reader, double frequency, double bandwidth, unsigned char dmaChannel, bool preserveCarrierOnExit)
|
||||
{
|
||||
if (transmitting) {
|
||||
throw ErrorReporter("Cannot play, transmitter already in use");
|
||||
|
@ -195,7 +195,8 @@ void Transmitter::play(WaveReader &reader, double frequency, unsigned char dmaCh
|
|||
}
|
||||
bool eof = samples->size() < bufferSize;
|
||||
|
||||
unsigned clockDivisor = (unsigned)((500 << 12) / frequency + 0.5);
|
||||
unsigned clockDivisor = (unsigned)round((500 << 12) / frequency);
|
||||
unsigned divisorRange = clockDivisor - (unsigned)round((500 << 12) / (frequency + 0.0005 * bandwidth));
|
||||
bool isError = false;
|
||||
string errorMessage;
|
||||
|
||||
|
@ -205,7 +206,7 @@ void Transmitter::play(WaveReader &reader, double frequency, unsigned char dmaCh
|
|||
throw ErrorReporter("DMA channel number out of range (0 - 15)");
|
||||
}
|
||||
|
||||
if (!allocateMemory(sizeof(unsigned) * ((bufferSize << 1) + 1) + sizeof(DMAControllBlock) * (bufferSize << 1))) {
|
||||
if (!allocateMemory(sizeof(unsigned) * ((2 * bufferSize) + 1) + sizeof(DMAControllBlock) * (2 * bufferSize))) {
|
||||
delete samples;
|
||||
throw ErrorReporter("Cannot allocate memory");
|
||||
}
|
||||
|
@ -245,14 +246,14 @@ void Transmitter::play(WaveReader &reader, double frequency, unsigned char dmaCh
|
|||
#endif
|
||||
|
||||
volatile DMAControllBlock *dmaCb = (DMAControllBlock *)memAllocated;
|
||||
volatile unsigned *clkDiv = (unsigned *)memAllocated + ((sizeof(DMAControllBlock) / sizeof(unsigned)) << 1) * bufferSize;
|
||||
volatile unsigned *pwmFifoData = (unsigned *)memAllocated + (((sizeof(DMAControllBlock) / sizeof(unsigned)) << 1) + 1) * bufferSize;
|
||||
volatile unsigned *clkDiv = (unsigned *)memAllocated + 2 * (sizeof(DMAControllBlock) / sizeof(unsigned)) * bufferSize;
|
||||
volatile unsigned *pwmFifoData = (unsigned *)memAllocated + 2 * ((sizeof(DMAControllBlock) / sizeof(unsigned)) + 1) * bufferSize;
|
||||
for (i = 0; i < bufferSize; i++) {
|
||||
value = (*samples)[i].getMonoValue();
|
||||
#ifndef NO_PREEMP
|
||||
value = preEmp.filter(value);
|
||||
#endif
|
||||
clkDiv[i] = (0x5A << 24) | (clockDivisor - (int)round(value * 16.0));
|
||||
clkDiv[i] = (0x5A << 24) | (clockDivisor - (int)round(value * divisorRange));
|
||||
dmaCb[cbIndex].transferInfo = (0x01 << 26) | (0x01 << 3);
|
||||
dmaCb[cbIndex].srcAddress = getAddress(&clkDiv[i]);
|
||||
dmaCb[cbIndex].dstAddress = PERIPHERALS_PHYS_BASE | (CLK0_BASE_OFFSET + 0x04);
|
||||
|
@ -279,7 +280,7 @@ void Transmitter::play(WaveReader &reader, double frequency, unsigned char dmaCh
|
|||
dma->cbAddress = getAddress(dmaCb);
|
||||
dma->ctlStatus = (0xFF << 16) | 0x01;
|
||||
|
||||
usleep(BUFFER_TIME >> 2);
|
||||
usleep(BUFFER_TIME / 4);
|
||||
|
||||
try {
|
||||
while (!eof && transmitting) {
|
||||
|
@ -294,10 +295,10 @@ void Transmitter::play(WaveReader &reader, double frequency, unsigned char dmaCh
|
|||
#ifndef NO_PREEMP
|
||||
value = preEmp.filter(value);
|
||||
#endif
|
||||
while (i == (((dma->cbAddress - getAddress(dmaCb)) / sizeof(DMAControllBlock)) >> 1)) {
|
||||
while (i == ((dma->cbAddress - getAddress(dmaCb)) / (2 *sizeof(DMAControllBlock)))) {
|
||||
usleep(1);
|
||||
}
|
||||
clkDiv[i] = (0x5A << 24) | (clockDivisor - (int)round(value * 16.0));
|
||||
clkDiv[i] = (0x5A << 24) | (clockDivisor - (int)round(value * divisorRange));
|
||||
cbIndex += 2;
|
||||
}
|
||||
delete samples;
|
||||
|
@ -308,11 +309,8 @@ void Transmitter::play(WaveReader &reader, double frequency, unsigned char dmaCh
|
|||
isError = true;
|
||||
}
|
||||
|
||||
if (eof || isError) {
|
||||
dmaCb[cbIndex].nextCbAddress = 0x00;
|
||||
} else {
|
||||
dmaCb[(bufferSize - 1) << 1].nextCbAddress = 0x00;
|
||||
}
|
||||
cbIndex -= 2;
|
||||
dmaCb[cbIndex].nextCbAddress = 0x00;
|
||||
while (dma->cbAddress != 0x00) {
|
||||
usleep(1);
|
||||
}
|
||||
|
@ -330,10 +328,11 @@ void Transmitter::play(WaveReader &reader, double frequency, unsigned char dmaCh
|
|||
unsigned sampleOffset = 0;
|
||||
vector<Sample> *buffer = samples;
|
||||
|
||||
void *transmitterParams[4] = {
|
||||
void *transmitterParams[5] = {
|
||||
(void *)&buffer,
|
||||
(void *)&sampleOffset,
|
||||
(void *)&clockDivisor,
|
||||
(void *)&divisorRange,
|
||||
(void *)&header.sampleRate
|
||||
};
|
||||
|
||||
|
@ -346,7 +345,7 @@ void Transmitter::play(WaveReader &reader, double frequency, unsigned char dmaCh
|
|||
throw ErrorReporter(oss.str());
|
||||
}
|
||||
|
||||
usleep(BUFFER_TIME >> 1);
|
||||
usleep(BUFFER_TIME / 2);
|
||||
|
||||
try {
|
||||
while (!eof && transmitting) {
|
||||
|
@ -361,7 +360,7 @@ void Transmitter::play(WaveReader &reader, double frequency, unsigned char dmaCh
|
|||
eof = samples->size() < bufferSize;
|
||||
buffer = samples;
|
||||
}
|
||||
usleep(BUFFER_TIME >> 1);
|
||||
usleep(BUFFER_TIME / 2);
|
||||
}
|
||||
}
|
||||
catch (ErrorReporter &error) {
|
||||
|
@ -389,7 +388,8 @@ void *Transmitter::transmit(void *params)
|
|||
vector<Sample> **buffer = (vector<Sample> **)((void **)params)[0];
|
||||
unsigned *sampleOffset = (unsigned *)((void **)params)[1];
|
||||
unsigned *clockDivisor = (unsigned *)((void **)params)[2];
|
||||
unsigned *sampleRate = (unsigned *)((void **)params)[3];
|
||||
unsigned *divisorRange = (unsigned *)((void **)params)[3];
|
||||
unsigned *sampleRate = (unsigned *)((void **)params)[4];
|
||||
|
||||
unsigned offset, length, prevOffset;
|
||||
vector<Sample> *samples = NULL;
|
||||
|
@ -442,7 +442,7 @@ void *Transmitter::transmit(void *params)
|
|||
#ifndef NO_PREEMP
|
||||
value = preEmp.filter(value);
|
||||
#endif
|
||||
clk0->div = (0x5A << 24) | (*clockDivisor - (int)round(value * 16.0));
|
||||
clk0->div = (0x5A << 24) | (*clockDivisor - (int)round(value * (*divisorRange)));
|
||||
while (offset == prevOffset) {
|
||||
usleep(1); // asm("nop");
|
||||
current = *(unsigned long long *)&timer->low;
|
||||
|
|
|
@ -43,7 +43,7 @@ class Transmitter
|
|||
public:
|
||||
virtual ~Transmitter();
|
||||
static Transmitter &getInstance();
|
||||
void play(WaveReader &reader, double frequency, unsigned char dmaChannel, bool preserveCarrierOnExit);
|
||||
void play(WaveReader &reader, double frequency, double bandwidth, unsigned char dmaChannel, bool preserveCarrierOnExit);
|
||||
void stop();
|
||||
private:
|
||||
Transmitter();
|
||||
|
|
Ładowanie…
Reference in New Issue