├── Relase Notes ├── README.md ├── TX-CPUTemp.sh └── nbfm.c /Relase Notes: -------------------------------------------------------------------------------- 1 | My most important changes to the old pifm code are: 2 | - ppM frequency correction, deviation, pre-emphasys, output power settings by command-line parameters 3 | - frequency synthesis using not only the fractional PLL but also the "DMA modulation", 4 | - switch output off when exiting, 5 | - sinewave tone for ctcss applications etc. 6 | - correct correlation between deviation, center frequency and sampling rate 7 | - other minor changes... 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # NBFM 2 | NBFM for transforming a Raspberry PI into a ham radio FM transmitter 3 | 4 | Raspberry PI as RF Signal Generator 5 | 6 | The Raspberry PI can be turned into a performant RF signal generator without modifications, just using the proper software. 7 | 8 | nbfm by IK1PLD is a small program useful to transmit audio information via a frequency modulated RF carrier. 9 | 10 | By setting the deviation parameter to zero, it performs like a continuous wave signal generator, in the range 123 kHz - 250 MHz. 11 | 12 | Be careful laws may restric radio frequency transmission in your Country. 13 | -------------------------------------------------------------------------------- /TX-CPUTemp.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | #creates a 10MB RamDisk 4 | if [ ! -e /mnt/ramdisk ]; then 5 | mkdir -p /mnt/ramdisk 6 | fi 7 | mount -t ramfs -o size=10m ramfs /mnt/ramdisk 8 | 9 | cp nbfm /mnt/ramdisk/nbfm 10 | cp TX-CPUTemp.sh /mnt/ramdisk/TX-CPUTemp.sh 11 | cp pkt2wave /mnt/ramdisk/pkt2wave 12 | 13 | cd /mnt/ramdisk 14 | 15 | rm -f CPUTemp.txt 16 | rm -f CPUTemp_pkt.txt 17 | 18 | #activates GPIO22 port (pin15) as pull-upped Input 19 | #you can choose any available ports you wish and use them as signal inputs or alarms 20 | gpio -g mode 22 up 21 | 22 | #activates GPIO17 port (pin11) as output to light on a led indicating RF output or to switch amplifiers, filters etc. 23 | gpio -g mode 17 out 24 | gpio -g write 17 0 25 | 26 | clear 27 | 28 | inp1=$1 29 | inp2=$2 30 | 31 | if [ -z $1 ]; then 32 | echo "" 33 | echo "NBFM Transmission of the CPU temp, load and time, v20140901 by IK1PLD" 34 | echo "" 35 | echo "Use: ./TX-CPUTemp.sh Freq [Interval [ppM [dev [p-emph [ToneL [ToneF [Pow]" 36 | echo "Freq: carrier frequency in MHz (example 145.3125)" 37 | echo "Interval: time interval, in sec, between transmissions (default 60)" 38 | echo "run ./nbfm for further information" 39 | exit 40 | fi 41 | 42 | if [ -z $1 ]; then 43 | #default not used. It is mandatory to input a frequency in the command line. 44 | inp1=145.3125 45 | fi 46 | 47 | if [ -z $2 ]; then 48 | #set here the default interval in seconds 49 | inp2=60 50 | fi 51 | 52 | if [ -z $3 ]; then 53 | #set here the ppM correction for your own device 54 | inp3=-21 55 | fi 56 | 57 | if [ -z $4 ]; then 58 | #set here the default deviation 59 | inp4=5 60 | fi 61 | 62 | if [ -z $5 ]; then 63 | #set here the default pre-emphasys in microseconds 64 | inp5=400 65 | fi 66 | 67 | if [ -z $6 ]; then 68 | #set here the default Tone Level in absolute (between 0 and 1) 69 | inp6=0 70 | fi 71 | 72 | if [ -z $7 ]; then 73 | #set here the default Tone Frequency in Hz 74 | inp7=110.9 75 | fi 76 | 77 | if [ -z $8 ]; then 78 | #set here the default output Power (0-7) 79 | inp8=7 80 | fi 81 | 82 | echo "" 83 | echo "CPU temperature and load is transmitted on $inp1 MHz every $inp2 seconds" 84 | echo "A logger file named /mnt/ramdisk/Logger.txt is also temporary saved onto the ramdisk" 85 | echo "" 86 | startN=1 87 | 88 | while [ $startN -le 1000 ]; 89 | do 90 | temp_gpu=$(/opt/vc/bin/vcgencmd measure_temp | tr -d [a-z,A-Z,"=","'","."][]) 91 | cpuUsageM=$(top -bn 1 | awk '{print $9}' | tail -n +8 | awk '{s+=$1} END {print s}') 92 | cpuTempString=$(cat /sys/class/thermal/thermal_zone0/temp) 93 | cpuTempInt=$(($cpuTempString/1000)) 94 | cpuTempDec=$(($cpuTempString/100)) 95 | cpuTempDec=$(($cpuTempDec % $cpuTempInt)) 96 | cpuTemp=$cpuTempInt$cpuTempDec 97 | let cpuTemp=(${cpuTemp}+${temp_gpu})/2 98 | TempCoreInt=${cpuTemp:0:2} 99 | TempCoreDec=${cpuTemp:2} 100 | Time=$(date +"%R") 101 | Seconds=$(date +"%S") 102 | Day=$(date +"%F") 103 | GPIO22=$(gpio -g read 22) 104 | echo "Core Temperature is ${TempCoreInt}.${TempCoreDec} degrees, load is ${cpuUsageM} %, . at ${Time}" >> CPUTemp.txt 105 | echo "IK1PLD>BEACON:Temperature=${TempCoreInt}.${TempCoreDec}°C, load is ${cpuUsageM}%, at ${Time}" >> CPUTemp_pkt.txt 106 | 107 | if [ $GPIO22 == 0 ]; then 108 | #chech the GPIO22 as input for signals or alarms, and modify the following text as you wish 109 | echo " . Input 22 active!!!" >> CPUTemp.txt 110 | echo ". Input 22 active!!!" >> CPUTemp_pkt.txt 111 | fi 112 | 113 | echo "${Day};${Time}:${Seconds};${TempCoreInt}.${TempCoreDec};${cpuUsageM};${GPIO22};" >> Logger.txt 114 | cat CPUTemp.txt 115 | 116 | #the following line is to generate the voice audio file 117 | text2wave CPUTemp.txt -F 22500 -o CPUTemp.wav 118 | 119 | #the following line is to generate the packet radio audio file 120 | ./pkt2wave CPUTemp_pkt.txt -n 1 -r 22500 -o CPUTemp_pkt.wav 121 | 122 | gpio -g write 17 1 123 | 124 | #the following two lines are for testing. Please comment them if you want to use ./nbfm FM transmitter 125 | aplay CPUTemp.wav 126 | aplay CPUTemp_pkt.wav 127 | 128 | #the following line is to transmit voice messages. Digit ./nbfm for help 129 | #./nbfm CPUTemp.wav $inp1 22500 $inp3 $inp4 $inp5 $inp6 $inp7 $inp8 130 | 131 | #the following line is to transmit packet radio. 132 | #./nbfm CPUTemp-pkt.wav $inp1 44100 $inp3 $inp4 $inp5 $inp6 $inp7 $inp8 133 | 134 | gpio -g write 17 0 135 | rm -f CPUTemp.txt 136 | rm -f CPUTemp_pkt.txt 137 | let startN=$startN+1 138 | sleep $inp2 139 | done 140 | 141 | echo "" 142 | echo "End of transmissions." 143 | exit 144 | -------------------------------------------------------------------------------- /nbfm.c: -------------------------------------------------------------------------------- 1 | // verificare fattibilità di compensazione in temperatura 2 | // capire bene deviazione in base a sampling che sembra 5@11kHz, 8@22, 10@44, 12@88 ossia 75@11, 150@88 in FM larga 3 | 4 | 5 | // To compile: 6 | // gcc -o3 -lm -std=c99 -o nbfm nbfm.c 7 | // Derived from pifm, the great project by pihackfm 8 | // Thanks to Mauro, IK1WVQ, for support 9 | 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | 25 | #define PAGE_SIZE (4*1024) 26 | #define BLOCK_SIZE (4*1024) 27 | #define PI2 6.2831853071 28 | 29 | #define KNRM "\x1B[0m" //normal 30 | //#define KRED "\x1B[31m" //red 31 | //#define KGRN "\x1B[32m" //green 32 | //#define KYEL "\x1B[33m" //yellow 33 | //#define KBLU "\x1B[34m" //blue 34 | //#define KMAG "\x1B[35m" //magenta 35 | #define KCYN "\x1B[36m" //cyan 36 | //#define KWHT "\x1B[37m" //white 37 | //#define RESET "\033[0m" //reset 38 | #define BOLDYELLOW "\033[1m\033[33m" 39 | //#define ESC 27 //chr 27 escape 40 | //#define BoldOn "%c[1m",27 //Bold activation 41 | //#define BoldOf "%c[0m",27 //Bold deactivation 42 | 43 | int mem_fd; 44 | int PreEmph; 45 | float NominalFreq; 46 | float CorrFreq; 47 | float Deviation; 48 | float Sampling; 49 | char *gpio_mem, *gpio_map; 50 | char *spi0_mem, *spi0_map; 51 | float ToneFreq; //frequency in Hz of the tone for CTCSS or testing 52 | float ToneLev; //level of the tone. Example 0.001 for 1/1000 of the full scale peak2peak 53 | float ToneSampling; // ToneFreq/samplerate ratio 54 | char Power='7'; //Output power factor 0-7 55 | //Direct output from MY UNIT (at max level=7): 56 | // 1842 kHz >> +13.9dBm 160m 57 | // 3512 kHz >> +13.8dBm 80m 58 | // 7012 kHz >> +13.6dBm 40m 59 | // 10112 kHz >> +13.6dBm 30m 60 | // 14012 kHz >> +13.5dBm 20m 61 | // 18080 kHz >> +13.7dBm 17m 62 | // 21012 kHz >> +13.7dBm 15m 63 | // 24902 kHz >> +13.5dBm 12m 64 | // 28012 kHz >> +13.5dBm 10m 65 | 66 | // On 10m (28012 kHz) the 8 levels are about: 67 | // 7 = +13.5dBm 68 | // 6 = +13.2dBm 69 | // 5 = +12.8dBm 70 | // 4 = +12.2dBm 71 | // 3 = +11.4dBm 72 | // 2 = +10.2dBm 73 | // 1 = + 8.0dBm 74 | // 0 = + 3.2dBm 75 | 76 | float FreqDivider; 77 | float FractFreq; 78 | float DMAShift; 79 | float DMAFreqCorr; 80 | int IntFreqDivider; 81 | int FractFreqDivider; 82 | 83 | 84 | // I/O access 85 | volatile unsigned *gpio; 86 | volatile unsigned *allof7e; 87 | 88 | // GPIO setup macros. Always use INP_GPIO(x) before using OUT_GPIO(x) or SET_GPIO_ALT(x,y) 89 | #define INP_GPIO(g) *(gpio+((g)/10)) &= ~(7<<(((g)%10)*3)) 90 | #define OUT_GPIO(g) *(gpio+((g)/10)) |= (1<<(((g)%10)*3)) 91 | #define SET_GPIO_ALT(g,a) *(gpio+(((g)/10))) |= (((a)<=3?(a)+4:(a)==4?3:2)<<(((g)%10)*3)) 92 | 93 | #define GPIO_SET *(gpio+7) // sets bits which are 1 ignores bits which are 0 94 | #define GPIO_CLR *(gpio+10) // clears bits which are 1 ignores bits which are 0 95 | #define GPIO_GET *(gpio+13) // sets bits which are 1 ignores bits which are 0 96 | 97 | #define ACCESS(base) *(volatile int*)((int)allof7e+base-0x7e000000) 98 | #define SETBIT(base, bit) ACCESS(base) |= 1<0) ToneData = ToneLev * sin (ToneLoop*PI2); 254 | DataSample= (1-ToneLev) * (float)(data)/32767; 255 | //fprintf(stderr,"Data=%i Loop=%.1f",ToneData,ToneLoop); 256 | //float fmconstant = samplerate * PreEmph / 1000000; // moved outside this loop. For pre-emphisis filter. 50us time constant WBFM, 500us NBFM 257 | //int clocksPerSample = 22500.0/samplerate*1400.0; // moved outside this while loop 258 | 259 | datanew = DataSample+ToneData; 260 | 261 | float sample = datanew + (dataold-datanew) / (1-fmconstant); // fir of 1 + s tau 262 | float dval = (sample*Deviation) + DMAFreqCorr; // actual transmitted sample. 15 is bandwidth (about 75 kHz), 1 for NBFM at 145MHz 263 | 264 | int intval = (int)(round(dval)); // integer component 265 | float frac = (dval - (float)intval)/2 + 0.5; 266 | unsigned int fracval = frac*clocksPerSample; 267 | 268 | bufPtr++; 269 | while( ACCESS(DMABASE + 0x04 /* CurBlock*/) == (int)(instrs[bufPtr].p)) usleep(1000); 270 | ((struct CB*)(instrs[bufPtr].v))->SOURCE_AD = (int)constPage.p + 2048 + intval*4 - 4 ; 271 | 272 | bufPtr++; 273 | while( ACCESS(DMABASE + 0x04 /* CurBlock*/) == (int)(instrs[bufPtr].p)) usleep(1000); 274 | ((struct CB*)(instrs[bufPtr].v))->TXFR_LEN = clocksPerSample-fracval; 275 | 276 | bufPtr++; 277 | while( ACCESS(DMABASE + 0x04 /* CurBlock*/) == (int)(instrs[bufPtr].p)) usleep(1000); 278 | ((struct CB*)(instrs[bufPtr].v))->SOURCE_AD = (int)constPage.p + 2048 + intval*4+4; 279 | 280 | bufPtr=(bufPtr+1) % (1024); 281 | while( ACCESS(DMABASE + 0x04 /* CurBlock*/) == (int)(instrs[bufPtr].p)) usleep(1000); 282 | ((struct CB*)(instrs[bufPtr].v))->TXFR_LEN = fracval; 283 | 284 | dataold = datanew; 285 | if (ToneLoop<1) {ToneLoop=ToneLoop+ToneSampling;} else {ToneLoop=ToneSampling;} 286 | } 287 | close(fp); 288 | } 289 | 290 | void unSetupDMA(){ 291 | printf("exiting\n"); 292 | struct DMAregs* DMA0 = (struct DMAregs*)&(ACCESS(DMABASE)); 293 | DMA0->CS =1<<31; // reset dma controller 294 | 295 | } 296 | 297 | void handSig() { 298 | exit(0); 299 | } 300 | void setupDMA( float centerFreq ){ 301 | 302 | atexit(unSetupDMA); 303 | signal (SIGINT, handSig); 304 | signal (SIGTERM, handSig); 305 | signal (SIGHUP, handSig); 306 | signal (SIGQUIT, handSig); 307 | 308 | // allocate a few pages of ram 309 | getRealMemPage(&constPage.v, &constPage.p); 310 | 311 | int centerFreqDivider = (int)((500.0 / centerFreq) * (float)(1<<12) + 0.5); 312 | //fprintf(stderr,"\ncentreFreDiv=%i\n",centerFreqDivider); //for debug 313 | 314 | // make data page contents - it's essientially 1024 different commands for the 315 | // DMA controller to send to the clock module at the correct time. 316 | for (int i=0; i<1024; i++) 317 | ((int*)(constPage.v))[i] = (0x5a << 24) + centerFreqDivider - 512 + i; 318 | 319 | 320 | int instrCnt = 0; 321 | 322 | while (instrCnt<1024) { 323 | getRealMemPage(&instrPage.v, &instrPage.p); 324 | 325 | // make copy instructions 326 | struct CB* instr0= (struct CB*)instrPage.v; 327 | 328 | for (int i=0; i<4096/sizeof(struct CB); i++) { 329 | instrs[instrCnt].v = (void*)((int)instrPage.v + sizeof(struct CB)*i); 330 | instrs[instrCnt].p = (void*)((int)instrPage.p + sizeof(struct CB)*i); 331 | instr0->SOURCE_AD = (unsigned int)constPage.p+2048; 332 | instr0->DEST_AD = PWMBASE+0x18 /* FIF1 */; 333 | instr0->TXFR_LEN = 4; 334 | instr0->STRIDE = 0; 335 | //instr0->NEXTCONBK = (int)instrPage.p + sizeof(struct CB)*(i+1); 336 | instr0->TI = (1/* DREQ */<<6) | (5 /* PWM */<<16) | (1<<26/* no wide*/) ; 337 | instr0->RES1 = 0; 338 | instr0->RES2 = 0; 339 | 340 | if (i%2) { 341 | instr0->DEST_AD = CM_GP0DIV; 342 | instr0->STRIDE = 4; 343 | instr0->TI = (1<<26/* no wide*/) ; 344 | } 345 | 346 | if (instrCnt!=0) ((struct CB*)(instrs[instrCnt-1].v))->NEXTCONBK = (int)instrs[instrCnt].p; 347 | instr0++; 348 | instrCnt++; 349 | } 350 | } 351 | ((struct CB*)(instrs[1023].v))->NEXTCONBK = (int)instrs[0].p; 352 | 353 | // set up a clock for the PWM 354 | ACCESS(CLKBASE + 40*4 /*PWMCLK_CNTL*/) = 0x5A000026; //dec 1509949478 355 | usleep(1000); 356 | ACCESS(CLKBASE + 41*4 /*PWMCLK_DIV*/) = 0x5A002800; //dec 1509959680 357 | ACCESS(CLKBASE + 40*4 /*PWMCLK_CNTL*/) = 0x5A000016; //dec 1509949462 358 | usleep(1000); 359 | 360 | // set up pwm 361 | ACCESS(PWMBASE + 0x0 /* CTRL*/) = 0; 362 | usleep(1000); 363 | ACCESS(PWMBASE + 0x4 /* status*/) = -1; // clear errors 364 | usleep(1000); 365 | ACCESS(PWMBASE + 0x0 /* CTRL*/) = -1; //(1<<13 /* Use fifo */) | (1<<10 /* repeat */) | (1<<9 /* serializer */) | (1<<8 /* enable ch */) ; 366 | usleep(1000); 367 | ACCESS(PWMBASE + 0x8 /* DMAC*/) = (1<<31 /* DMA enable */) | 0x0707; 368 | 369 | //activate dma 370 | struct DMAregs* DMA0 = (struct DMAregs*)&(ACCESS(DMABASE)); 371 | DMA0->CS =1<<31; // reset 372 | DMA0->CONBLK_AD=0; 373 | DMA0->TI=0; 374 | DMA0->CONBLK_AD = (unsigned int)(instrPage.p); 375 | DMA0->CS =(1<<0)|(255 <<16); // enable bit = 0, clear end flag = 1, prio=19-16 376 | } 377 | 378 | 379 | void STOP_rf_output(int source) 380 | { 381 | /* open /dev/mem */ 382 | if ((mem_fd = open("/dev/mem", O_RDWR|O_SYNC) ) < 0) 383 | { 384 | printf("can't open /dev/mem \n"); 385 | exit (-1); 386 | } 387 | 388 | allof7e = (unsigned *)mmap( NULL, 0x01000000, /*len */ PROT_READ|PROT_WRITE, MAP_SHARED, mem_fd, 0x20000000 /* base */ ); 389 | 390 | if ((int)allof7e==-1) exit(-1); 391 | 392 | SETBIT(GPFSEL0 , 14); 393 | CLRBIT(GPFSEL0 , 13); 394 | CLRBIT(GPFSEL0 , 12); 395 | 396 | struct GPCTL setupword = {source/*SRC*/, 0, 0, 0, 0, 1,0x5a}; 397 | ACCESS(CM_GP0CTL) = *((int*)&setupword); 398 | } 399 | 400 | 401 | void OffhandSig() { 402 | setupDMA(500); 403 | exit(0); 404 | } 405 | 406 | 407 | int main(int argc, char **argv) 408 | { 409 | clearScreen(); 410 | int clock_source = 6; //0=GND 411 | float ppM=0.0; //parts per Million frequency correction factor 412 | FILE *temperatureFile; 413 | double Temperature; 414 | //char TXLine[82]; 415 | 416 | temperatureFile = fopen ("/sys/class/thermal/thermal_zone0/temp", "r"); 417 | if (temperatureFile == NULL) Temperature=100.0; 418 | fscanf (temperatureFile, "%lf", &Temperature); 419 | fclose (temperatureFile); 420 | Temperature /= 1000; 421 | //printf ("The temperature is %6.3f C.\n", Temperature); //for debug 422 | 423 | fprintf(stderr, "┌────────────────────────────────────────────────────────────────────────────┐\n"); 424 | fprintf(stderr, "│" BOLDYELLOW "NBFM transmitter v20140807 by IK1PLD " KNRM "│\n"); 425 | fprintf(stderr, "├────────────────────────────────────────────────────────────────────────────┤\n"); 426 | 427 | if (argc>1) { 428 | PreEmph=argc>6?atoi(argv[6]):400; //this is the Pre-Emphasys (default 400us); 429 | NominalFreq=argc>2?atof(argv[2]):29.6000; //this is still the uncorrected nominal frequency; 430 | Sampling=argc>3?atof(argv[3]):11025; // this is the sample rate of the audio file 431 | ppM=argc>4?atof(argv[4]):0.0; 432 | CorrFreq=NominalFreq+(NominalFreq*ppM/1000000); //now ppM correction applied 433 | Deviation=argc>5?atof(argv[5]):5; //this is the FM deviation (default 5kHz for ham NBFM) 434 | Deviation=Deviation+(Deviation/1.6)*log(Sampling/11025.0); //this is the corrected deviation (depending on sample rate) 435 | Deviation=(2368*Deviation)/(CorrFreq*CorrFreq); //2368 is the value corresponding to NBFM 5kHz deviation, 436 | // 16kHz channel BW; (Deviation*log(Sampling/11025.0)/1.6) parameter related to sample rate 437 | ToneLev=argc>7?atof(argv[7]):0.000;//this is the level for the audio tone to be added to the modulation 438 | ToneFreq=argc>8?atof(argv[8]):110.9;//this is the frequency for the audio tone to be added to the modulation 439 | Power=argc>9?*argv[9]:'7';//this is the output power 440 | if((Power<'0') || (Power>'7')) Power='7'; 441 | 442 | setup_fm(); 443 | 444 | FreqDivider = 500.0 / CorrFreq; //frequency divisor, decimal 445 | IntFreqDivider = (int)FreqDivider; // integer part of the frequency divisor 446 | FractFreqDivider =(int)(((FreqDivider - IntFreqDivider) * 4096.0) + 0.5); //fractional part of the frequency divisor 447 | FractFreq = 500.0 / (IntFreqDivider + (FractFreqDivider / 4096.0)); //fractional frequency without DMA shifting 448 | DMAShift = 1000.0 * (FractFreq - CorrFreq); // DMA frequency shift 449 | DMAFreqCorr = (2300.0 * DMAShift) / (CorrFreq * CorrFreq); //DMA Frequency shift factor 450 | 451 | //int centerFreqDivider = (int)((500.0 / CorrFreq) * (float)(1<<12) + 0.5);//for debug 452 | //fprintf(stderr,"\ncenterFreDivMAIN=%i\n",centerFreqDivider);//for debug 453 | //fprintf(stderr,"\ncenterFrequencyDivFrequency=%8.4f\n",4096.0*500.0/centerFreqDivider);//for debug 454 | 455 | setupDMA(CorrFreq); 456 | 457 | //strcpy(greeting, "Hello World"); 458 | fprintf(stderr, "│Transmitting %s in NBFM on %.4f MHz with %.1f ppM correction\n",argv[1], NominalFreq ,ppM); 459 | //fprintf(stderr, "\nCalcolo stringa = %i\n\n", (int)20-(FreqCorr>=1?strlen(argv[1]):0)-(int)(log10(FreqCorr))-(ppM>0?(int)(log10(ppM)):0)); 460 | //for (int i=0; i<20-strlen(argv[1])-(int)(log10(FreqCorr))-(int)(log10(ppM)); i++) fprintf(stderr, " "); 461 | fprintf(stderr, "│Fractional divider frequency=%8.4f MHz DMA Shift=%7.4f MHz │\n", FractFreq,DMAShift/1000.0); 462 | fprintf(stderr, "│Deviation factor=%10.1f Power factor=%c │\n",Deviation,Power); 463 | if (ToneLev>0) fprintf(stderr, "│Adding Tone frequency %5.1f Hz, level %5.3f │\n",ToneFreq, ToneLev); 464 | fprintf(stderr, "└────────────────────────────────────────────────────────────────────────────┘\n"); 465 | 466 | ToneSampling=1.004*ToneFreq/Sampling;//1.004 factor for subtones frequency centering with 1388.889c/s 467 | //fprintf(stderr, "ToneSampling %.4f\n",ToneSampling); 468 | signal (SIGINT, OffhandSig); 469 | signal (SIGTERM, OffhandSig); 470 | signal (SIGHUP, OffhandSig); 471 | signal (SIGQUIT, OffhandSig); 472 | playWav(argv[1], Sampling); 473 | //setupDMA(500); 474 | STOP_rf_output(clock_source); 475 | //fflush(stdout); 476 | // fprintf(stderr, "\n"); 477 | } else { 478 | fprintf(stderr, "│Usage: │\n"); 479 | fprintf(stderr, "│" KCYN "%s afile.wav [freq [sampling [ppM [dev [p-emph [ToneL [ToneF] [Power] " KNRM "│\n",argv[0]); //0 for program name 480 | fprintf(stderr, "│ │\n"); 481 | fprintf(stderr, "│Where: │\n"); 482 | fprintf(stderr, "│" KCYN "afile" KNRM " is audio 16 bit Mono. Set wavfile to '-' to use stdin. │\n"); //1 483 | fprintf(stderr, "│" KCYN "freq" KNRM " is carrier frequency in Mhz (default 29.6) │\n"); //2 484 | fprintf(stderr, "│" KCYN "sampling" KNRM " is sample rate of wav file in Hz (default 11025) │\n"); //3 485 | fprintf(stderr, "│" KCYN "ppM" KNRM " is the frequency correction in parts per Million (default 0) │\n"); //4 486 | fprintf(stderr, "│" KCYN "Deviation" KNRM " in kHz to set FM index (default 5) │\n"); //5 487 | fprintf(stderr, "│" KCYN "pre-emphasys" KNRM " in us (default 400) │\n"); //6 488 | fprintf(stderr, "│" KCYN "ToneL" KNRM " is the tone level (default 0.0) │\n"); //7 489 | fprintf(stderr, "│" KCYN "ToneF" KNRM " is the tone frequency in Hz for CTCSS or tests (default: 110.9) │\n"); //8 490 | fprintf(stderr, "│" KCYN "Power" KNRM " is the output power factor, between 0 and 7 (default: 7) │\n"); //9 491 | fprintf(stderr, "│ │\n"); 492 | fprintf(stderr, "│Set deviation to 0 to transmit silence (or for frequency calibration) │\n"); 493 | fprintf(stderr, "└────────────────────────────────────────────────────────────────────────────┘\n"); 494 | } 495 | 496 | return 0; 497 | 498 | } // main 499 | --------------------------------------------------------------------------------