├── pub ├── .gitkeep └── logbook.txt ├── sdr-40m.jpg ├── gpio_soft_reset.png ├── dist11 ├── pub2 │ ├── lu3dj.jpg │ ├── carrier.png │ ├── lu8fcj.jpg │ ├── pattern.png │ ├── smeter1.png │ ├── edgelower.png │ ├── edgeupper.png │ ├── scaleblack.png │ ├── edgelowerbb.png │ ├── edgeupperbb.png │ ├── websdr-1405020937.jar │ ├── crossdomain.xml │ ├── buttons.css │ ├── m.html │ ├── stat.html │ ├── websdr-javasound.js │ ├── index.html.es │ ├── websdr-javawaterfall.js │ ├── index.html │ ├── sysop.html │ ├── websdr-head.html │ ├── websdr-waterfall.js │ ├── websdr-controls.html │ ├── websdr-sound.js │ ├── mobile-controls.html │ └── websdr-base.js ├── websdr.cfg ├── websdr-40m.cfg └── websdr-80m.cfg ├── libpng-1.2.59.tar.xz ├── openssl-1.0.0k.tar.gz ├── rtl-sdr-driver-patched.zip ├── gpio_antenna_control_npn.png ├── .devcontainer ├── libssl1.0.0_1.0.2n-1ubuntu5.13_amd64.deb └── devcontainer.json ├── .gitignore ├── etc ├── systemd │ └── system │ │ ├── reset.service │ │ ├── rtl_tcp@.service │ │ ├── rtl_tcp_direct_sampling.service │ │ ├── websdr.service │ │ └── websdr@.service └── rc.local ├── crontab ├── opt ├── reset.py └── check_band.sh ├── Dockerfile ├── README.md └── README.es.md /pub/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /sdr-40m.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reynico/raspberry-websdr/HEAD/sdr-40m.jpg -------------------------------------------------------------------------------- /gpio_soft_reset.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reynico/raspberry-websdr/HEAD/gpio_soft_reset.png -------------------------------------------------------------------------------- /dist11/pub2/lu3dj.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reynico/raspberry-websdr/HEAD/dist11/pub2/lu3dj.jpg -------------------------------------------------------------------------------- /libpng-1.2.59.tar.xz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reynico/raspberry-websdr/HEAD/libpng-1.2.59.tar.xz -------------------------------------------------------------------------------- /openssl-1.0.0k.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reynico/raspberry-websdr/HEAD/openssl-1.0.0k.tar.gz -------------------------------------------------------------------------------- /dist11/pub2/carrier.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reynico/raspberry-websdr/HEAD/dist11/pub2/carrier.png -------------------------------------------------------------------------------- /dist11/pub2/lu8fcj.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reynico/raspberry-websdr/HEAD/dist11/pub2/lu8fcj.jpg -------------------------------------------------------------------------------- /dist11/pub2/pattern.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reynico/raspberry-websdr/HEAD/dist11/pub2/pattern.png -------------------------------------------------------------------------------- /dist11/pub2/smeter1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reynico/raspberry-websdr/HEAD/dist11/pub2/smeter1.png -------------------------------------------------------------------------------- /dist11/pub2/edgelower.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reynico/raspberry-websdr/HEAD/dist11/pub2/edgelower.png -------------------------------------------------------------------------------- /dist11/pub2/edgeupper.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reynico/raspberry-websdr/HEAD/dist11/pub2/edgeupper.png -------------------------------------------------------------------------------- /dist11/pub2/scaleblack.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reynico/raspberry-websdr/HEAD/dist11/pub2/scaleblack.png -------------------------------------------------------------------------------- /rtl-sdr-driver-patched.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reynico/raspberry-websdr/HEAD/rtl-sdr-driver-patched.zip -------------------------------------------------------------------------------- /dist11/pub2/edgelowerbb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reynico/raspberry-websdr/HEAD/dist11/pub2/edgelowerbb.png -------------------------------------------------------------------------------- /dist11/pub2/edgeupperbb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reynico/raspberry-websdr/HEAD/dist11/pub2/edgeupperbb.png -------------------------------------------------------------------------------- /gpio_antenna_control_npn.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reynico/raspberry-websdr/HEAD/gpio_antenna_control_npn.png -------------------------------------------------------------------------------- /dist11/pub2/websdr-1405020937.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reynico/raspberry-websdr/HEAD/dist11/pub2/websdr-1405020937.jar -------------------------------------------------------------------------------- /.devcontainer/libssl1.0.0_1.0.2n-1ubuntu5.13_amd64.deb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reynico/raspberry-websdr/HEAD/.devcontainer/libssl1.0.0_1.0.2n-1ubuntu5.13_amd64.deb -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | libpng-1.2.59/ 3 | openssl-1.0.0k/ 4 | websdr 5 | websdr64 6 | websdr-rpi 7 | fftw-wisdom.dat 8 | log-cpuload.txt 9 | userslog.txt 10 | tmp.tmp 11 | tmp/ -------------------------------------------------------------------------------- /dist11/pub2/crossdomain.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /etc/systemd/system/reset.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Soft hardware reset by Python 3 | After=multi-user.target 4 | 5 | [Service] 6 | Type=simple 7 | ExecStart=/usr/bin/python /opt/reset.py 8 | Restart=on-abort 9 | 10 | [Install] 11 | WantedBy=multi-user.target 12 | -------------------------------------------------------------------------------- /crontab: -------------------------------------------------------------------------------- 1 | * 20,21,22,23,00 * * * /opt/check_band.sh 80 2 | * 00,01,02,03,04,05,06,07 * * * /opt/check_band.sh 80 3 | 59 07 * * * sleep 10 && systemctl stop websdr@80.service 4 | * 08,0910,11,12,13,14,15,16,17,18,19 * * * /opt/check_band.sh 40 5 | 59 19 * * * sleep 10 && systemctl stop websdr@40.service 6 | -------------------------------------------------------------------------------- /opt/reset.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import os 3 | import time 4 | import RPi.GPIO as GPIO 5 | GPIO.setmode(GPIO.BCM) 6 | GPIO.setup(2,GPIO.IN) 7 | 8 | while True: 9 | input = GPIO.input(2) 10 | if not input: 11 | print("RESET!!") 12 | os.system("reboot") 13 | time.sleep(1) 14 | -------------------------------------------------------------------------------- /etc/systemd/system/rtl_tcp@.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=RTL TCP server on %I frequency 3 | After=multi-user.target 4 | 5 | [Service] 6 | Type=simple 7 | ExecStart=/usr/bin/rtl_tcp -s 1024000 -g 10 -d%I -p999%I 8 | ExecStop=/bin/kill -9 $MAINPID 9 | Restart=always 10 | 11 | [Install] 12 | WantedBy=multi-user.target 13 | -------------------------------------------------------------------------------- /pub/logbook.txt: -------------------------------------------------------------------------------- 1 | WebSDR logbook - note that entries can be made by anyone so correctness is not guaranteed 2 | -------------------------------------------------------------------------------------------------- 3 | date UTC freq call comments dxcc country heard by 4 | -------------------------------------------------------------------------------------------------- 5 | -------------------------------------------------------------------------------- /etc/systemd/system/rtl_tcp_direct_sampling.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=RTL TCP server on %I frequency 3 | After=multi-user.target 4 | 5 | [Service] 6 | Type=simple 7 | WorkingDirectory=/home/pi/pkg-rtl-sdr/build/src 8 | ExecStart=/home/pi/pkg-rtl-sdr/build/src/rtl_tcp -D2 -s 1024000 -g 10 -d%I -p999%I 9 | ExecStop=/bin/kill -9 $MAINPID 10 | Restart=always 11 | 12 | [Install] 13 | WantedBy=multi-user.target 14 | -------------------------------------------------------------------------------- /etc/systemd/system/websdr.service: -------------------------------------------------------------------------------- 1 | Description=WebSDR server 2 | After=network-online.target 3 | 4 | [Service] 5 | Type=simple 6 | WorkingDirectory=/home/pi/dist11/ 7 | ExecStartPre=/bin/sleep 10 8 | ExecStartPost=/bin/bash -c 'sleep 5 && sudo systemctl --no-block restart rtl_tcp@0.service' 9 | ExecStart=/home/pi/dist11/websdr-rpi /home/pi/dist11/websdr.cfg 10 | ExecStop=/bin/kill -9 $MAINPID 11 | Restart=always 12 | RestartSec=4 13 | 14 | [Install] 15 | WantedBy=multi-user.target 16 | -------------------------------------------------------------------------------- /etc/systemd/system/websdr@.service: -------------------------------------------------------------------------------- 1 | Description=WebSDR server on %Im band 2 | After=network-online.target 3 | 4 | [Service] 5 | Type=simple 6 | WorkingDirectory=/home/pi/dist11/ 7 | ExecStartPre=/bin/sleep 10 8 | ExecStartPost=/bin/bash -c 'sleep 5 && sudo systemctl --no-block restart rtl_tcp@0.service' 9 | ExecStart=/home/pi/dist11/websdr-rpi /home/pi/dist11/websdr-%Im.cfg 10 | ExecStop=/bin/kill -9 $MAINPID 11 | Restart=always 12 | RestartSec=4 13 | 14 | [Install] 15 | WantedBy=multi-user.target 16 | -------------------------------------------------------------------------------- /dist11/pub2/buttons.css: -------------------------------------------------------------------------------- 1 | .custombutton { 2 | background-color: #4CAF50; /* Green */ 3 | border: none; 4 | color: white; 5 | padding: 15px 32px; 6 | text-align: center; 7 | text-decoration: none; 8 | display: inline-block; 9 | font-size: 16px; 10 | margin: 4px 2px; 11 | cursor: pointer; 12 | } 13 | 14 | .button2 {background-color: #008CBA;} /* Blue */ 15 | .button3 {background-color: #f44336;} /* Red */ 16 | .button4 {background-color: #e7e7e7; color: black;} /* Gray */ 17 | .button5 {background-color: #555555;} /* Black */ 18 | -------------------------------------------------------------------------------- /opt/check_band.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if [ $# -eq 0 ]; then 4 | echo "Usage: ./check_band.sh BAND. example: ./check_band.sh 40" 5 | fi 6 | 7 | BAND="$1" 8 | 9 | if pgrep -x "websdr-rpi" > /dev/null 10 | then 11 | echo "Running" 12 | else 13 | sudo systemctl start websdr@${BAND}.service 14 | sudo kill -9 $(pidof rtl_tcp) 15 | sleep 3 16 | sudo renice -10 $(pidof rtl_tcp) 17 | sudo renice -10 $(pidof websdr-rpi) 18 | if [ "$BAND" == "80" ]; then 19 | echo 1 > /sys/class/gpio/gpio3/value 20 | else 21 | echo 0 > /sys/class/gpio/gpio3/value 22 | fi 23 | fi 24 | -------------------------------------------------------------------------------- /dist11/pub2/m.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | WebSDR - Mobile version 6 | 7 | 8 | Esta es la versión movil de este WebSDR. 9 | Debería funcionar utilizando Safari en dispositivos Apple, y Firefox en dispositivos Android. 10 | La sintonización es diferente al WebSDR normal: aquí hay que arrastrar la cascada. 11 |
12 |
13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /etc/rc.local: -------------------------------------------------------------------------------- 1 | #!/bin/sh -e 2 | # 3 | # rc.local 4 | # 5 | # This script is executed at the end of each multiuser runlevel. 6 | # Make sure that the script will "exit 0" on success or any other 7 | # value on error. 8 | # 9 | # In order to enable or disable this script just change the execution 10 | # bits. 11 | # 12 | # By default this script does nothing. 13 | 14 | # Antenna switching 15 | echo "3" > /sys/class/gpio/export 16 | echo out > /sys/class/gpio/gpio3/direction 17 | chmod 0777 /sys/class/gpio/gpio3/value 18 | 19 | #Soft reboot by hardware 20 | echo "2" > /sys/class/gpio/export 21 | echo in > /sys/class/gpio/gpio2/direction 22 | -------------------------------------------------------------------------------- /dist11/pub2/stat.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Estadisticas 5 | 9 | 10 | 11 | 12 | 13 |
14 | Source 15 | 16 | 17 | -------------------------------------------------------------------------------- /.devcontainer/devcontainer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Raspberry WebSDR Development", 3 | "build": { 4 | "dockerfile": "../Dockerfile" 5 | }, 6 | "customizations": { 7 | "vscode": { 8 | "extensions": [ 9 | "ms-azuretools.vscode-docker" 10 | ], 11 | "settings": { 12 | "terminal.integrated.defaultProfile.linux": "bash" 13 | } 14 | } 15 | }, 16 | "forwardPorts": [8080], 17 | "remoteUser": "vscode", 18 | "postStartCommand": "nohup bash -c 'nc -l -k -p 9990 &' > /dev/null 2>&1", 19 | "features": { 20 | "ghcr.io/devcontainers/features/common-utils:2":{ 21 | "installZsh": false, 22 | "username": "vscode", 23 | "userUid": "1000", 24 | "userGid": "1000" 25 | }, 26 | "ghcr.io/devcontainers/features/git:1": {} 27 | } 28 | } 29 | 30 | -------------------------------------------------------------------------------- /dist11/pub2/websdr-javasound.js: -------------------------------------------------------------------------------- 1 | 2 | 3 | /** 4 | * @constructor 5 | */ 6 | function make_javasound() 7 | { 8 | document.getElementById('soundappletdiv').innerHTML= 9 | '' + 10 | '<\/applet>' + 11 | '\n'; 12 | 13 | try { 14 | var sa=document["javasoundapplet"]; 15 | this['setparam'] = function(s) { sa.setparam(s); } 16 | this['smeter'] = function () { return sa.smeter; } 17 | this['getid'] = function () { return sa.getid(); } 18 | this['setvolume'] = function (v) { sa.setvolume(v); } 19 | this['destroy'] = function() { sa.destroy(); } 20 | this['javaversion'] = function() { sa.javaversion(); } 21 | this['app'] = sa; 22 | } catch (e) {}; 23 | } 24 | 25 | 26 | window["prep_javasound"] = function() 27 | { 28 | window['soundapplet']=new make_javasound(); 29 | } 30 | 31 | prep_javasound(); 32 | -------------------------------------------------------------------------------- /dist11/pub2/index.html.es: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | WebSDR on Villa Cañás, Santa Fe 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 |

Receptor Web SDR con Raspberry PI 3 Linux

16 |

Villa Cañás, Santa Fe, Argentina

17 |

Carlitos - LU8FCJ | Daniel - LU3DJ

18 | Mas información acerca del proyecto WebSDR se puede encontrar en http://www.websdr.org. 19 |
20 | El conocimiento humano pertenece al mundo. Toda la configuración de este WebSDR se encuentra en Github 21 |
22 |
23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM debian:bullseye 2 | 3 | ENV DEBIAN_FRONTEND=noninteractive 4 | 5 | RUN apt-get update && apt-get install -y \ 6 | g++ \ 7 | make \ 8 | libsigc++-2.0-dev \ 9 | libgsm1-dev \ 10 | libpopt-dev \ 11 | tcl8.6-dev \ 12 | libgcrypt-dev \ 13 | libspeex-dev \ 14 | libasound2-dev \ 15 | alsa-utils \ 16 | libsigc++-2.0-0v5 \ 17 | cmake \ 18 | groff \ 19 | rtl-sdr \ 20 | libusb-1.0-0-dev \ 21 | libusb-1.0-0 \ 22 | unzip \ 23 | git \ 24 | rsync \ 25 | python3 \ 26 | build-essential \ 27 | wget \ 28 | curl \ 29 | libtool \ 30 | sudo \ 31 | zlib1g-dev \ 32 | libfftw3-3 \ 33 | libasound2 \ 34 | netcat \ 35 | && apt-get clean \ 36 | && rm -rf /var/lib/apt/lists/* 37 | 38 | RUN useradd -m -s /bin/bash vscode && \ 39 | echo "vscode ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers 40 | 41 | WORKDIR /workspace 42 | 43 | COPY libpng-1.2.59.tar.xz . 44 | 45 | RUN tar xf libpng-1.2.59.tar.xz \ 46 | && cd libpng-1.2.59 \ 47 | && ./autogen.sh \ 48 | && ./configure --disable-dependency-tracking \ 49 | && make \ 50 | && make install 51 | 52 | COPY .devcontainer/libssl1.0.0_1.0.2n-1ubuntu5.13_amd64.deb . 53 | RUN dpkg -i libssl1.0.0_1.0.2n-1ubuntu5.13_amd64.deb 54 | 55 | USER vscode 56 | 57 | -------------------------------------------------------------------------------- /dist11/pub2/websdr-javawaterfall.js: -------------------------------------------------------------------------------- 1 | 2 | 3 | var wfjavaid=0; 4 | 5 | /** 6 | * @constructor 7 | */ 8 | function make_javawaterfall(w) 9 | // creates an HTML canvas element inside the HTML div referred to by w.div 10 | // then enhances that canvas object with lots of variables and functions to make it a waterfall "applet" 11 | // returns the canvas object for future reference 12 | { 13 | document.getElementById(w.div).innerHTML='
'+ 14 | '' + 15 | '' + 16 | '' + 17 | '' + 18 | '' + 19 | '<\/applet>'+ 20 | '<\/div>'; 21 | var wfa=document["waterfallapplet"+w.id]; 22 | 23 | w['setSize'] = function (w,h) 24 | { 25 | wfa.height=h; 26 | wfa.setSize(1024,h); 27 | wfa.setheight(h); 28 | } 29 | 30 | w['destroy'] = function() 31 | { 32 | wfa.destroy(); 33 | } 34 | 35 | w['setzoom']= function(newzoom, newstart) 36 | { 37 | wfa.setzoom(newzoom,newstart); 38 | } 39 | 40 | w['setslow']=function (slow) { 41 | wfa.setslow(slow); 42 | } 43 | 44 | w['setmode']=function (m) { wfa.setmode(m); } 45 | 46 | w['setband']=function(new_band, new_maxzoom, new_zoom, new_start) 47 | { 48 | wfa.setband(new_band, new_maxzoom, new_zoom, new_start); 49 | } 50 | 51 | return w; 52 | } 53 | 54 | 55 | 56 | window["prep_javawaterfalls"] = function() 57 | { 58 | for (i=0;i 2 | 3 | 4 | 5 | WebSDR on Tres Arroyos, Buenos Aires 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 |

Receptor Web SDR con Raspberry PI 3 Linux

16 |

Tres Arroyos, Buenos Aires, Argentina

17 |

Daniel - LU3DJ

18 |

19 | Mas información acerca del proyecto WebSDR se puede encontrar en http://www.websdr.org. 20 |
21 | El conocimiento humano pertenece al mundo. Toda la configuración de este WebSDR se encuentra en Github 22 |

23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 |
35 |
36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /dist11/pub2/sysop.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | WebSDR sysop page 4 | 5 | 6 | 7 | 8 | The below commands only work when accessed from the machine on which the WebSDR server itself is running, 9 | or from the local network if so configured (see documentation). 10 | 11 |
12 | 13 | To erase a line from the chatbox, copy the (initial part of the) offending line (incl. timestamp) here and submit: 14 |
15 | 16 | 17 |
18 |
19 | 20 | 21 |
22 |
23 | 24 | 25 |
26 | 27 |
28 | 29 | To display an important message on the waterfall, type it here and submit: 30 |
31 | 32 | 33 |
34 | 35 |
36 | 37 |
38 | To reload the configuration file without forcing all users browsers to reload the page: 39 | 40 | 41 |
New configuration filename (optional): 42 |
43 |

44 | 45 |


46 | 47 |
48 | To reload the configuration file with forcing all users browsers to reload the page 49 | (e.g., when a frequency range has changed): 50 | 51 | 52 |
New configuration filename (optional): 53 |
54 | 55 |
56 | 57 |
58 | To stop the WebSDR server: 59 | 60 | 61 |
62 | 63 |
64 | 65 | 66 | -------------------------------------------------------------------------------- /dist11/pub2/websdr-head.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /dist11/pub2/websdr-waterfall.js: -------------------------------------------------------------------------------- 1 | //WebSDR HTML5 client side - Copyright 2013-2014, pa3fwm@websdr.org - all rights reserved 2 | //Since the intended use of this code involves sending a copy to the client computer, I (PA3FWM) hereby allow making it available unmodified, via my original WebSDR server software, to original WebSDR clients. Other use, including distribution in part or entirety or as part of other software, or reverse engineering, is not allowed without my explicit prior permission. 3 | (function(){var t=!0,u=!1,v=0,x=0,B=1,H=0;if(!window.requestAnimationFrame)for(var K;;){if(K=window.mozRequestAnimationFrame){window.requestAnimationFrame=K;break}if(K=window.webkitRequestAnimationFrame){window.requestAnimationFrame=K;break}B=0;break} 4 | function V(){function I(c){var a=c.layerX||c.offsetX,b=c.wheelDelta||-c.detail;0b&&L(-1,a);return cancelEvent(c)}var b=waterfallapplet[i];function N(c){var g=a.width<c&&(c=0),g=(1024<g&&(c=g);B?(a.z=c,b.q=1):(g=a.c,a.c=c,a.e("GET /~~waterparam?start="+c),A(g,a.c,a.b,u),J())}function W(c){var a=getMouseXY(c);N(a.x);return cancelEvent(c)}function O(c){c.preventDefault(); 5 | a.k=t;a.j=10}function P(c){c.preventDefault();setTimeout(X,300)}function X(){a.k=u}function Q(c){c.preventDefault();var b=0;c=Math.floor(10*c.scale);c>a.j+1&&(b=-1);cc?w(-1,dragorigX):a.i&&300>(new Date).getTime()-a.i&&(a.B=setTimeout(Y,300))}function U(){if(b.q){b.q=0;var c=a.c;a.c=a.z;a.e("GET /~~waterparam?start="+a.z);A(c,a.c,a.b,u);J()}b.r&&(c=d.o,b.r=0,d.a.drawImage(d,0,1,d.width,d.height-1,0,0,d.width,d.height-1),d.a.putImageData(c,0,d.height-1),(a.g!=a.b||a.f!=a.c)&&A(a.f,a.c,a.g,t));requestAnimationFrame(U)}function J(){zoomchange(a.v, 7 | a.b,a.c)}function A(c,g,n,e){var f=b.width,h=0,k=d.height-h;if(e)if(x){if(h=q-1,k=1,0>h)return}else h=d.height-1,k=1;if(n==a.b)try{if(g>a.maxzoom-a.b;d.a.drawImage(d,0,h,f-l,k,l,h,f-l,k);d.a.fillStyle="#000000";d.a.fillRect(0,h,l,k);e||(j.a.drawImage(j,0,h,f-l,k,l,h,f-l,k),j.a.fillStyle="#000000",j.a.fillRect(0,h,l,k))}else if(g>c){var m=g-c>>a.maxzoom-a.b;d.a.drawImage(d,m,h,f-m,k,0,h,f-m,k);d.a.fillStyle="#000000";d.a.fillRect(f-m,h,m,k);e||(j.a.drawImage(j,m,h,f-m,k,0,h,f-m,k),j.a.fillStyle= 8 | "#000000",j.a.fillRect(f-m,h,m,k))}}catch(s){}else if(a.l=t,n>a.b)m=1<>a.maxzoom-n);try{d.a.drawImage(d,l,h,f/m,k,0,h,f,k),!e&&x&&j.a.drawImage(j,l,h,f/m,k,0,h,f,k)}catch(r){d.a.fillStyle="#000000", 9 | d.a.fillRect(0,h,f,k),!e&&x&&j.a.fillRect(0,h,f,k)}}}function w(c,b){if(0!=a.maxzoom){var d=a.b,e=a.c,f=a.c+(b<a.b&&(a.b=0);a.b>a.maxzoom&&(a.b=a.maxzoom);var h=a.width<f&&(f=0),h=(1024<h&&(f=h);a.c=f;A(e,a.c,d,u);a.e("GET /~~waterparam?zoom="+a.b+"&start="+a.c);J()}}function L(c,d){if(-1==c)w(1,d);else if(-2==c)w(-1,d);else{var e=this.b,j=this.c,f=c,h=b.width;0>f&&(f=0); 10 | f>this.maxzoom&&(f=this.maxzoom);H&&(0>d&&(d=0),h=(1024<h&&(d=h));this.e("GET /~~waterparam?zoom="+f+"&start="+d);this.b=f;this.c=d;A(j,a.c,e,u);J()}}b.width||(b.width=1024);b.height||(b.height=100);document.getElementById(b.div).innerHTML='
testtest
';b.h=document.getElementById("wfcdiv"+v);var s=0<=v?"on":"off";b.h.v=b.id;b.h.band=b.band;b.h.width=b.width;b.h.height=b.height;b.h.maxzoom=b.maxzoom;b=b.h;b.r=0;b.q=0;b.destroy=function(){try{b.d.close()}catch(c){}b.parentNode.removeChild(b)};var C=document.getElementById("wf1canvas"+v),D=document.getElementById("wf2canvas"+v),d=C,j=D;x||(j.height=0);v++;C.a=C.getContext("2d");D.a=D.getContext("2d");d.a.fillStyle= 12 | "#000000";d.a.fillRect(0,0,d.width,d.height);C.o=C.a.createImageData(1024,1);D.o=D.a.createImageData(1024,1);var a=b;-1e;e++)E[e]=0,F[e]=0,G[e]=2*e;for(;128>e;e++)E[e]=3*e-192,F[e]=0,G[e]=2*e;for(;192>e;e++)E[e]=e+64,F[e]=256*Math.sqrt((e-128)/64),G[e]=511-2*e;for(;256>e;e++)E[e]=255,F[e]=255,G[e]=512+2*e;b.b=0;b.c=0;-2g[2]&&(a.g=g[2]),g=g.subarray(8));if(2==g[1]){a.m=g[2]+(g[3]<<8);for(g=0;gg;g++){if(1==a.n){var n=c,p=y;for(e=0;e>8),r=Z[l],w=$[l]);f+=w;8<=f&&(f-=8,h++);if(1==r||-1==r)m=1;if(1r)m=2;0==r&&(m=0);s+=r<<4;r=p[k]+s;0>r&&(r=8);255c;c++)n=y[c],g.data[4*c]=E[n],g.data[4*c+1]=F[n],g.data[4*c+2]=G[n],g.data[4*c+3]=255;M++;300==M&&(M=0);x?(d.a.putImageData(g,0,q),d.style.top=d.height-q+"px",j.style.top=-q+"px",q++,q>=d.height&&(q=0,c=j,j=d,d=c)):B?b.r=1:(d.a.drawImage(d,0,1,d.width,d.height-1,0,0, 15 | d.width,d.height-1),d.a.putImageData(g,0,d.height-1));!B&&(a.g!=a.b||a.f!=a.c)&&A(a.f,a.c,a.g,t)}else{d.a.fillStyle="#000000";d.a.fillRect(0,0,d.width,d.height);d.a.fillStyle="#00ffff";if(a.l){for(c=0;1024>c;c++)a.s[c]=y[c];a.l=u}for(c=0;c 4 |
5 | 8 | 9 |
10 | 11 | 12 |
13 | 14 |
15 |
16 |
17 |
18 | It seems Java is not installed or disabled on your computer. You need to install and/or enable it for this website to work properly. 19 |
Since Java version 7u51, Java needs to be enabled separately for each website; see http://websdr.org/java.html for instructions. 20 |
21 |
22 | Your browser does not seem to support HTML5 WebAudio; please try a recent version of Firefox, Chrome, Opera or Safari; or switch to Java. 23 |
24 |
25 | 26 |
27 |
28 | Vista: 29 | 30 |
31 |
32 | Usar teclado: 33 | 34 |
35 | j k ← →: subir bajar frecuencia (+shift/ctrl/alt mas velocidad) 36 |
37 | u l c a f: USB, LSB, CW, AM, FM 38 |
39 | z Z: centrar/zoom cascada 40 |
41 | g: ingresar frecuencia 42 |
43 |
44 |
45 | 46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 | 54 |
55 |
56 |
57 |
58 |
59 |
60 | 61 | 62 |
63 |
64 |
65 |
66 |
67 |
68 | 69 |
70 |
71 |
72 | 73 |
74 |
75 |
76 | 77 |
78 | Frecuencia: 79 | 80 | 81 | 82 | 83 | 84 | kHz 85 | 86 | 87 |
88 | 89 | 90 | 91 | 92 |
93 | 94 | 95 | 96 | 97 |
98 |
99 | O ajustá la frecuencia clickeando/ajustando sobre la escala. 100 |
101 |
102 | Memorias: 103 |
104 |
105 |
106 |
107 | 108 | 109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 | [scale] 121 |
122 |
123 |
124 | ? dB; 125 | peak 126 | ? dB; 127 |
128 | mute 129 | squelch 130 | autonotch 131 |
132 | 133 | Volumen: 134 | 137 | 138 | 146 |
147 | Mostrar nivel de señal: 148 | 153 |
154 | 155 |
156 |
157 | 158 | 159 |
160 |
161 | Cascada: 162 |
163 | 164 | 165 | 169 | 170 | 171 | 175 | 176 | 179 | 202 | 205 |
166 | 167 | 168 |
172 | 173 | 174 |
177 | O usá la rueda del mouse en la cascada. 178 |
180 | Velocidad: 181 | 186 |
187 | Tamaño: 188 | 193 |
194 | Vista: 195 | 201 |
203 | 204 |
206 |
207 |
208 | 209 |
210 |
211 | Ancho de banda: 212 |
213 | ? kHz @ -6dB; 214 | ? kHz @ -60dB. 215 |
216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 |
232 | 233 | O ajustá la frecuencia clickeando/ajustando sobre la escala. 234 | 235 |
236 | 237 | 238 |
239 | Libro de guardia: 240 |
241 | Licencia de la estación que escuchaste: 242 |
243 | Comentarios: 244 | 245 |
246 | 247 | Nota: fecha, frecuencia, tu nombre/licencia e información DX es agregada automaticamente. 248 |
249 | Ver ultimas 20 entradas del libro de guardia, 250 | o el libro de guardia completo. 251 |
252 |
253 | 254 |
255 |
256 | 257 | 258 |
259 | Este WebSDR está siendo utilizado por ? usuario(s) de forma simultanea:      260 | vista compacta 261 |
262 |
263 |
264 |
265 |
266 |

Chatbox:
267 | 268 | Este chat está ideado para discutir sobre la operación de WebSDR. 269 |
270 | Los operadores de este sitio niegan cualquier responsabilidad por los textos escritos en este chatbox. 271 |
272 |

273 |
274 |
275 | 276 | 277 |
278 |
279 |
280 | Free counters! 281 | 282 |
283 |
284 | Estadisticas: 285 |
286 |
287 | 288 | 289 | 290 |
291 | 292 |
293 | 294 | 295 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # WebSDR node based on a Raspberry PI 2 | 3 | Leer en [Inglés](README.md), [Español](README.es.md). 4 | Read in [English](README.md), [Spanish](README.es.md). 5 | 6 | ![SDR receiver working on 40m band](https://github.com/reynico/raspberry-websdr/raw/master/sdr-40m.jpg) 7 | 8 | - [WebSDR node based on a Raspberry PI](#websdr-node-based-on-a-raspberry-pi) 9 | - [General hardware requirements](#general-hardware-requirements) 10 | - [Running on a Raspberry PI 4 or newer?](#running-on-a-raspberry-pi-4-or-newer) 11 | - [Required setup and software](#required-setup-and-software) 12 | - [Clone this repository](#clone-this-repository) 13 | - [Missing libraries](#missing-libraries) 14 | - [libpng12](#libpng12) 15 | - [libssl-1.0.0](#libssl-100) 16 | - [RTL SDR direct sampling (500KHz - 28.8MHz without upconverter!)](#rtl-sdr-direct-sampling-500khz---288mhz-without-upconverter) 17 | - [Install WebSDR](#install-websdr) 18 | - [Single band WebSDR](#single-band-websdr) 19 | - [Time-controlled, dual band WebSDR](#time-controlled-dual-band-websdr) 20 | - [Antenna controller](#antenna-controller) 21 | - [Enable WebSDR for dual band operation](#enable-websdr-for-dual-band-operation) 22 | - [Cron](#cron) 23 | - [Manual control](#manual-control) 24 | - [GPIO Setup](#gpio-setup) 25 | - [Software reset](#software-reset) 26 | - [Blacklist RTL modules](#blacklist-rtl-modules) 27 | - [SD Card wear](#sd-card-wear) 28 | 29 | This WebSDR setup covers a dual band receiver (80/40 meters bands) time-based switched. It uses a relay to switch between antennas who is managed by one GPiO pin on the Raspberry PI (using a driver transistor). 30 | 31 | Very special thanks to Pieter PA3FWM, Mark G4FPH and Jarek SQ9NFI for the helpful hand on configuring the progfreq setting. 32 | 33 | ## General hardware requirements 34 | 35 | - Raspberry PI 3 or greater. 36 | - [Raspberry Pi OS (Legacy) installed](https://downloads.raspberrypi.com/raspios_oldstable_lite_armhf/images/raspios_oldstable_lite_armhf-2024-03-12/2024-03-12-raspios-bullseye-armhf-lite.img.xz) and working. 37 | - Internet access setup and working 38 | - RTL-SDR USB Dongle 39 | 40 | ## Running on a Raspberry PI 4 or newer? 41 | 42 | Raspberry PI 4 or newer runs on a different (but similar) architecture than older hardware like the PI 3. Since `websdr-rpi` is not fully compatible with this newer arch (mostly due to outdated dependencies) you may face some of these errors: 43 | 44 | ``` 45 | websdr-rpi: cannot execute: required file not found 46 | ``` 47 | 48 | or 49 | 50 | ``` 51 | websdr-rpi: error while loading shared libraries: libfftw3f.so.3 52 | ``` 53 | 54 | Some of the steps listed below have specific instructions for setting up a newer Raspberry PI hardware and overcome these issues. 55 | 56 | ## Required setup and software 57 | 58 | ```bash 59 | sudo apt update && sudo apt upgrade 60 | sudo apt install -y \ 61 | g++ \ 62 | make \ 63 | libsigc++-2.0-dev \ 64 | libgsm1-dev \ 65 | libpopt-dev \ 66 | tcl8.6-dev \ 67 | libgcrypt-dev \ 68 | libspeex-dev \ 69 | libasound2-dev \ 70 | alsa-utils \ 71 | libsigc++-2.0-0v5 \ 72 | cmake \ 73 | groff \ 74 | rtl-sdr \ 75 | libusb-1.0-0-dev \ 76 | unzip \ 77 | git \ 78 | rsync 79 | ``` 80 | 81 | If you are running a Raspberry PI 4 or newer (arm64 architecture) install the `armhf` arch: 82 | 83 | ```bash 84 | sudo dpkg --add-architecture armhf 85 | sudo apt install -y \ 86 | libsigc++-2.0-dev:armhf \ 87 | libgsm1-dev:armhf \ 88 | libpopt-dev:armhf \ 89 | tcl8.6-dev:armhf \ 90 | libgcrypt-dev:armhf \ 91 | libspeex-dev:armhf \ 92 | libasound2-dev:armhf \ 93 | libsigc++-2.0-0v5:armhf \ 94 | libusb-1.0-0-dev:armhf \ 95 | libc6:armhf \ 96 | zlib1g-dev:armhf 97 | ``` 98 | 99 | ## Clone this repository 100 | 101 | ``` 102 | cd /home/pi/ 103 | git clone https://github.com/reynico/raspberry-websdr.git 104 | cd raspberry-websdr/ 105 | ``` 106 | 107 | ## Missing libraries 108 | 109 | Over the time, some required libraries were deprecated in favor of newer and incompatible versions. 110 | 111 | ### libpng12 112 | 113 | If you're running a Raspberry PI 4 or newer (arm64 architecture), compile `libpng` like this: 114 | 115 | ```bash 116 | sudo apt install gcc-arm-linux-gnueabihf g++-arm-linux-gnueabihf 117 | 118 | tar xf libpng-1.2.59.tar.xz 119 | cd libpng-1.2.59/ 120 | CC=arm-linux-gnueabihf-gcc \ 121 | CXX=arm-linux-gnueabihf-g++ \ 122 | ./configure \ 123 | --host=arm-linux-gnueabihf \ 124 | --prefix=/usr/local \ 125 | --libdir=/usr/local/lib/arm-linux-gnueabihf 126 | 127 | make 128 | sudo make install 129 | sudo ldconfig 130 | sudo ln -s /usr/local/lib/arm-linux-gnueabihf/libpng12.so.0 /lib/arm-linux-gnueabihf/libpng12.so.0 131 | cd.. 132 | 133 | ``` 134 | 135 | 136 | If you're running a Raspberry PI older than a PI4, compile it this way: 137 | 138 | ```bash 139 | tar xf libpng-1.2.59.tar.xz 140 | cd libpng-1.2.59/ 141 | ./configure 142 | make 143 | sudo make install 144 | cd .. 145 | ``` 146 | 147 | ### libssl-1.0.0 148 | 149 | If you're running a Raspberry PI 4 or newer (arm64 architecture), compile `libssl` like this: 150 | 151 | ```bash 152 | cd openssl-1.0.0k/ 153 | 154 | CC=arm-linux-gnueabihf-gcc ./Configure linux-armv4 shared \ 155 | --prefix=/usr/local/arm32 \ 156 | --openssldir=/usr/local/arm32/ssl \ 157 | -Wl,--version-script=openssl.ld \ 158 | -Wl,-Bsymbolic-functions 159 | 160 | make 161 | sudo make install_sw 162 | 163 | sudo ln -s /usr/local/arm32/lib/libcrypto.so.1.0.0 /usr/lib/arm-linux-gnueabihf/libcrypto.so.1.0.0 164 | sudo ln -s /usr/local/arm32/lib/libssl.so.1.0.0 /usr/lib/arm-linux-gnueabihf/libssl.so.1.0.0 165 | 166 | sudo ldconfig 167 | ``` 168 | 169 | 170 | If you're running a Raspberry PI older than a PI4, compile it this way: 171 | 172 | ```bash 173 | tar xf openssl-1.0.0k.tar.gz 174 | cd openssl-1.0.0k/ 175 | cat << EOF > openssl.ld 176 | OPENSSL_1.0.0 { 177 | global: 178 | *; 179 | }; 180 | EOF 181 | ./config shared --prefix=$HOME/libssl/openssl --openssldir=$HOME/libssl/ssl -Wl,--version-script=openssl.ld -Wl,-Bsymbolic-functions 182 | make 183 | sudo make install_sw 184 | sudo ldconfig 185 | ldd $HOME/libssl/openssl/bin/openssl 186 | sudo cp $HOME/libssl/openssl/lib/libcrypto.so /usr/local/lib 187 | sudo chmod 0755 /usr/local/lib/libcrypto.so 188 | sudo ldconfig 189 | cd .. 190 | ``` 191 | 192 | ## RTL SDR direct sampling (500KHz - 28.8MHz without upconverter!) 193 | 194 | If you are running a RTL-SDR.com V4 receiver, a special version of the `rtl-sdr` driver is needed in order to receive 500KHz-28.8MHz 195 | 196 | ```bash 197 | git clone https://github.com/rtlsdrblog/rtl-sdr-blog 198 | cd rtl-sdr-blog/ 199 | mkdir build 200 | cd build 201 | cmake ../ -DINSTALL_UDEV_RULES=ON 202 | make 203 | sudo make install 204 | sudo cp ../rtl-sdr.rules /etc/udev/rules.d/ 205 | sudo ldconfig 206 | ``` 207 | 208 | If you are running a different SDR device, but if it supports direct sampling (such as RTL-SDR.com V3 receiver), there's a way to receive 500khz-28.8mhz without an external upconverter hardware, easing the node build. 209 | 210 | ```bash 211 | unzip rtl-sdr-driver-patched.zip 212 | cd pkg-rtl-sdr/ 213 | mkdir -p build/ 214 | cd build/ 215 | cmake ../ -DINSTALL_UDEV_RULES=ON -DDETACH_KERNEL_DRIVER=ON 216 | make 217 | sudo make install 218 | sudo ldconfig 219 | cd ../../ 220 | cp -r pkg-rtl-sdr/ /home/pi/ 221 | cd .. 222 | ``` 223 | 224 | Don't forget to remove the `progfreq` line from the websdr configuration file(s). 225 | 226 | Use the following systemd unit for direct sampling: 227 | 228 | ```bash 229 | sudo cp etc/systemd/system/rtl_tcp_direct_sampling.service /etc/systemd/system/rtl_tcp@.service 230 | ``` 231 | 232 | ## Install WebSDR 233 | 234 | - This is a good time to reboot your Raspberry PI, do a `sudo reboot`. 235 | - Ask [Pieter](http://websdr.org/) to get a copy of WebSDR. 236 | - Copy the websdr-rpi binary and files to your home directory (/home/pi/) 237 | - Edit websdr.cfg (for single band use), or websdr-80m.cfg and websdr-40m.cfg (for dual band use) to fulfill your configuration 238 | - Create Systemd units to manage websdr and rtl_tcp 239 | 240 | ```bash 241 | sudo cp etc/systemd/system/websdr@.service /etc/systemd/system/websdr@.service 242 | ``` 243 | 244 | Copy this systemd unit only if you're NOT using the RTL SDR direct sampling method. RTL-SDR.com V4 receivers don't need the [Direct Sampling](#rtl-sdr-direct-sampling-500khz---288mhz-without-upconverter) mod to work, it just works out of the box. 245 | 246 | ```bash 247 | sudo cp etc/systemd/system/rtl_tcp@.service /etc/systemd/system/rtl_tcp@.service 248 | ``` 249 | 250 | ## Single band WebSDR 251 | 252 | The setup described in this section is the most usual: a single band SDR receiver (say, 40m band). 253 | 254 | - Create systemd units to manage `websdr` and `rtl_tcp`: 255 | 256 | ```bash 257 | sudo cp etc/systemd/system/websdr.service /etc/systemd/system/websdr.service 258 | ``` 259 | 260 | - Enable the `rtl_tcp` systemd unit: 261 | 262 | ```bash 263 | sudo systemctl enable rtl_tcp@0.service 264 | ``` 265 | 266 | - Enable the `websdr` unit: 267 | 268 | ```bash 269 | sudo systemctl enable websdr.service 270 | ``` 271 | 272 | ## Time-controlled, dual band WebSDR 273 | 274 | The setup described in this section is a more powerful one, where the Raspberry PI switches the WebSDR bands according to the time you define in `crontab`. 275 | 276 | ### Antenna controller 277 | 278 | This WebSDR setup uses one RTL-SDR dongle for two bands (40/80 meters), crontab takes control of which band is working. As wave length isn't the same on both two bands, I'm using a GPiO port to switch between them using a DPDT relay. GPIO3 is controlled by the check_band.sh cron script. 279 | ![Antenna controller schematic](https://github.com/reynico/raspberry-websdr/raw/master/gpio_antenna_control_npn.png) 280 | 281 | ### Enable WebSDR for dual band operation 282 | 283 | - Enable just the `rtl_tcp` systemd unit. Websdr is managed by crontab 284 | 285 | ```bash 286 | sudo systemctl enable rtl_tcp@0.service 287 | ``` 288 | 289 | ### Cron 290 | 291 | - I built a crontab configuration to switch between 40m and 80m bands time-based. Just import the crontab lines into your crontab. 292 | 293 | ### Manual control 294 | 295 | You can always control band changes manual way. Disable cron lines to avoid automatic setup changes. Then you can use 296 | 297 | ```bash 298 | sudo systemctl stop websdr@40.service 299 | sudo systemctl start websdr@40.service 300 | ``` 301 | 302 | Where 40 is the band you want to receive. You can use and setup almost any band you want, as long as you had setup your websdr-{{band}}m.cfg 303 | 304 | ### GPIO Setup 305 | 306 | - Copy the etc/rc.local file to your /etc/rc.local 307 | 308 | ```bash 309 | sudo cp etc/rc.local /etc/rc.local 310 | ``` 311 | 312 | - Check and match GPIO ports for relay control to switch antennas and a button to soft reset the Raspberry pi. 313 | 314 | ## Software reset 315 | 316 | - There is a Python script that handles Raspberry PI reboots from a hardware switch without killing power. 317 | - Check the /etc/rc.local file and match the desired GPIO port for this task. 318 | - Copy etc/systemd/system/reset.service to /etc/systemd/system/reset.service 319 | 320 | ```bash 321 | sudo cp opt/reset.py /opt/reset.py 322 | sudo cp etc/systemd/system/reset.service /etc/systemd/system/reset.service 323 | sudo chmod 644 /etc/systemd/system/reset.service 324 | sudo systemctl enable reset.service 325 | sudo systemctl start reset.service 326 | ``` 327 | 328 | This is the software reset schematic. It has a 5v pull-up signal between a 10k resistor. 329 | ![Soft reset](https://github.com/reynico/raspberry-websdr/raw/master/gpio_soft_reset.png) 330 | 331 | ## Blacklist RTL modules 332 | 333 | You'll need to blacklist some modules in order to get rtl_tcp working. Edit or create the file `/etc/modprobe.d/blacklist.conf` with the following content: 334 | 335 | ```bash 336 | blacklist dvb_usb_rtl28xxu 337 | ``` 338 | 339 | 340 | ## SD Card wear 341 | 342 | To reduce the SD Card wear due to writes from logs, I recommend you to install [log2ram](https://github.com/azlux/log2ram), a tool which creates a ramdisk mountpoint for `/var/log`. 343 | 344 | ```bash 345 | echo "deb [signed-by=/usr/share/keyrings/azlux-archive-keyring.gpg] http://packages.azlux.fr/debian/ bookworm main" | sudo tee /etc/apt/sources.list.d/azlux.list 346 | sudo wget -O /usr/share/keyrings/azlux-archive-keyring.gpg https://azlux.fr/repo.gpg 347 | sudo apt update 348 | sudo apt install log2ram 349 | 350 | sudo systemctl enable log2ram 351 | ``` 352 | 353 | You may want to increase the ramdisk partition size up to 200M, edit the file `/etc/log2ram.conf` and set `SIZE=200M`. 354 | 355 | While Websdr has an option to prevent logs to be written to the card (`logfileinterval 0`), there are two log files that are written anyway: `log-cpuload.txt` and `userslog.txt`. Create a logs folder inside `/var/log` and link both files in a symbolic fashion. 356 | 357 | ```bash 358 | sudo rm /home/pi/dist11/userslog.txt 359 | sudo rm /home/pi/dist11/log-cpuload.txt 360 | sudo mkdir -p /var/log/websdr 361 | ln -s /var/log/websdr/userslog.txt /home/pi/dist11/userslog.txt 362 | ln -s /var/log/websdr/log-cpuload.txt /home/pi/dist11/log-cpuload.txt 363 | ``` 364 | -------------------------------------------------------------------------------- /README.es.md: -------------------------------------------------------------------------------- 1 | # Nodo WebSDR utilizando un Raspberry PI 2 | 3 | Leer en [Inglés](README.md), [Español](README.es.md) 4 | Read in [English](README.md), [Español](README.es.md) 5 | 6 | ![Receptor SDR funcionando en la banda de 40 metros](https://github.com/reynico/raspberry-websdr/raw/master/sdr-40m.jpg) 7 | 8 | - [Nodo WebSDR utilizando un Raspberry PI](#nodo-websdr-utilizando-un-raspberry-pi) 9 | - [Requerimientos de hardware](#requerimientos-de-hardware) 10 | - [Estás usando una Raspberry PI 4 o mas nueva?](#estás-usando-una-raspberry-pi-4-o-mas-nueva) 11 | - [Configuración y software requerido](#configuración-y-software-requerido) 12 | - [Clonar este repositorio](#clonar-este-repositorio) 13 | - [Librerias faltantes](#librerias-faltantes) 14 | - [libpng12](#libpng12) 15 | - [libssl-1.0.0](#libssl-100) 16 | - [Direct sampling en RTL SDR (500KHz - 28.8MHz sin upconverter!)](#direct-sampling-en-rtl-sdr-500khz---288mhz-sin-upconverter) 17 | - [Instalar WebSDR](#instalar-websdr) 18 | - [WebSDR de una sola banda](#websdr-de-una-sola-banda) 19 | - [WebSDR multi banda controlado por tiempo](#websdr-multi-banda-controlado-por-tiempo) 20 | - [Controlador de antena](#controlador-de-antena) 21 | - [Habilitar WebSDR doble banda](#habilitar-websdr-doble-banda) 22 | - [Cron](#cron) 23 | - [Control manual](#control-manual) 24 | - [Configuración de los pines GPIO](#configuración-de-los-pines-gpio) 25 | - [Reinicio por software](#reinicio-por-software) 26 | - [Blacklist de los modulos RTL](#blacklist-de-los-modulos-rtl) 27 | - [Desgaste de la tarjeta SD](#desgaste-de-la-tarjeta-sd) 28 | 29 | Esta guía cubre la configuración de un receptor de doble banda (80/40 metros) basada en forma horaria. Usa un relay para intercambiar entre antenas, controlado por un puerto GPiO del Raspberry PI (utilizando un transistor como driver). 30 | 31 | Muchas gracias a Pieter PA3FWM, Mark GP4FPH y Jarek SQ9NFI por la gran mano configurando el parámetro progfreq.req setting. 32 | 33 | ## Requerimientos de hardware 34 | 35 | - Raspberry PI 3 o superior 36 | - [Raspberry Pi OS (Legacy) instalado](https://downloads.raspberrypi.com/raspios_oldstable_lite_armhf/images/raspios_oldstable_lite_armhf-2024-03-12/2024-03-12-raspios-bullseye-armhf-lite.img.xz) y funcionando. 37 | - Acceso a internet configurado y funcionando 38 | - Receptor RTL-SDR USB 39 | 40 | ## Estás usando una Raspberry PI 4 o mas nueva? 41 | 42 | A partir de la Raspberry PI 4, el hardware corre en una arquitectura diferente (pero similar) al hardware viejo. `websdr-rpi` no es completamente compatible con esta nueva arquitectura, mas que nada por dependencias desactualizadas y por lo tanto puede que te encuentres con algunos de estos errores: 43 | 44 | ``` 45 | websdr-rpi: cannot execute: required file not found 46 | ``` 47 | 48 | or 49 | 50 | ``` 51 | websdr-rpi: error while loading shared libraries: libfftw3f.so.3 52 | ``` 53 | 54 | Algunos de los pasos listados aquí abajo tienen instrucciones específicas para configurar una Raspberry PI 4 con hardware moderno y solucionar estos problemas. 55 | 56 | ## Configuración y software requerido 57 | 58 | ```bash 59 | sudo apt update && sudo apt upgrade 60 | sudo apt install -y \ 61 | g++ \ 62 | make \ 63 | libsigc++-2.0-dev \ 64 | libgsm1-dev \ 65 | libpopt-dev \ 66 | tcl8.6-dev \ 67 | libgcrypt-dev \ 68 | libspeex-dev \ 69 | libasound2-dev \ 70 | alsa-utils \ 71 | libsigc++-2.0-0v5 \ 72 | cmake \ 73 | groff \ 74 | rtl-sdr \ 75 | libusb-1.0-0-dev \ 76 | unzip \ 77 | git \ 78 | rsync 79 | ``` 80 | 81 | Si estás usando una Rapberry PI 4 o mas nueva (con arquitectura arm64), instalá la arquitectura `armhf`: 82 | 83 | ```bash 84 | sudo dpkg --add-architecture armhf 85 | sudo apt install -y \ 86 | libsigc++-2.0-dev:armhf \ 87 | libgsm1-dev:armhf \ 88 | libpopt-dev:armhf \ 89 | tcl8.6-dev:armhf \ 90 | libgcrypt-dev:armhf \ 91 | libspeex-dev:armhf \ 92 | libasound2-dev:armhf \ 93 | libsigc++-2.0-0v5:armhf \ 94 | libusb-1.0-0-dev:armhf \ 95 | libc6:armhf \ 96 | zlib1g-dev:armhf 97 | ``` 98 | 99 | ## Clonar este repositorio 100 | 101 | ``` 102 | cd /home/pi/ 103 | git clone https://github.com/reynico/raspberry-websdr.git 104 | cd raspberry-websdr/ 105 | ``` 106 | 107 | ## Librerias faltantes 108 | 109 | Con las últimas actualizaciones, algunas librerias requeridas por websdr fueron deprecadas haciéndolas incompatibles. 110 | 111 | ### libpng12 112 | 113 | Si estás usando una Rapberry PI 4 o mas nueva (con arquitectura arm64), compilá `libpng` de esta manera: 114 | 115 | ```bash 116 | sudo apt install gcc-arm-linux-gnueabihf g++-arm-linux-gnueabihf 117 | 118 | tar xf libpng-1.2.59.tar.xz 119 | cd libpng-1.2.59/ 120 | CC=arm-linux-gnueabihf-gcc \ 121 | CXX=arm-linux-gnueabihf-g++ \ 122 | ./configure \ 123 | --host=arm-linux-gnueabihf \ 124 | --prefix=/usr/local \ 125 | --libdir=/usr/local/lib/arm-linux-gnueabihf 126 | 127 | make 128 | sudo make install 129 | sudo ldconfig 130 | sudo ln -s /usr/local/lib/arm-linux-gnueabihf/libpng12.so.0 /lib/arm-linux-gnueabihf/libpng12.so.0 131 | cd.. 132 | 133 | ``` 134 | 135 | Si estás usando una Raspberry anterior a una PI 4, usá este comando: 136 | 137 | ```bash 138 | tar xf libpng-1.2.59.tar.xz 139 | cd libpng-1.2.59/ 140 | ./configure 141 | make 142 | sudo make install 143 | cd .. 144 | ``` 145 | 146 | ### libssl-1.0.0 147 | 148 | Si estás usando una Rapberry PI 4 o mas nueva (con arquitectura arm64), compilá `libssl` de esta manera: 149 | 150 | ```bash 151 | cd openssl-1.0.0k/ 152 | 153 | CC=arm-linux-gnueabihf-gcc ./Configure linux-armv4 shared \ 154 | --prefix=/usr/local/arm32 \ 155 | --openssldir=/usr/local/arm32/ssl \ 156 | -Wl,--version-script=openssl.ld \ 157 | -Wl,-Bsymbolic-functions 158 | 159 | make 160 | sudo make install_sw 161 | 162 | sudo ln -s /usr/local/arm32/lib/libcrypto.so.1.0.0 /usr/lib/arm-linux-gnueabihf/libcrypto.so.1.0.0 163 | sudo ln -s /usr/local/arm32/lib/libssl.so.1.0.0 /usr/lib/arm-linux-gnueabihf/libssl.so.1.0.0 164 | 165 | sudo ldconfig 166 | ``` 167 | 168 | 169 | Si estás usando una Raspberry anterior a una PI 4, usá este comando: 170 | 171 | ```bash 172 | tar xf openssl-1.0.0k.tar.gz 173 | cd openssl-1.0.0k/ 174 | cat << EOF > openssl.ld 175 | OPENSSL_1.0.0 { 176 | global: 177 | *; 178 | }; 179 | EOF 180 | ./config shared --prefix=$HOME/libssl/openssl --openssldir=$HOME/libssl/ssl -Wl,--version-script=openssl.ld -Wl,-Bsymbolic-functions 181 | make 182 | sudo make install_sw 183 | sudo ldconfig 184 | ldd $HOME/libssl/openssl/bin/openssl 185 | sudo cp $HOME/libssl/openssl/lib/libcrypto.so /usr/local/lib 186 | sudo chmod 0755 /usr/local/lib/libcrypto.so 187 | sudo ldconfig 188 | cd .. 189 | ``` 190 | 191 | ## Direct sampling en RTL SDR (500KHz - 28.8MHz sin upconverter!) 192 | 193 | Si estás usando un receptor RTL-SDR.com V4, necesitás una versión especial del driver `rtl-sdr` para poder recibir 500KHz-28.8MHz 194 | 195 | ```bash 196 | git clone https://github.com/rtlsdrblog/rtl-sdr-blog 197 | cd rtl-sdr-blog/ 198 | mkdir build 199 | cd build 200 | cmake ../ -DINSTALL_UDEV_RULES=ON 201 | make 202 | sudo make install 203 | sudo cp ../rtl-sdr.rules /etc/udev/rules.d/ 204 | sudo ldconfig 205 | ``` 206 | 207 | If you are running a different SDR device, but if it supports direct sampling (such as RTL-SDR.com V3 receiver), there's a way to receive 500KHz-28.8MHz without an external upconverter hardware, easing the node build. 208 | 209 | Si estás usando un receptor SDR diferente, pero que soporta direct sampling (como el RTL-SDR.com v3), hay una manera de recibir las frecuencias entre 500KHz y 28.8MHz sin necesidad de un upconverter externo, facilitando la construcción del nodo. 210 | 211 | ```bash 212 | unzip rtl-sdr-driver-patched.zip 213 | cd pkg-rtl-sdr/ 214 | mkdir -p build/ 215 | cd build/ 216 | cmake ../ -DINSTALL_UDEV_RULES=ON -DDETACH_KERNEL_DRIVER=ON 217 | make 218 | sudo make install 219 | sudo ldconfig 220 | cd ../../ 221 | cp -r pkg-rtl-sdr/ /home/pi/ 222 | cd .. 223 | ``` 224 | 225 | No olvides eliminar o comentar la linea `progfreq` de el(los) archivo(s) de configuración de websdr. 226 | 227 | Use the following systemd unit for direct sampling: 228 | 229 | ```bash 230 | sudo cp etc/systemd/system/rtl_tcp_direct_sampling.service /etc/systemd/system/rtl_tcp@.service 231 | ``` 232 | 233 | ## Instalar WebSDR 234 | 235 | - Este es un buen momento para reiniciar la Raspberry PI, ejecutar `sudo reboot`. 236 | - Enviale un email a [Pieter](http://websdr.org/) para obtener una copia de WebSDR. 237 | - Copia el binario websdr-rpi y los archivos de configuración a tu directorio personal (/home/pi/) 238 | - Edita websdr-80m.cfg y websdr-40m.cfg para ajustarlo a tu configuración 239 | - Crea dos Systemd units para controlar websdr y rtl_tcp 240 | 241 | ```bash 242 | sudo cp etc/systemd/system/websdr@.service /etc/systemd/system/websdr@.service 243 | ``` 244 | 245 | Copia este archivo solo si NO estás usando el modo direct sampling de tu RTL SDR. Los receptores RTL-SDR.com V4 no necesitan la configuración especial de [Direct Sampling](#rtl-sdr-direct-sampling-500khz---288mhz-without-upconverter) para funcionar. 246 | 247 | ```bash 248 | sudo cp etc/systemd/system/rtl_tcp@.service /etc/systemd/system/rtl_tcp@.service 249 | ``` 250 | 251 | ## WebSDR de una sola banda 252 | 253 | El setup descripto en esta sección es el mas común: un receptor SDR de una sola banda, por ejemplo de 40 metros. 254 | 255 | - Crea las systemd units para controlar `websdr` y `rtl_tcp`: 256 | 257 | ```bash 258 | sudo cp etc/systemd/system/websdr.service /etc/systemd/system/websdr.service 259 | sudo cp etc/systemd/system/rtl_tcp@.service /etc/systemd/system/rtl_tcp@.service 260 | ``` 261 | 262 | - Habilita la systemd unit `rtl_tcp`: 263 | 264 | ```bash 265 | sudo systemctl enable rtl_tcp@0.service 266 | ``` 267 | 268 | - Habilita la systemd unit `websdr`: 269 | 270 | ```bash 271 | sudo systemctl enable websdr.service 272 | ``` 273 | 274 | ## WebSDR multi banda controlado por tiempo 275 | 276 | El Setup descripto en esta sección es mas poderoso: donde la Raspberry PI cambia la banda de recepción de WebSDR de acuerdo a los horarios definidos en `crontab`. 277 | 278 | ### Controlador de antena 279 | 280 | Esta configuración de WebSDR utiliza un solo receptor RTL-SDR para dos bandas (40/80 metros), crontab toma el control de que banda está funcionando. Así como la longitud de onda no es la misma para las dos bandas, estoy utilizando un pin GPiO para intercambiar entre ellas usando un relé doble polo doble inversor. El pin GPiO3 es controlado por el script check_band.sh 281 | ![Circuito esquemático del control de antena](https://github.com/reynico/raspberry-websdr/raw/master/gpio_antenna_control_npn.png) 282 | 283 | ### Habilitar WebSDR doble banda 284 | 285 | - Hablita solo la systemd unit `rtl_tcp`. Websdr es controlado por `crontab`. 286 | 287 | ```bash 288 | sudo systemctl enable rtl_tcp@0.service 289 | ``` 290 | 291 | ### Cron 292 | 293 | - Fabriqué una configuración de crontabl para intercambiar entre las bandas de 40 y 80 metros basada en horarios. Sólo importa las lineas del archivo crontab en tu crontab. 294 | 295 | ### Control manual 296 | 297 | Siempre podrás controlar el cambio de bandas de forma manual. Deshabilita las lineas de cron para evitar cambios automáticos. Luego puedes usar: 298 | 299 | ```bash 300 | sudo systemctl stop websdr@40.service 301 | sudo systemctl start websdr@40.service 302 | ``` 303 | 304 | Donde 40 es la banda que quieres recibir. Puedes usar y configurar prácticamente cualquier banda que quieras, siempre y cuando hayas configurado tu archivo websdr-{{banda}}m.cfg 305 | 306 | ### Configuración de los pines GPIO 307 | 308 | - Copia el archivo etc/rc.local a tu /etc/rc.local 309 | 310 | ```bash 311 | sudo cp etc/rc.local /etc/rc.local 312 | ``` 313 | 314 | - Controla y revisa el número de puerto GPiO para el control de antenas y el botón de reinicio por software. 315 | 316 | ## Reinicio por software 317 | 318 | - Hay un script en Python que controla el reinicio de la Raspberry PI a través de un switch de hardware, sin necesidad de quitarle la energía eléctrica. 319 | - Revisa el archivo /etc/rc.local y sincroniza el pin GPiO designado para esta aplicación. 320 | - Copia el archivo etc/systemd/system/reset.service a /etc/systemd/system/reset.service 321 | 322 | ```bash 323 | sudo cp opt/reset.py /opt/reset.py 324 | sudo cp etc/systemd/system/reset.service /etc/systemd/system/reset.service 325 | sudo chmod 644 /etc/systemd/system/reset.service 326 | sudo systemctl enable reset.service 327 | sudo systemctl start reset.service 328 | ``` 329 | 330 | Éste es el circuito esquemático del reinicio por software. Tiene una señal de pull-up a 5v a través de una resistencia de 10k. 331 | ![Soft reset](https://github.com/reynico/raspberry-websdr/raw/master/gpio_soft_reset.png) 332 | 333 | ## Blacklist de los modulos RTL 334 | 335 | Tendrás que hacer blacklist (bloquear) algunos modulos para conseguir que rtl_tcp funcione. Edita o crea el archivo `/etc/modprobe.d/blacklist.conf` con el siguiente contenido: 336 | 337 | ```bash 338 | blacklist dvb_usb_rtl28xxu 339 | ``` 340 | 341 | 342 | ## Desgaste de la tarjeta SD 343 | 344 | Para reducir el desgaste de la tarjeta SD por las escrituras a disco de los logs, recomiendo instalar [log2ram](https://github.com/azlux/log2ram), una herramienta que crea un punto de montaje en ram para `/var/log`. 345 | 346 | ```bash 347 | echo "deb [signed-by=/usr/share/keyrings/azlux-archive-keyring.gpg] http://packages.azlux.fr/debian/ bookworm main" | sudo tee /etc/apt/sources.list.d/azlux.list 348 | sudo wget -O /usr/share/keyrings/azlux-archive-keyring.gpg https://azlux.fr/repo.gpg 349 | sudo apt update 350 | sudo apt install log2ram 351 | 352 | sudo systemctl enable log2ram 353 | ``` 354 | 355 | Probablemente quieras aumentar el tamaño de la partición de logs a 200M, edita el archivo `/etc/log2ram.conf` y configura `SIZE=200M`. 356 | 357 | Si bien Websdr tiene una opción para prevenir que los logs se escriban a la tarjeta (`logfileinterval 0`), hay dos archivos de log que se escriben de todas formas: `log-cpuload.txt` y `userslog.txt`. Crea un directorio para logs dentro de `/var/log` y enlaza de forma simbólica para los dos archivos. 358 | 359 | ```bash 360 | sudo rm /home/pi/dist11/userslog.txt 361 | sudo rm /home/pi/dist11/log-cpuload.txt 362 | sudo mkdir -p /var/log/websdr 363 | ln -s /var/log/websdr/userslog.txt /home/pi/dist11/userslog.txt 364 | ln -s /var/log/websdr/log-cpuload.txt /home/pi/dist11/log-cpuload.txt 365 | ``` 366 | -------------------------------------------------------------------------------- /dist11/pub2/websdr-sound.js: -------------------------------------------------------------------------------- 1 | //WebSDR HTML5 client side - Copyright 2013-2014, pa3fwm@websdr.org - all rights reserved 2 | //Since the intended use of this code involves sending a copy to the client computer, I (PA3FWM) hereby allow making it available unmodified, via my original WebSDR server software, to original WebSDR clients. Other use, including distribution in part or entirety or as part of other software, or reverse engineering, is not allowed without my explicit prior permission. 3 | (function(){var m=null,aa=window.AudioContext||window.webkitAudioContext;if(aa){document.ct||(document.ct=new aa);var ba;try{ba=document.ct.createConvolver}catch(ja){}ba||(document.ct=m,sup_webaudio=!1)}var r=32768; 4 | function ta(){function ca(a){var b=a.outputBuffer.getChannelData(0),c=a.outputBuffer.length;ka++;for(a=0;a=r&&(q-=r);var d=t[q];da&&(e=(k*d+(g-k)*e)/g)}K&&(e=0);b[a]=2E-5*e*S}T=(new Date).getTime();a=(new Date).getTime();U&&(c=a-U,c>ea&&(ea=c));U=a;F&&0==b[0]&&H++}function la(a){a=a.inputBuffer.getChannelData(0);K||(N?N--:0!=a[0]?H=5:(H--,H||(H=5,N=25,F++,f.disconnect(),f.onaudioprocess=m,f=g.createScriptProcessor(I,1,1),f.onaudioprocess= 5 | ca,this.p=f,f.connect(w),f.connect(D))))}document.getElementById("soundappletdiv").innerHTML="
WebSDR HTML5 sound - Copyright 2007-2014, P.T. de Boer, pa3fwm@websdr.org
"; 6 | var t=new Int16Array(r),l=0,q=6144,k=0,C=8E3,h=48E3,B=1,S=1,V=-1,L,x=0,y=[],ma=[-5504,-5248,-6016,-5760,-4480,-4224,-4992,-4736,-7552,-7296,-8064,-7808,-6528,-6272,-7040,-6784,-2752,-2624,-3008,-2880,-2240,-2112,-2496,-2368,-3776,-3648,-4032,-3904,-3264,-3136,-3520,-3392,-22016,-20992,-24064,-23040,-17920,-16896,-19968,-18944,-30208,-29184,-32256,-31232,-26112,-25088,-28160,-27136,-11008,-10496,-12032,-11520,-8960,-8448,-9984,-9472,-15104,-14592,-16128,-15616,-13056,-12544,-14080,-13568,-344,-328, 7 | -376,-360,-280,-264,-312,-296,-472,-456,-504,-488,-408,-392,-440,-424,-88,-72,-120,-104,-24,-8,-56,-40,-216,-200,-248,-232,-152,-136,-184,-168,-1376,-1312,-1504,-1440,-1120,-1056,-1248,-1184,-1888,-1824,-2016,-1952,-1632,-1568,-1760,-1696,-688,-656,-752,-720,-560,-528,-624,-592,-944,-912,-1008,-976,-816,-784,-880,-848,5504,5248,6016,5760,4480,4224,4992,4736,7552,7296,8064,7808,6528,6272,7040,6784,2752,2624,3008,2880,2240,2112,2496,2368,3776,3648,4032,3904,3264,3136,3520,3392,22016,20992,24064,23040, 8 | 17920,16896,19968,18944,30208,29184,32256,31232,26112,25088,28160,27136,11008,10496,12032,11520,8960,8448,9984,9472,15104,14592,16128,15616,13056,12544,14080,13568,344,328,376,360,280,264,312,296,472,456,504,488,408,392,440,424,88,72,120,104,24,8,56,40,216,200,248,232,152,136,184,168,1376,1312,1504,1440,1120,1056,1248,1184,1888,1824,2016,1952,1632,1568,1760,1696,688,656,752,720,560,528,624,592,944,912,1008,976,816,784,880,848],na=[0.0084619,3.6183E-4,2.0264E-4,-4.8621E-5,-3.6771E-4,-7.0991E-4,-0.0010355, 9 | -0.0012935,-0.0014506,-0.0014733,-0.0013571,-0.0011019,-7.4253E-4,-3.0836E-4,1.358E-4,5.6517E-4,8.8996E-4,0.0010664,0.0010912,9.2941E-4,6.0906E-4,1.5494E-4,-3.7203E-4,-9.1186E-4,-0.001389,-0.0017416,-0.0019133,-0.0018764,-0.0016225,-0.0011768,-5.8255E-4,9.2361E-5,7.5528E-4,0.0013306,0.0017366,0.0019135,0.0018299,0.0014834,9.0902E-4,1.6944E-4,-6.4513E-4,-0.0014333,-0.0020901,-0.002526,-0.0026728,-0.0024998,-0.0020096,-0.0012556,-3.2129E-4,6.8312E-4,0.0016291,0.0023941,0.0028705,0.0029846,0.002705, 10 | 0.0020504,0.0010884,-7.0093E-5,-0.0012848,-0.0024011,-0.0032709,-0.0037692,-0.0038134,-0.0033791,-0.0024966,-0.0012601,1.8871E-4,0.0016733,0.0030042,0.0040027,0.0045239,0.0044775,0.0038421,0.0026705,0.0010869,-7.2511E-4,-0.0025439,-0.0041336,-0.0052788,-0.0058077,-0.0056185,-0.0046979,-0.0031235,-0.0010625,0.0012472,0.0035218,0.005466,0.0068099,0.0073449,0.0069525,0.0056265,0.0034784,7.3321E-4,-0.0022942,-0.005233,-0.007697,-0.0093357,-0.0098779,-0.0091724,-0.0072151,-0.0041599,-3.1209E-4,0.0038988, 11 | 0.007962,0.011341,0.013538,0.014156,0.012956,0.0099028,0.0051811,-7.971E-4,-0.0074221,-0.013934,-0.019491,-0.023251,-0.024456,-0.02252,-0.017099,-0.0081382,0.0040987,0.019038,0.035829,0.053411,0.070595,0.086169,0.098999,0.10813,0.11288,0.11288,0.10813,0.098999,0.086169,0.070595,0.053411,0.035829,0.019038,0.0040987,-0.0081382,-0.017099,-0.02252,-0.024456,-0.023251,-0.019491,-0.013934,-0.0074221,-7.971E-4,0.0051811,0.0099028,0.012956,0.014156,0.013538,0.011341,0.007962,0.0038988,-3.1209E-4,-0.0041599, 12 | -0.0072151,-0.0091724,-0.0098779,-0.0093357,-0.007697,-0.005233,-0.0022942,7.3321E-4,0.0034784,0.0056265,0.0069525,0.0073449,0.0068099,0.005466,0.0035218,0.0012472,-0.0010625,-0.0031235,-0.0046979,-0.0056185,-0.0058077,-0.0052788,-0.0041336,-0.0025439,-7.2511E-4,0.0010869,0.0026705,0.0038421,0.0044775,0.0045239,0.0040027,0.0030042,0.0016733,1.8871E-4,-0.0012601,-0.0024966,-0.0033791,-0.0038134,-0.0037692,-0.0032709,-0.0024011,-0.0012848,-7.0093E-5,0.0010884,0.0020504,0.002705,0.0029846,0.0028705, 13 | 0.0023941,0.0016291,6.8312E-4,-3.2129E-4,-0.0012556,-0.0020096,-0.0024998,-0.0026728,-0.002526,0.0035218,0.0012472,-0.0010625,-0.0031235,-0.0046979,-0.0056185,-0.0058077,-0.0052788,-0.0041336,-0.0025439,-7.2511E-4,0.0010869,0.0026705,0.0038421,0.0044775,0.0045239,0.0040027,0.0030042,0.0016733,1.8871E-4,-0.0012601,-0.0024966,-0.0033791,-0.0038134,-0.0037692,-0.0032709,-0.0024011,-0.0012848,-7.0093E-5,0.0010884,0.0020504,0.002705,0.0029846,0.0028705,0.0023941,0.0016291,6.8312E-4,-3.2129E-4,-0.0012556, 14 | -0.0020096,-0.0024998,-0.0026728,-0.002526,-0.0020901,-0.0014333,-6.4513E-4,1.6944E-4,9.0902E-4,0.0014834,0.0018299,0.0019135,0.0017366,0.0013306,7.5528E-4,9.2361E-5,-5.8255E-4,-0.0011768,-0.0016225,-0.0018764,-0.0019133,-0.0017416,-0.001389,-9.1186E-4,-3.7203E-4,1.5494E-4,6.0906E-4,9.2941E-4,0.0010912,0.0010664,8.8996E-4,5.6517E-4,1.358E-4,-3.0836E-4,-7.4253E-4,-0.0011019,-0.0013571,-0.0014733,-0.0014506,-0.0012935,-0.0010355,-7.0991E-4,-3.6771E-4,-4.8621E-5,2.0264E-4,3.6183E-4,0.0084619],oa=[-6.0729E-4, 15 | 0.005547,0.0012025,5.5133E-4,1.6992E-4,-2.3195E-4,-5.9426E-4,-8.2206E-4,-8.4752E-4,-6.5061E-4,-2.6977E-4,2.0642E-4,6.5721E-4,9.6247E-4,0.001032,8.3416E-4,4.0395E-4,-1.5769E-4,-7.1117E-4,-0.0011087,-0.0012377,-0.0010475,-5.7139E-4,8.2522E-5,7.5058E-4,0.001259,0.001464,0.0012963,7.7829E-4,2.8808E-5,-7.6962E-4,-0.0014083,-0.001713,-0.0015833,-0.001033,-1.8129E-4,7.6089E-4,0.0015551,0.0019797,0.0019098,0.0013313,3.7702E-4,-7.2555E-4,-0.0016986,-0.0022625,-0.0022768,-0.0016922,-6.3215E-4,6.491E-4,0.0018212, 16 | 0.002576,0.0026933,0.0021119,9.4722E-4,-5.2351E-4,-0.0019315,-0.0029037,-0.0031646,-0.002609,-0.0013427,3.3954E-4,0.0020171,0.0032517,0.0036965,0.003196,0.0018324,-8.0558E-5,-0.0020711,-0.0036212,-0.004303,-0.0038921,-0.0024398,-2.7089E-4,0.0020837,0.0040178,0.0050015,0.0047273,0.003197,7.4395E-4,-0.0020419,-0.0044463,-0.0058212,-0.0057441,-0.0041549,-0.0013781,0.0019266,0.004923,0.0068105,0.0070197,0.0053925,0.0022394,-0.00171,-0.0054734,-0.0080484,-0.0086754,-0.0070524,-0.0034446,0.001345,0.0061452, 17 | 0.0096921,0.010951,0.009402,0.0052149,-7.3621E-4,-0.0070399,-0.012063,-0.014353,-0.013025,-0.0080465,-3.2853E-4,0.0084029,0.015957,0.020176,0.019477,0.013318,0.0024682,-0.010997,-0.023996,-0.032993,-0.034705,-0.026822,-0.0085825,0.018925,0.052806,0.088761,0.12179,0.14705,0.16074,0.16074,0.14705,0.12179,0.088761,0.052806,0.018925,-0.0085825,-0.026822,-0.034705,-0.032993,-0.023996,-0.010997,0.0024682,0.013318,0.019477,0.020176,0.015957,0.0084029,-3.2853E-4,-0.0080465,-0.013025,-0.014353,-0.012063,-0.0070399, 18 | -7.3621E-4,0.0052149,0.009402,0.010951,0.0096921,0.0061452,0.001345,-0.0034446,-0.0070524,-0.0086754,-0.0080484,-0.0054734,-0.00171,0.0022394,0.0053925,0.0070197,0.0068105,0.004923,0.0019266,-0.0013781,-0.0041549,-0.0057441,-0.0058212,-0.0044463,-0.0020419,7.4395E-4,0.003197,0.0047273,0.0050015,0.0040178,0.0020837,-2.7089E-4,-0.0024398,-0.0038921,-0.004303,-0.0036212,-0.0020711,-8.0558E-5,0.0018324,0.003196,0.0036965,0.0032517,0.0020171,3.3954E-4,-0.0013427,-0.002609,-0.0031646,-0.0029037,-0.0019315, 19 | -5.2351E-4,9.4722E-4,0.0021119,0.0026933,0.002576,0.0018212,6.491E-4,-6.3215E-4,-0.0016922,-0.0022768,-0.0022625,-0.0016986,-7.2555E-4,3.7702E-4,0.0013313,0.0019098,0.0019797,0.0015551,7.6089E-4,-1.8129E-4,-0.001033,-0.0015833,-0.001713,-0.0014083,-7.6962E-4,2.8808E-5,7.7829E-4,0.0012963,0.001464,0.001259,7.5058E-4,8.2522E-5,-5.7139E-4,-0.0010475,-0.0012377,-0.0011087,-7.1117E-4,-1.5769E-4,4.0395E-4,8.3416E-4,0.001032,9.6247E-4,6.5721E-4,2.0642E-4,-2.6977E-4,-6.5061E-4,-8.4752E-4,-8.2206E-4,-5.9426E-4, 20 | -2.3195E-4,1.6992E-4,5.5133E-4,0.0012025,0.005547,-6.0729E-4],pa=[-0.0057279,-3.55E-4,-3.4657E-4,-3.2265E-4,-2.8568E-4,-2.3281E-4,-1.673E-4,-8.6773E-5,4.4005E-6,1.0817E-4,2.1895E-4,3.3872E-4,4.6081E-4,5.89E-4,7.1566E-4,8.5449E-4,9.6872E-4,0.0010826,0.0011887,0.0012785,0.0013539,0.0014097,0.0014451,0.0014572,0.0014449,0.0014071,0.0013429,0.001253,0.0011363,9.944E-4,8.2476E-4,6.3679E-4,4.284E-4,2.0073E-4,-3.878E-5,-2.8944E-4,-5.4502E-4,-8.0225E-4,-0.0010553,-0.0012998,-0.0015302,-0.001742,-0.00193, 21 | -0.0020897,-0.0022163,-0.0023046,-0.0023534,-0.0023594,-0.0023179,-0.0022306,-0.0020944,-0.0019112,-0.0016809,-0.0014065,-0.0010903,-7.3703E-4,-3.507E-4,6.2545E-5,4.9677E-4,9.4413E-4,0.0013968,0.001847,0.0022869,0.0027056,0.0030964,0.003449,0.0037558,0.0040081,0.0041989,0.0043211,0.0043693,0.0043381,0.0042245,0.0040256,0.0037411,0.0033716,0.0029193,0.002387,0.0017819,0.0011093,3.7879E-4,-4.0024E-4,-0.0012159,-0.002056,-0.0029064,-0.0037527,-0.0045791,-0.0053699,-0.0061082,-0.006778,-0.007363,-0.0078475, 22 | -0.0082153,-0.0084535,-0.0085479,-0.0084873,-0.0082614,-0.007862,-0.0072826,-0.0065194,-0.00557,-0.0044354,-0.0031181,-0.0016241,3.9188E-5,0.0018614,0.00383,0.0059296,0.008144,0.010454,0.012839,0.015277,0.017745,0.020218,0.022673,0.025085,0.027427,0.029676,0.031808,0.0338,0.035631,0.037279,0.038728,0.039961,0.040964,0.041727,0.04224,0.042498,0.042498,0.04224,0.041727,0.040964,0.039961,0.038728,0.037279,0.035631,0.0338,0.031808,0.029676,0.027427,0.025085,0.022673,0.020218,0.017745,0.015277,0.012839, 23 | 0.010454,0.008144,0.0059296,0.00383,0.0018614,3.9188E-5,-0.0016241,-0.0031181,-0.0044354,-0.00557,-0.0065194,-0.0072826,-0.007862,-0.0082614,-0.0084873,-0.0085479,-0.0084535,-0.0082153,-0.0078475,-0.007363,-0.006778,-0.0061082,-0.0053699,-0.0045791,-0.0037527,-0.0029064,-0.002056,-0.0012159,-4.0024E-4,3.7879E-4,0.0011093,0.0017819,0.002387,0.0029193,0.0033716,0.0037411,0.0040256,0.0042245,0.0043381,0.0043693,0.0043211,0.0041989,0.0040081,0.0037558,0.003449,0.0030964,0.0027056,0.0022869,0.001847,0.0013968, 24 | 9.4413E-4,4.9677E-4,6.2545E-5,-3.507E-4,-7.3703E-4,-0.0010903,-0.0014065,-0.0016809,-0.0019112,-0.0020944,-0.0022306,-0.0023179,-0.0023594,-0.0023534,-0.0023046,-0.0022163,-0.0020897,-0.00193,-0.001742,-0.0015302,-0.0012998,-0.0010553,-8.0225E-4,-5.4502E-4,-2.8944E-4,-3.878E-5,2.0073E-4,4.284E-4,6.3679E-4,8.2476E-4,9.944E-4,0.0011363,0.001253,0.0013429,0.0014071,0.0014449,0.0014572,0.0014451,0.0014097,0.0013539,0.0012785,0.0011887,0.0010826,9.6872E-4,8.5449E-4,7.1566E-4,5.89E-4,4.6081E-4,3.3872E-4, 25 | 2.1895E-4,1.0817E-4,4.4005E-6,-8.6773E-5,-1.673E-4,-2.3281E-4,-2.8568E-4,-3.2265E-4,-3.4657E-4,-3.55E-4,-0.0057279],qa=[1.4796E-5,5.4604E-4,1.0129E-4,-1.1334E-6,-1.3189E-4,-1.9395E-4,-1.3571E-4,2.1337E-5,1.8632E-4,2.5029E-4,1.5699E-4,-5.2509E-5,-2.543E-4,-3.138E-4,-1.739E-4,9.7403E-5,3.3704E-4,3.8346E-4,1.8391E-4,-1.5855E-4,-4.3525E-4,-4.5776E-4,-1.8388E-4,2.3889E-4,5.5005E-4,5.3497E-4,1.7021E-4,-3.4174E-4,-6.8209E-4,-6.1268E-4,-1.3912E-4,4.6928E-4,8.3027E-4,6.8693E-4,8.6099E-5,-6.2455E-4,-9.9487E-4, 26 | -7.5513E-4,-6.7519E-6,8.1051E-4,0.0011744,8.1167E-4,-1.0417E-4,-0.0010287,-0.001367,-8.5273E-4,2.5207E-4,0.0012819,0.0015701,8.7113E-4,-4.4169E-4,-0.0015723,-0.0017801,-8.6101E-4,6.801E-4,0.0019007,0.0019933,8.1468E-4,-9.7311E-4,-0.0022683,-0.0022045,-7.237E-4,0.0013276,0.0026763,0.002408,5.7881E-4,-0.0017511,-0.0031255,-0.0025968,-3.6912E-4,0.0022518,0.0036165,0.0027634,8.2343E-5,-0.0028398,-0.0041506,-0.0028982,2.9666E-4,0.0035272,0.0047294,0.0029905,-7.8603E-4,-0.0043296,-0.0053562,-0.0030274, 27 | 0.0014094,0.0052683,0.0060364,0.0029929,-0.0021982,-0.0063734,-0.0067797,-0.0028658,0.003197,0.0076896,0.0076028,0.0026174,-0.0044718,-0.0092868,-0.008534,-0.0022054,0.0061279,0.01128,0.0096244,0.0015614,-0.0083444,-0.013874,-0.010969,-5.6468E-4,0.011455,0.017461,0.01276,-0.0010262,-0.016164,-0.022908,-0.015444,0.0037854,0.024249,0.03255,0.020318,-0.0095077,-0.041874,-0.055528,-0.033308,0.02819,0.11468,0.19857,0.25007,0.25007,0.19857,0.11468,0.02819,-0.033308,-0.055528,-0.041874,-0.0095077,0.020318, 28 | 0.03255,0.024249,0.0037854,-0.015444,-0.022908,-0.016164,-0.0010262,0.01276,0.017461,0.011455,-5.6468E-4,-0.010969,-0.013874,-0.0083444,0.0015614,0.0096244,0.01128,0.0061279,-0.0022054,-0.008534,-0.0092868,-0.0044718,0.0026174,0.0076028,0.0076896,0.003197,-0.0028658,-0.0067797,-0.0063734,-0.0021982,0.0029929,0.0060364,0.0052683,0.0014094,-0.0030274,-0.0053562,-0.0043296,-7.8603E-4,0.0029905,0.0047294,0.0035272,2.9666E-4,-0.0028982,-0.0041506,-0.0028398,8.2343E-5,0.0027634,0.0036165,0.0022518,-3.6912E-4, 29 | -0.0025968,-0.0031255,-0.0017511,5.7881E-4,0.002408,0.0026763,0.0013276,-7.237E-4,-0.0022045,-0.0022683,-9.7311E-4,8.1468E-4,0.0019933,0.0019007,6.801E-4,-8.6101E-4,-0.0017801,-0.0015723,-4.4169E-4,8.7113E-4,0.0015701,0.0012819,2.5207E-4,-8.5273E-4,-0.001367,-0.0010287,-1.0417E-4,8.1167E-4,0.0011744,8.1051E-4,-6.7519E-6,-7.5513E-4,-9.9487E-4,-6.2455E-4,8.6099E-5,6.8693E-4,8.3027E-4,4.6928E-4,-1.3912E-4,-6.1268E-4,-6.8209E-4,-3.4174E-4,1.7021E-4,5.3497E-4,5.5005E-4,2.3889E-4,-1.8388E-4,-4.5776E-4, 30 | -4.3525E-4,-1.5855E-4,1.8391E-4,3.8346E-4,3.3704E-4,9.7403E-5,-1.739E-4,-3.138E-4,-2.543E-4,-5.2509E-5,1.5699E-4,2.5029E-4,1.8632E-4,2.1337E-5,-1.3571E-4,-1.9395E-4,-1.3189E-4,-1.1334E-6,1.0129E-4,5.4604E-4,1.4796E-5],fa=[0.0065862,-0.0022838,-0.0059202,-0.0112239,-0.0169697,-0.0214685,-0.022742,-0.0189184,-0.0086407,0.0084932,0.0316914,0.0588926,0.0870663,0.1126093,0.1320398,0.1425063,0.1425063,0.1320398,0.1126093,0.0870663,0.0588926,0.0316914,0.0084932,-0.0086407,-0.0189184,-0.022742,-0.0214685, 31 | -0.0169697,-0.0112239,-0.0059202,-0.0022838,0.0065862],ra=[-0.0086492,-0.0044524,-0.0043664,-0.0030946,-3.094E-4,0.0042348,0.010627,0.018809,0.028525,0.039333,0.050605,0.061655,0.071697,0.079997,0.085925,0.089013,0.089013,0.085925,0.079997,0.071697,0.061655,0.050605,0.039333,0.028525,0.018809,0.010627,0.0042348,-3.094E-4,-0.0030946,-0.0043664,-0.0044524,-0.0086492],sa=[0.0080236,0.001488,-0.0074204,-0.0175495,-0.0195816,-0.0075536,0.0136434,0.0283156,0.020311,-0.0121801,-0.0489419,-0.0561146,-0.0078976, 32 | 0.0922184,0.2069483,0.2833355,0.2833355,0.2069483,0.0922184,-0.0078976,-0.0561146,-0.0489419,-0.0121801,0.020311,0.0283156,0.0136434,-0.0075536,-0.0195816,-0.0175495,-0.0074204,0.001488,0.0080236],da=!0,G=1E3,K=!1,T=0,U=0,ea=0,ka=0,H=5,N=25,g,f,w,u=[],F=0,D,z,O=0,W=0,ga=0,P=0,X=0,Y=0,ha=0;if(g=document.ct){/firefox\/([0-9]+)/i.exec(navigator.userAgent)&&(F=1);g.sampleRate=h;var h=g.sampleRate,I=2048,I=2*I;try{f=g.createScriptProcessor(I,0,1)}catch(ua){f=g.createJavaScriptNode(I,1,1)}f.onaudioprocess= 33 | ca;this.p=f;w=g.createConvolver();f.connect(w);w.connect(g.destination);var j,e;window.sup_android?(e=g.createBuffer(1,32,h),j=e.getChannelData(0),j.set(fa),u[0]=e,e=g.createBuffer(1,32,h),j=e.getChannelData(0),j.set(fa),u[1]=e,e=g.createBuffer(1,32,h),j=e.getChannelData(0),j.set(ra),u[2]=e,e=g.createBuffer(1,32,h),j=e.getChannelData(0),j.set(sa)):(e=g.createBuffer(1,512,h),j=e.getChannelData(0),j.set(oa),u[0]=e,e=g.createBuffer(1,512,h),j=e.getChannelData(0),j.set(na),u[1]=e,e=g.createBuffer(1,512, 34 | h),j=e.getChannelData(0),j.set(pa),u[2]=e,e=g.createBuffer(1,512,h),j=e.getChannelData(0),j.set(qa));u[3]=e;w.normalize=!1;w.buffer=u[0];F&&(D=g.createScriptProcessor(I,1,1),f.connect(D),D.onaudioprocess=la)}else(z=new Audio)&&z.mozSetup?z.mozSetup(1,h):(z=m,window.browsersupporterror&&window.browsersupporterror());var Z=0,Q=0.3*h,R;doe=function(){if(z){var a=z.mozCurrentSampleOffset(),b=Math.round(Q-(O-a));0==Z&&(0==a?b=Math.round(0.05*h):(Z=O,Q=Z+Math.round(0.05*h),Q<0.25*h&&(Q=Math.round(0.25* 35 | h))));for(var c=[],a=0;a=r&&(q-=r);var d=t[q];da&&(e=(k*d+(g-k)*e)/g)}K&&(e=0);(e=2E-5*e*S)||(e=0);g=0.044*e+0.088*W+0.044*ga- -1.354*P-0.53*X;d=0.044*g+0.088*P+0.044*X- -1.354*Y-0.53*ha;ga=W;W=e;X=P;P=g;ha=Y;Y=d;c[a]=d;3==R&&(c[a]=e)}T=(new Date).getTime();b=z.mozWriteAudio(c);O+=b}};var ia,v,$,J=[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],A=[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],M=0,n=new WebSocket("ws://"+window.location.host+"/~~stream?v=11"); 36 | n.binaryType="arraybuffer";var E=500;n.onmessage=function(a){g||doe();var b=(2*r+l-q-((new Date).getTime()-T)*C/1E3)%r;E+=0.01*(b-E);E>2*G&&(q=(r+l-G)%r,b=E=G);B=1+1E-5*(E-G);1.002B&&(B=0.998);b=Math.round(b)+" "+Math.round(G)+" "+Math.round(E)+" "+Math.round(1E6*(B-1))+" "+Math.round(C);b+=g?" WebAudio":z?" MozAudio":" none";b+="
";F&&(b+=" FFbug "+(F-1)+"("+H+","+N+")");document.getElementById("soundappletdebug").innerHTML=b;var b=new Uint8Array(a.data),c;a=l;for(c=0;cd;d++)t[l+d]=ma[b[c+1+d]];l+=128;l>=r&&(l-=r);c+=128;var f;for(f=0;20>f;f++)J[f]=A[f]=0;M=0}else if(144<=b[c]&&223>=b[c])h=4,e=2,v=14-(b[c]>>4);else if(128!=(b[c]&128))h=1,e=2;else if(129==b[c])C=256*b[c+1]+b[c+2],0d;d++)t[l+d]=0; 38 | l+=128;l>=r&&(l-=r);for(f=0;20>f;f++)J[f]=A[f]=0;M=0}if(2==e){e=0;for(f=16==($&16)?12:14;128>e;){d=b[c+3]&255|(b[c+2]&255)<<8|(b[c+1]&255)<<16|(b[c+0]&255)<<24;d<<=h;var p=0,j;j=15-v;var k=ia,s=[999,999,8,4,2,1,99,99];if(0!=d)for(;0==(d&2147483648)&&p>24&255,p+=8,d<<=8);var n=0;j>=s[v]&&n++;j>=s[v-1]&&n++;n>v-1&&(n=v-1);s=(d>>16&65535)>>17-v&-1<d;d++)p+=J[d]*A[d];p|=0; 39 | p=0<=p?p>>12:p+4095>>12;k=s*k+k/2;s=k>>4;for(d=19;0<=d;d--){J[d]+=-(J[d]>>7)+(A[d]*s>>f);if(0==d)break;A[d]=A[d-1]}A[0]=p+k;k=A[0]+(M>>4);M=16==($&16)?0:M+(A[0]<<4>>3);t[l++]=k;l>=r&&(l-=r);e++}0==h&&c--}}if(L)for(;a!=l;){if(1E4>C||a&1)L.setInt16(x,t[a],!0),x+=2,65536<=x&&(b=new ArrayBuffer(65536),x=0,L=new DataView(b),y.push(b));a++;a>=r&&(a-=r)}};n.onopen=function(){soundappletstarted()};n.onclose=function(){for(var a=0;a 3 | 54 | 55 |
56 | Your browser is not supported; if it does not work, please try again with Firefox on Android, or Safari version 6 or later on iOS.
57 | 58 | 59 |


Memorias:

113 |
114 | Configuración: 115 |
116 | 117 | 121 |
122 | 123 | 129 |
130 | 131 | 135 |
136 | 137 | 144 | 145 |
146 |
147 |
148 | 149 | 150 | 151 | 699 | 700 | 701 | 702 | 703 | -------------------------------------------------------------------------------- /dist11/pub2/websdr-base.js: -------------------------------------------------------------------------------- 1 | 2 | // WebSDR JavaScript part 3 | // Copyright 2007-2014, Pieter-Tjerk de Boer, pa3fwm@websdr.org; all rights reserved. 4 | // Naturally, distributing this file by the original WebSDR server software to original WebSDR clients is permitted. 5 | 6 | 7 | // variables governing what the user listens to: 8 | var lo = -2.4, hi = -0.1; // edges of passband, in kHz w.r.t. the carrier 9 | var mode = "LSB"; // 1 if AM, 0 otherwise (SSB/CW); or text "AM", "FM" etc 10 | var band = 0; // id of the band we're listening to 11 | var freq = bandinfo[0].vfo; // frequency (of the carrier) in kHz 12 | var memories = []; 13 | 14 | 15 | // variables governing what the user sees: 16 | var Views = { allbands: 0, othersslow: 1, oneband: 2, blind: 3 }; 17 | var view = Views.blind; 18 | var nwaterfalls = 0; 19 | var waterslowness = 4; 20 | var waterheight = 100; 21 | var watermode = 1; 22 | var scaleheight = 14; 23 | 24 | 25 | // information about the available "virtual" bands: 26 | // contains: effsamplerate, effcenterfreq, zoom, start, minzoom, maxzoom, samplerate, centerfreq, vfo, scaleimgs, realband 27 | var bi = new Array(); 28 | // number of bands: 29 | var nvbands = nbands; 30 | 31 | 32 | // references to objects on the screen: 33 | var scaleobj; 34 | var scaleobjs = new Array(); 35 | var scaleimgs0 = new Array(); 36 | var scaleimgs1 = new Array(); 37 | var passbandobj; 38 | var edgelowerobj; 39 | var edgeupperobj; 40 | var carrierobj; 41 | var smeterobj; 42 | var numericalsmeterobj; 43 | var smeterpeakobj; 44 | var numericalsmeterpeakobj; 45 | var waterfallapplet = new Array(); 46 | var soundapplet = null; 47 | 48 | // timers: 49 | var interval_updatesmeter; 50 | var interval_ajax3; 51 | var timeout_idle; 52 | var setfreqif_fut_timer; // timer for typing in the frequency field 53 | 54 | 55 | // misc 56 | var serveravailable = -1; // -1 means yet to be tested, 0 and 1 mean false and true 57 | var smeterpeaktimer = 2; 58 | var smeterpeak = 0; 59 | var allloadeddone = false; 60 | var waitingforwaterfalls = 0; // number of waterfallapplets that are still in the process of starting 61 | var band_fetchdxtimer = new Array(); 62 | var hidedx = 0; 63 | var usejavawaterfall = 1; 64 | var usejavasound = 1; 65 | var javaerr = 0; 66 | var isTouchDev = false; 67 | 68 | 69 | // derived quantities: 70 | var khzperpixel = bandinfo[band].samplerate / 1024; 71 | var passbandobjstart = 0; // position (in pixels) of start of passband on frequency axis, w.r.t. location of carrier 72 | var passbandobjwidth = 0; // width of passband in pixels 73 | var centerfreq = bandinfo[band].centerfreq; 74 | 75 | 76 | 77 | 78 | function debug(a) { 79 | // console.debug(a); 80 | } 81 | 82 | 83 | // from http://www.switchonthecode.com/tutorials/javascript-tutorial-the-scroll-wheel 84 | function cancelEvent(e) { 85 | e = e ? e : window.event; 86 | if (e.stopPropagation) e.stopPropagation(); 87 | if (e.preventDefault) e.preventDefault(); 88 | e.cancelBubble = true; 89 | e.cancel = true; 90 | e.returnValue = false; 91 | return false; 92 | } 93 | 94 | 95 | function timeout_idle_do() { 96 | try { clearInterval(interval_updatesmeter); } catch (e) { }; 97 | try { clearTimeout(interval_ajax3); } catch (e) { }; 98 | var i; 99 | try { for (i = 0; i < nwaterfalls; i++) waterfallapplet[i].destroy(); } catch (e) { }; 100 | try { soundapplet.destroy(); } catch (e) { }; 101 | document.body.innerHTML = "Idle time out.\n"; 102 | } 103 | 104 | 105 | function timeout_idle_restart() { 106 | if (!idletimeout) return; 107 | try { clearTimeout(timeout_idle); } catch (e) { }; 108 | timeout_idle = setTimeout('timeout_idle_do();', idletimeout); 109 | } 110 | 111 | function send_soundsettings_to_server() { 112 | var m = mode; 113 | if (m == "USB") m = 0; 114 | else if (m == "LSB") m = 0; 115 | else if (m == "CW") m = 0; 116 | else if (m == "AM") m = 1; 117 | else if (m == "FM") m = 4; 118 | try { 119 | soundapplet.setparam( 120 | "f=" + freq 121 | + "&band=" + band 122 | + "&lo=" + lo 123 | + "&hi=" + hi 124 | + "&mode=" + m 125 | + "&name=" + encodeURIComponent(document.usernameform.username.value) 126 | ); 127 | } catch (e) { }; 128 | timeout_idle_restart() 129 | } 130 | 131 | 132 | function setsquelch(a) { 133 | a = Number(a); 134 | soundapplet.setparam("squelch=" + a); 135 | } 136 | 137 | function setautonotch(a) { 138 | a = Number(a); 139 | soundapplet.setparam("autonotch=" + a); 140 | } 141 | 142 | function setmute(a) { 143 | a = Number(a); 144 | soundapplet.setparam("mute=" + a); 145 | } 146 | 147 | 148 | function draw_passband() { 149 | passbandobjstart = Math.round((lo - 0.045) / khzperpixel); 150 | passbandobjwidth = Math.round((hi + 0.045) / khzperpixel) - passbandobjstart; 151 | if (passbandobjwidth == 0) passbandobjwidth = 1; 152 | passbandobj.style.width = passbandobjwidth + "px"; 153 | if (!scaleobj) return; 154 | 155 | var x = (freq - centerfreq) / khzperpixel + 512; 156 | var maxx = parseInt(scaleobj.style.width); 157 | if (isTouchDev && x > maxx) x = maxx; 158 | var y = scaleobj.offsetTop + 15; 159 | passbandobj.style.top = y + "px"; 160 | edgelowerobj.style.top = y + "px"; 161 | edgeupperobj.style.top = y + "px"; 162 | carrierobj.style.top = y + "px"; 163 | carrierobj.style.left = x + "px"; 164 | x = x + passbandobjstart; 165 | passbandobj.style.left = x + "px"; 166 | edgelowerobj.style.left = (x - 11) + "px"; 167 | edgeupperobj.style.left = (x + passbandobjwidth) + "px"; 168 | } 169 | 170 | 171 | function iscw() { 172 | return hi - lo < 1.0; 173 | } 174 | 175 | function nominalfreq() { 176 | if (iscw()) return freq + (hi + lo) / 2; 177 | return freq; 178 | } 179 | 180 | function freq2x(f, b) { 181 | return (f - bi[b].effcenterfreq) * 1024 / bi[b].effsamplerate + 512; 182 | } 183 | 184 | function setwaterfall(b, f) 185 | // adjust waterfall so passband is visible 186 | { 187 | if (waitingforwaterfalls > 0) return; 188 | var x = freq2x(f, b); 189 | if (x < 0 || x >= 1024) wfset_freq(b, bi[b].zoom, f); 190 | } 191 | 192 | 193 | function dx(freq, mode, text) 194 | // called by updates fetched from the server 195 | { 196 | dxs.push({ freq: freq, mode: mode, text: text }); 197 | } 198 | 199 | function setfreqm(b, f, mo) { 200 | setband(b); 201 | set_mode(mo); 202 | if (iscw()) f -= (hi + lo) / 2; 203 | setfreq(f); 204 | } 205 | 206 | 207 | function showdx(b) { 208 | var s = ''; 209 | if (!hidedx) { 210 | var mems = memories.slice(); 211 | for (i = 0; i < mems.length; i++) mems[i].nr = i; 212 | mems.sort(function (a, b) { return a.nomfreq - b.nomfreq }); 213 | for (i = 0; i < dxs.length; i++) { 214 | var x = freq2x(dxs[i].freq, b); 215 | var nextx; 216 | if (x > 1024) break; 217 | if (i < dxs.length - 1) nextx = freq2x(dxs[i + 1].freq, b); 218 | else nextx = 1024; 219 | if (nextx >= 1024) nextx = 1280; 220 | if (x < 0) continue; 221 | var fr = dxs[i].freq; 222 | var mo = dxs[i].mode; 223 | s += '
'; 224 | s += '
' + dxs[i].text + '<\/div><\/div><\/div>'; 225 | s += '
<\/div>'; 226 | } 227 | for (i = 0; i < mems.length; i++) if (mems[i].band == b) { 228 | var x = freq2x(mems[i].nomfreq, b); 229 | var nextx; 230 | if (x > 1024) break; 231 | if (i < mems.length - 1) nextx = freq2x(mems[i + 1].nomfreq, b); 232 | else nextx = 1024; 233 | if (nextx >= 1024) nextx = 1280; 234 | if (x < 0) continue; 235 | var fr = mems[i].freq; 236 | var mo = mems[i].mode; 237 | s += '
'; 238 | var l = mems[i].label; 239 | if (!l || l == '') l = 'mem ' + mems[i].nr; 240 | s += '
' + l + '<\/div><\/div><\/div>'; 241 | s += '
<\/div>'; 242 | } 243 | } 244 | document.getElementById('blackbar' + band2id(b)).innerHTML = s; 245 | if (s != '') { 246 | document.getElementById('blackbar' + band2id(b)).style.height = '64px'; 247 | } else { 248 | document.getElementById('blackbar' + band2id(b)).style.height = '30px'; 249 | } 250 | } 251 | 252 | function fetchdx(b) { 253 | var xmlHttp; 254 | try { xmlHttp = new XMLHttpRequest(); } 255 | catch (e) { 256 | try { xmlHttp = new ActiveXObject("Msxml2.XMLHTTP"); } 257 | catch (e) { 258 | try { xmlHttp = new ActiveXObject("Microsoft.XMLHTTP"); } 259 | catch (e) { alert("Your browser does not support AJAX!"); return false; } 260 | } 261 | } 262 | xmlHttp.onreadystatechange = function () { 263 | if (xmlHttp.readyState == 4) { 264 | if (xmlHttp.responseText != "") { 265 | eval(xmlHttp.responseText); 266 | showdx(b); 267 | } 268 | } 269 | } 270 | var url = "/~~fetchdx?min=" + (bi[b].effcenterfreq - bi[b].effsamplerate / 2) + "&max=" + (bi[b].effcenterfreq + bi[b].effsamplerate / 2); 271 | xmlHttp.open("GET", url, true); 272 | xmlHttp.send(null); 273 | } 274 | 275 | 276 | function setscaleimgs(b, id) { 277 | var e = bi[b]; 278 | var st = e.start >> (e.maxzoom - e.zoom); 279 | if (st < 0) scaleimgs0[id].src = "scaleblack.png"; 280 | else scaleimgs0[id].src = e.scaleimgs[e.zoom][st >> 10]; 281 | if (e.scaleimgs[e.zoom][1 + (st >> 10)]) scaleimgs1[id].src = e.scaleimgs[e.zoom][1 + (st >> 10)]; 282 | else scaleimgs1[id].src = "scaleblack.png"; 283 | st += 1024; 284 | scaleimgs0[id].style.left = (-(st % 1024)) + "px"; 285 | scaleimgs1[id].style.left = (1024 - (st % 1024)) + "px"; 286 | } 287 | 288 | 289 | // this function is called from java when the scrollwheel is moved to change the zoom 290 | function zoomchange(id, zoom, start) { 291 | var b = id2band(id); 292 | var e = bi[b]; 293 | var oldzoom = e.zoom; 294 | e.effsamplerate = e.samplerate / (1 << zoom); 295 | e.effcenterfreq = e.centerfreq - e.samplerate / 2 + (start * (e.samplerate / (1 << e.maxzoom)) / 1024) + e.effsamplerate / 2; 296 | e.zoom = zoom; 297 | e.start = start; 298 | setscaleimgs(b, id); 299 | if (b == band) { 300 | khzperpixel = bi[band].effsamplerate / 1024; 301 | centerfreq = bi[band].effcenterfreq; 302 | updbw(); 303 | } 304 | if (!hidedx) { 305 | clearTimeout(band_fetchdxtimer[b]); 306 | if (zoom != oldzoom) { 307 | dxs = []; document.getElementById('blackbar' + id).innerHTML = ""; 308 | fetchdx(b); 309 | } else { 310 | { 311 | showdx(b); 312 | band_fetchdxtimer[b] = setTimeout('dxs=[]; fetchdx(' + b + ');', 400); 313 | } 314 | } 315 | } 316 | } 317 | 318 | 319 | var dont_update_textual_frequency = false; 320 | 321 | function setfreq(f) { 322 | try { clearTimeout(setfreqif_fut_timer); } catch (e) { }; 323 | freq = f; 324 | document.getElementById("dummyforie").style.display = 'none'; document.getElementById("dummyforie").style.display = 'block'; // utter nonsense, but forces IE8 to update the screen :( 325 | send_soundsettings_to_server(); 326 | if (view != Views.blind) draw_passband(); 327 | if (dont_update_textual_frequency) return; 328 | var nomfreq = nominalfreq(); 329 | if (freq.toFixed) document.freqform.frequency.value = nomfreq.toFixed(2); 330 | else document.freqform.frequency.value = nomfreq + " kHz"; 331 | } 332 | 333 | function setfreqb(f) 334 | // sets frequency but also autoselects band 335 | { 336 | if (iscw()) f -= (hi + lo) / 2; 337 | var e = bi[band]; 338 | if (f > e.centerfreq - e.samplerate / 2 - 4 && f < e.centerfreq + e.samplerate / 2 + 4) { 339 | // new frequency is in the current band 340 | setwaterfall(band, f); 341 | setfreq(f); 342 | return; 343 | } 344 | // new frequency is not in the current band: then search through all bands until we find the right one (if any) 345 | for (i = 0; i < nvbands; i++) { 346 | e = bi[i]; 347 | c = e.centerfreq; 348 | w = e.samplerate / 2 + 4; 349 | if (f > c - w && f < c + w) { 350 | e.vfo = f; 351 | setband(i); 352 | return; 353 | } 354 | } 355 | } 356 | 357 | 358 | 359 | function setfreqif(str) 360 | // called when frequency is entered textually 361 | { 362 | f = parseFloat(str); 363 | if (!(f > 0)) return; 364 | dont_update_textual_frequency = true; 365 | setfreqb(f); 366 | dont_update_textual_frequency = false; 367 | document.freqform.frequency.value = str; 368 | } 369 | 370 | function setfreqif_fut(str) 371 | // called when typing in the frequency field; schedules a frequency update in the future, in case no more key presses follow soon 372 | { 373 | try { clearTimeout(setfreqif_fut_timer); } catch (e) { }; 374 | setfreqif_fut_timer = setTimeout('setfreqif(' + str + ')', 1000); 375 | } 376 | 377 | 378 | function setmf(m, l, h) // "set mode and filter" 379 | { 380 | mode = m.toUpperCase(); 381 | lo = l; 382 | hi = h; 383 | updbw(); 384 | } 385 | 386 | function set_mode(m) // ...with appropriate filter 387 | { 388 | switch (m.toUpperCase()) { 389 | case "USB": setmf("usb", 0.0, 2.4); break; 390 | case "LSB": setmf("lsb", -2.4, -0.0); break; 391 | case "AM": setmf("am", -4, 4); break; 392 | case "CW": setmf("cw", -0.95, -0.55); break; 393 | case "FM": setmf("fm", -8, 8); break; 394 | } 395 | } 396 | 397 | 398 | function freqstep(st) 399 | // do a frequency step, suitable for the current mode 400 | // sign of st indicates direction 401 | // magnitude of st is 1,2 or 3 for small, medium, or large step, with large being one channel (where applicable) 402 | { 403 | var f = nominalfreq(); 404 | if (st == "9") { 405 | if (mode == "CW") { 406 | f = Math.round(f); 407 | setfreq(f - (hi + lo) / 2); 408 | } 409 | else { 410 | f = Math.round(f); 411 | setfreq(f); 412 | } 413 | } 414 | else { 415 | var steps_ssb = [0.1, 0.5, 1]; 416 | var steps_am5 = [0.1, 1, 5]; 417 | var steps_am9 = [0.1, 1, 10]; 418 | var steps_fm = [1, 5, 12.5]; 419 | var steps = steps_ssb; 420 | var grid = false; 421 | var i = Math.abs(st) - 1; 422 | } 423 | if (mode == "AM") { 424 | if (freq < 1800) steps = steps_am9; else steps = steps_am5; 425 | if (i >= 1) grid = true; 426 | } 427 | if (mode == "FM") { 428 | steps = steps_fm; 429 | if (i >= 1) grid = true; 430 | } 431 | var d = steps[i]; 432 | var f = (st > 0) ? f : -f; 433 | if (!grid) f = f + d; 434 | else f = d * Math.ceil(f / d + 0.1); 435 | f = (st > 0) ? f : -f; 436 | if (iscw()) f -= (hi + lo) / 2; 437 | setfreq(f); 438 | } 439 | 440 | function setfreqtune(s) { 441 | var param = new RegExp("([0-9.]*)([^&#]*)").exec(s); 442 | if (!param[1]) return; 443 | if (param[2]) set_mode(param[2]); 444 | setfreqif(param[1]); 445 | } 446 | 447 | 448 | 449 | function mem_recall(i) { 450 | setband(memories[i].band); 451 | mode = memories[i].mode; 452 | lo = memories[i].lo; 453 | hi = memories[i].hi; 454 | updbw(); 455 | setfreq(memories[i].freq); 456 | setwaterfall(band, memories[i].freq); 457 | } 458 | 459 | function mem_erase(i) { 460 | var b = memories[i].band; 461 | memories.splice(i, 1); 462 | mem_show(); 463 | showdx(b); 464 | try { localStorage.setItem('memories', JSON.stringify(memories)); } catch (e) { }; 465 | } 466 | 467 | function mem_store(i) { 468 | var nomf = nominalfreq(); 469 | var l; 470 | try { l = memories[i].label; } catch (e) { l = ''; }; 471 | memories[i] = { freq: freq, nomfreq: nomf, band: band, mode: mode, lo: lo, hi: hi, label: l }; 472 | mem_show(); 473 | showdx(memories[i].band); 474 | try { localStorage.setItem('memories', JSON.stringify(memories)); } catch (e) { }; 475 | } 476 | 477 | function mem_label(i, nw) { 478 | memories[i].label = nw; 479 | showdx(memories[i].band); 480 | try { localStorage.setItem('memories', JSON.stringify(memories)); } catch (e) { }; 481 | } 482 | 483 | function mem_show() { 484 | var i; 485 | var s = ""; 486 | for (i = 0; i < memories.length; i++) { 487 | var m = ""; 488 | m = memories[i].mode; 489 | s += ''; 490 | s += ''; 491 | s += '' + memories[i].nomfreq.toFixed(2) + ' kHz ' + m + ''; 492 | s += ''; 493 | s += ''; 494 | } 495 | s += ''; 496 | s += ''; 497 | s += '(new)'; 498 | s += ''; 499 | document.getElementById('memories').innerHTML = '' + s + '
'; 500 | } 501 | 502 | 503 | 504 | function wfset_freq(b, zoom, f) { 505 | // set waterfall for band 'b' to given zoomlevel and centerfrequency 506 | var id = band2id(b); 507 | var e = bi[b]; 508 | var effsamplerate = e.samplerate / (1 << zoom); 509 | var start = (f - e.centerfreq + e.samplerate / 2 - effsamplerate / 2) * 1024 / (e.samplerate / (1 << e.maxzoom)); 510 | waterfallapplet[id].setzoom(zoom, start); 511 | timeout_idle_restart() 512 | } 513 | 514 | function wfset(cmd) { 515 | var b = band; 516 | var e = bi[b]; 517 | var id = band2id(b); 518 | timeout_idle_restart() 519 | if (cmd == 0) { 520 | // zoom in 521 | //var x=(freq-centerfreq)/khzperpixel+512; 522 | var x = 512; 523 | waterfallapplet[id].setzoom(-2, x); 524 | return; 525 | } 526 | if (cmd == 1) { 527 | // zoom out 528 | //var x=(freq-centerfreq)/khzperpixel+512; 529 | var x = 512; 530 | waterfallapplet[id].setzoom(-1, x); 531 | return; 532 | } 533 | if (cmd == 2) { 534 | // zoom in deep with current listening frequency centered 535 | wfset_freq(b, e.maxzoom, freq); 536 | } 537 | if (cmd == 4) { 538 | // zoom fully out 539 | waterfallapplet[id].setzoom(0, 0); 540 | } 541 | } 542 | 543 | 544 | function setview(v) { 545 | timeout_idle_restart() 546 | if ((v == Views.allbands && view == Views.othersslow) || (view == Views.allbands && v == Views.othersslow)) { 547 | // no need to restart the applets in this case 548 | view = v; 549 | createCookie("view", view, 3652); 550 | waterfallspeed(waterslowness); 551 | return; 552 | } 553 | 554 | if (view == Views.blind) { 555 | var els = document.getElementsByTagName('*'); 556 | for (i = 0; i < els.length; i++) { 557 | if (els[i].className == "hideblind") els[i].style.display = "inline"; 558 | if (els[i].className == "showblind") els[i].style.display = "none"; 559 | } 560 | } 561 | for (i = 0; i < nwaterfalls; i++) waterfallapplet[i].destroy(); 562 | 563 | view = v; 564 | createCookie("view", view, 3652); 565 | 566 | document_waterfalls(); // (re)start the waterfall applets 567 | 568 | if (view == Views.blind) { 569 | var els = document.getElementsByTagName('*'); 570 | for (i = 0; i < els.length; i++) { 571 | if (els[i].className == "showblind") els[i].style.display = "inline"; 572 | if (els[i].className == "hideblind") els[i].style.display = "none"; 573 | } 574 | return; 575 | } 576 | 577 | sethidedx(hidedx); 578 | } 579 | 580 | 581 | function islsbband(b) { 582 | // returns true if default SSB mode for this band should be LSB 583 | var e = bi[b]; 584 | if (e.centerfreq > 3500 && e.centerfreq < 4000) return 1; 585 | if (e.centerfreq > 1800 && e.centerfreq < 2000) return 1; 586 | if (e.centerfreq > 7000 && e.centerfreq < 7400) return 1; 587 | return 0; 588 | } 589 | 590 | function setband(b) { 591 | if (b < 0 || b >= nvbands) return; 592 | bi[band].vfo = freq; 593 | 594 | if (islsbband(band) != islsbband(b)) { 595 | // if needed, exchange LSB/USB 596 | var tmp = hi; 597 | hi = -lo; 598 | lo = -tmp; 599 | if (mode == "USB") mode = "LSB"; 600 | else if (mode == "LSB") mode = "USB"; 601 | } 602 | 603 | band = b; 604 | var e = bi[b]; 605 | if (nbands > 1) document.freqform.group0[band].checked = true; 606 | if (view == Views.allbands || view == Views.othersslow) { 607 | scaleobj = scaleobjs[b]; 608 | } else if (view == Views.oneband) { 609 | scaleobj = scaleobjs[0]; 610 | setscaleimgs(b, 0); 611 | if (waitingforwaterfalls == 0) waterfallapplet[0].setband(b, e.maxzoom, e.zoom, e.start); 612 | if (!hidedx) { 613 | clearTimeout(band_fetchdxtimer[b]); 614 | dxs = []; document.getElementById('blackbar0').innerHTML = ""; 615 | fetchdx(b); 616 | } 617 | } 618 | setwaterfall(b, e.vfo); 619 | centerfreq = e.effcenterfreq; 620 | khzperpixel = e.effsamplerate / 1024; 621 | setfreq(e.vfo); 622 | waterfallspeed(waterslowness); 623 | } 624 | 625 | 626 | function sethidedx(h) { 627 | hidedx = h; 628 | if (view == Views.oneband) { 629 | if (hidedx) { 630 | dxs = []; document.getElementById('blackbar0').innerHTML = ""; 631 | clearTimeout(band_fetchdxtimer[band]); 632 | document.getElementById('blackbar0').style.height = '30px'; 633 | } else { 634 | showdx(band); 635 | fetchdx(band); 636 | } 637 | } else { 638 | for (b = 0; b < nvbands; b++) { 639 | if (hidedx) { 640 | dxs = []; document.getElementById('blackbar' + band2id(b)).innerHTML = ""; 641 | clearTimeout(band_fetchdxtimer[b]); 642 | document.getElementById('blackbar' + band2id(b)).style.height = '30px'; 643 | } else { 644 | showdx(b); 645 | fetchdx(b); 646 | } 647 | } 648 | } 649 | } 650 | 651 | 652 | function test_serverbusy() { 653 | try { soundapplet.app.l = 1; } catch (e) { }; 654 | try { serveravailable = soundapplet.getid(); } catch (e) { }; 655 | if (serveravailable == 0) { 656 | try { clearInterval(interval_updatesmeter); } catch (e) { }; 657 | try { clearTimeout(interval_ajax3); } catch (e) { }; 658 | var i; 659 | try { for (i = 0; i < nwaterfalls; i++) waterfallapplet[i].destroy(); } catch (e) { }; 660 | try { soundapplet.destroy(); } catch (e) { }; 661 | document.body.innerHTML = "Sorry, the WebSDR server is too busy right now; please try again later.\n"; 662 | } 663 | } 664 | 665 | 666 | var sgraph = { 667 | prevt: 0, 668 | e0: 80, // current lower end of scale 669 | e1: -190, // current upper end of scale 670 | d0: 80, // current estimate of lowest value of interest 671 | d1: -190, // current estimate of highest value of interest 672 | width: 200, 673 | cnt: 0 674 | }; 675 | 676 | function s2y(s) { 677 | return sgraph.cv.height - (s - sgraph.e0) / (sgraph.e1 - sgraph.e0) * sgraph.cv.height; 678 | } 679 | 680 | function updatesmeter() { 681 | if (!allloadeddone) return; 682 | 683 | try { 684 | var s = soundapplet.smeter(); 685 | } catch (e) { s = 0; }; 686 | var c = '' + (s / 100.0 - 127).toFixed(1); 687 | if (c.length < 6) c = '  ' + c; 688 | numericalsmeterobj.innerHTML = c; 689 | if (s >= 0) smeterobj.style.width = s * 0.0191667 + "px"; 690 | else smeterobj.style.width = "0px"; 691 | smeterpeaktimer--; 692 | if ((smeterpeak < s - 0.1) || (smeterpeaktimer <= 0)) { 693 | smeterpeak = s; 694 | smeterpeaktimer = 10; 695 | if (smeterpeak >= 0) smeterpeakobj.style.width = smeterpeak * 0.0191667 + "px"; 696 | else smeterpeakobj.style.width = "0px"; 697 | var c = '' + (s / 100.0 - 127).toFixed(1); 698 | if (c.length < 6) c = '  ' + c; 699 | numericalsmeterpeakobj.innerHTML = c; 700 | } 701 | 702 | if (serveravailable < 0) test_serverbusy(); 703 | 704 | // rest of this function is for drawing the signal strength plot 705 | 706 | var v = document.getElementById('sgraphchoice').value; 707 | if (!(v > 0)) { 708 | if (sgraph.cv) { 709 | sgraph.ct.clearRect(0, 0, sgraph.cv.width, sgraph.cv.height); 710 | sgraph.cv.style.display = 'none'; 711 | sgraph.cv = null; 712 | sgraph.e0 = 80; 713 | sgraph.e1 = -190; 714 | } 715 | return; 716 | } 717 | 718 | if (!sgraph.cv) { 719 | sgraph.cv = document.getElementById('sgraph'); 720 | sgraph.cv.style.display = ''; 721 | sgraph.ct = sgraph.cv.getContext("2d"); 722 | } 723 | var cv = sgraph.cv; 724 | var ct = sgraph.ct; 725 | sgraph.width = cv.width - 50; 726 | 727 | s = s / 100.0 - 127; 728 | // try to estimate the useful range of values, without storing all datapoints, and rescale the plot if needed 729 | if (sgraph.d0 > s) sgraph.d0 = s; else sgraph.d0 += 0.1 / v; 730 | if (sgraph.d1 < s) sgraph.d1 = s; else sgraph.d1 -= 0.1 / v; 731 | var redrawaxis = 0; 732 | if (sgraph.d0 > sgraph.e0 + 15 || sgraph.d0 < sgraph.e0) { 733 | var e0 = 10 * Math.floor(sgraph.d0 / 10) - 5; 734 | if (e0 > sgraph.e0) ct.drawImage(cv, 0, 0, sgraph.width, cv.height * (sgraph.e1 - e0) / (sgraph.e1 - sgraph.e0), 0, 0, sgraph.width, cv.height); 735 | else { 736 | var f = (sgraph.e1 - sgraph.e0) / (sgraph.e1 - e0); 737 | ct.drawImage(cv, 0, 0, sgraph.width, cv.height, 0, 0, sgraph.width, cv.height * f); 738 | ct.fillStyle = "white"; 739 | ct.fillRect(0, Math.floor(cv.height * f), sgraph.width, cv.height * (1 - f) + 1); 740 | } 741 | sgraph.e0 = e0; 742 | redrawaxis = 1; 743 | } 744 | if (sgraph.d1 > sgraph.e1 || sgraph.d1 < sgraph.e1 - 15) { 745 | var e1 = 10 * Math.ceil(sgraph.d1 / 10) + 5; 746 | if (e1 < sgraph.e1) { 747 | var f = (e1 - sgraph.e0) / (sgraph.e1 - sgraph.e0); 748 | if (f < 0) f = 0; 749 | ct.drawImage(cv, 0, cv.height * (1 - f), sgraph.width, cv.height * f, 0, 0, sgraph.width, cv.height); 750 | } else { 751 | var f = (sgraph.e1 - sgraph.e0) / (e1 - sgraph.e0); 752 | if (f < 0) f = 0; 753 | ct.drawImage(cv, 0, 0, sgraph.width, cv.height, 0, cv.height * (1 - f), sgraph.width, cv.height * f); 754 | ct.fillStyle = "white"; 755 | ct.fillRect(0, 0, sgraph.width, Math.ceil(cv.height * (1 - f))); 756 | } 757 | sgraph.e1 = e1; 758 | redrawaxis = 1; 759 | } 760 | if (redrawaxis) { 761 | ct.clearRect(sgraph.width, 0, cv.width - sgraph.width, cv.height); 762 | var w = sgraph.e0; 763 | ct.fillStyle = "black"; 764 | ct.font = "10px Verdana"; 765 | while ((w = 10 * Math.ceil(w / 10)) <= sgraph.e1) { 766 | var y = s2y(w); 767 | ct.fillText(w + " dB", sgraph.width + 2, y + 4, cv.width - sgraph.width); 768 | w += 1; 769 | } 770 | } 771 | 772 | sgraph.cnt++; 773 | if (sgraph.cnt >= v) { 774 | sgraph.cnt = 0; 775 | ct.drawImage(cv, 1, 0, sgraph.width - 1, cv.height, 0, 0, sgraph.width - 1, cv.height); // move the plot one pixel to the left 776 | var t = new Date().getTime(); 777 | if (v >= 10) v = 60; 778 | if (Math.floor(t / 1000 / v) != Math.floor(sgraph.prevt / 1000 / v)) { 779 | // draw grey vertical line as time marker 780 | ct.fillStyle = "rgba(210,210,210,1)"; 781 | ct.fillRect(sgraph.width - 1, 0, 1, cv.height); 782 | sgraph.prevt = t; 783 | } else { 784 | // draw white vertical line with grey dB scale markers 785 | ct.fillStyle = "white"; 786 | ct.fillRect(sgraph.width - 1, 0, 1, cv.height); 787 | ct.fillStyle = "rgba(210,210,210,1)"; 788 | var w = sgraph.e0; 789 | while ((w = 10 * Math.ceil(w / 10)) <= sgraph.e1) { 790 | var y = s2y(w); 791 | ct.fillRect(sgraph.width - 1, y, 1, 1); 792 | w += 1; 793 | } 794 | } 795 | } 796 | 797 | // plot the actual data point 798 | ct.fillStyle = "blue"; 799 | ct.fillRect(sgraph.width - 1, s2y(s), 1, 1); 800 | } 801 | 802 | 803 | var uu_names = new Array(); 804 | var uu_bands = new Array(); 805 | var uu_freqs = new Array(); 806 | var others_colours = ["#ff4040", "#ffa000", "#a0a000", "#80ff00", "#00ff00", "#00a0a0", "#0080ff", "#ff40ff"]; 807 | 808 | var dxs = []; 809 | 810 | function uu(i, username, band, freq) 811 | // called by updates fetched from the server 812 | { 813 | uu_names[i] = username; 814 | uu_bands[i] = band; 815 | uu_freqs[i] = freq; 816 | } 817 | 818 | var uu_compactview = false; 819 | function douu() 820 | // draw the diagram that shows the other listeners 821 | { 822 | s = ''; 823 | total = 0; 824 | for (b = 0; b < nbands; b++) { 825 | if (!uu_compactview) { 826 | s += "

"; 827 | for (i = 0; i < uu_names.length; i++) if (uu_bands[i] == b && uu_names[i] != "") { 828 | s += "
" + uu_names[i] + "
"; 831 | total++; 832 | } 833 | s += "

"; 834 | } else { 835 | s += "

"; 836 | for (i = 0; i < uu_names.length; i++) if (uu_bands[i] == b && uu_names[i] != "") { 837 | s += "
"; 840 | total++; 841 | } 842 | s += "

"; 843 | } 844 | } 845 | usersobj.innerHTML = s; 846 | numusersobj.innerHTML = total; 847 | } 848 | 849 | function setcompactview(c) { 850 | uu_compactview = c; 851 | douu(); 852 | } 853 | 854 | 855 | function ajaxFunction3() { 856 | var xmlHttp; 857 | try { xmlHttp = new XMLHttpRequest(); } 858 | catch (e) { 859 | try { xmlHttp = new ActiveXObject("Msxml2.XMLHTTP"); } 860 | catch (e) { 861 | try { xmlHttp = new ActiveXObject("Microsoft.XMLHTTP"); } 862 | catch (e) { alert("Your browser does not support AJAX!"); return false; } 863 | } 864 | } 865 | xmlHttp.onreadystatechange = function () { 866 | if (xmlHttp.readyState == 4) { 867 | if (xmlHttp.status == 200 && xmlHttp.responseText != "") { 868 | eval(xmlHttp.responseText); 869 | douu(); 870 | } 871 | clearTimeout(interval_ajax3); 872 | interval_ajax3 = setTimeout('ajaxFunction3()', 1000); 873 | } 874 | } 875 | interval_ajax3 = setTimeout('ajaxFunction3()', 120000); 876 | var url = "/~~othersjj?chseq=" + chseq; 877 | xmlHttp.open("GET", url, true); 878 | xmlHttp.send(null); 879 | } 880 | 881 | 882 | 883 | function javatest() { 884 | var javaversion; 885 | try { 886 | javaversion = soundapplet.javaversion(); 887 | } catch (err) { 888 | javaerr = 1; 889 | if (!usejavasound) return; 890 | document.getElementById("javawarning").style.display = "block"; 891 | javaversion = "999"; 892 | setTimeout('javatest()', 1000); // in case loading java was simply taking too long 893 | } 894 | if (javaversion < "1.4.2") { 895 | document.getElementById("javawarning").innerHTML = 'Your Java version is ' + javaversion + ', which is too old for the WebSDR. Please install version 1.4.2 or newer, e.g. from http://www.java.com if you hear no sound.'; 896 | document.getElementById("javawarning").style.display = "block"; 897 | } 898 | } 899 | 900 | 901 | 902 | function updbw() { 903 | if (lo > hi) { 904 | if (document.onmousemove == useMouseXYloweredge || touchingLower) lo = hi; 905 | else hi = lo; 906 | } 907 | var maxf = (mode == "FM") ? 15 : (bandinfo[band].maxlinbw * 0.95); 908 | if (lo < -maxf) lo = -maxf; 909 | if (hi > maxf) hi = maxf; 910 | var x6 = document.getElementById('numericalbandwidth6'); 911 | var x60 = document.getElementById('numericalbandwidth60'); 912 | x6.innerHTML = (hi - lo + 0.091).toFixed(2); 913 | x60.innerHTML = (hi - lo + 0.551).toFixed(2); 914 | setfreq(freq); 915 | } 916 | 917 | 918 | // from http://www.quirksmode.org/js/cookies.html 919 | function createCookie(name, value, days) { 920 | if (days) { 921 | var date = new Date(); 922 | date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000)); 923 | var expires = "; expires=" + date.toGMTString(); 924 | } 925 | else var expires = ""; 926 | document.cookie = name + "=" + value + expires + "; path=/"; 927 | } 928 | 929 | function readCookie(name) { 930 | var nameEQ = name + "="; 931 | var ca = document.cookie.split(';'); 932 | for (var i = 0; i < ca.length; i++) { 933 | var c = ca[i]; 934 | while (c.charAt(0) == ' ') c = c.substring(1, c.length); 935 | if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length, c.length); 936 | } 937 | return null; 938 | } 939 | 940 | 941 | function id2band(id) { 942 | if (view == Views.oneband) return band; else return id; 943 | } 944 | 945 | function band2id(b) { 946 | if (view == Views.oneband) return 0; else return b; 947 | } 948 | 949 | function waterfallspeed(sp) { 950 | waterslowness = sp; 951 | if (waitingforwaterfalls > 0) return; 952 | var done = 0; 953 | if (view == Views.othersslow) { 954 | for (i = 0; i < nwaterfalls; i++) 955 | if (i == band) waterfallapplet[i].setslow(sp); 956 | else waterfallapplet[i].setslow(100); 957 | } else { 958 | for (i = 0; i < nwaterfalls; i++) 959 | waterfallapplet[i].setslow(sp); 960 | } 961 | } 962 | 963 | function waterfallheight(si) { 964 | waterheight = si; 965 | if (waitingforwaterfalls > 0) return; 966 | for (i = 0; i < nwaterfalls; i++) { 967 | waterfallapplet[i].setSize(1024, si); 968 | } 969 | 970 | var y = scaleobj.offsetTop + 15; 971 | passbandobj.style.top = y + "px"; 972 | edgelowerobj.style.top = y + "px"; 973 | edgeupperobj.style.top = y + "px"; 974 | carrierobj.style.top = y + "px"; 975 | } 976 | 977 | function waterfallmode(m) { 978 | watermode = m; 979 | if (waitingforwaterfalls > 0) return; 980 | for (i = 0; i < nwaterfalls; i++) { 981 | waterfallapplet[i].setmode(m); 982 | } 983 | } 984 | 985 | 986 | 987 | function soundappletstarted() { 988 | if (usejavasound && javaerr) { 989 | javaerr = 0; 990 | document.getElementById("javawarning").style.display = "none"; 991 | } 992 | setTimeout('soundappletstarted2()', 100); 993 | } 994 | 995 | function soundappletstarted2() { 996 | allloadeddone = true; 997 | 998 | soundapplet.setvolume(Math.pow(10, document.getElementById('volumecontrol2').value / 10.)); 999 | 1000 | if (bi[0]) { 1001 | setfreqif(freq); 1002 | updbw(); 1003 | } 1004 | 1005 | try { setmute(document.getElementById('mutecheckbox').checked) } catch (e) { }; 1006 | try { setsquelch(document.getElementById('squelchcheckbox').checked) } catch (e) { }; 1007 | try { setautonotch(document.getElementById('autonotchcheckbox').checked) } catch (e) { }; 1008 | 1009 | test_serverbusy(); 1010 | } 1011 | 1012 | 1013 | function waterfallappletstarted(id) { 1014 | // this function is called when a waterfall applet becomes active 1015 | waitingforwaterfalls--; 1016 | if (waitingforwaterfalls < 0) waitingforwaterfalls = 0; // shouldn't happen... 1017 | if (waitingforwaterfalls != 0) return; 1018 | setTimeout('allwaterfallappletsstarted()', 100); 1019 | 1020 | waterfallapplet[0].setzoom(-2, 371); 1021 | waterfallapplet[0].setzoom(-2, 512); 1022 | } 1023 | 1024 | function allwaterfallappletsstarted() { 1025 | var i; 1026 | 1027 | waterfallspeed(waterslowness); 1028 | waterfallmode(watermode); 1029 | 1030 | for (i = 0; i < nwaterfalls; i++) { 1031 | var e = bi[i]; 1032 | waterfallapplet[i].setband(e.realband, e.maxzoom, e.zoom, e.start); 1033 | } 1034 | if (view == Views.oneband) { 1035 | var e = bi[band]; 1036 | waterfallapplet[0].setband(band, e.maxzoom, e.zoom, e.start); 1037 | } 1038 | 1039 | // and when the applets run, we can also be sure that the HTML elements for the frequency scale have been rendered: 1040 | for (i = 0; i < nwaterfalls; i++) { 1041 | scaleobjs[i] = document.getElementById('clipscale' + i); 1042 | scaleimgs0[i] = document.images["s0cale" + i]; 1043 | scaleimgs1[i] = document.images["s1cale" + i]; 1044 | } 1045 | if (view == Views.oneband) { 1046 | setscaleimgs(band, 0); 1047 | scaleobj = scaleobjs[0]; 1048 | } else { 1049 | for (i = 0; i < nwaterfalls; i++) setscaleimgs(i, i); 1050 | scaleobj = scaleobjs[band]; 1051 | } 1052 | draw_passband(); 1053 | } 1054 | 1055 | var sup_socket = !!window.WebSocket && !!WebSocket.CLOSING; // the CLOSING test excludes browsers with an old version of the websocket protocol, in particular Safari 5 1056 | var sup_canvas = !!window.CanvasRenderingContext2D; 1057 | var sup_webaudio = window.AudioContext || window.webkitAudioContext; 1058 | var sup_mozaudio = false; 1059 | try { if (typeof (Audio) === 'function' && typeof (new Audio().mozSetup) == 'function') sup_mozaudio = true; } catch (e) { }; 1060 | 1061 | function html5javawarn() { 1062 | // show warning regarding support for HTML5 or Java if needed 1063 | document.getElementById("javawarning").style.display = (usejavasound && javaerr) ? "block" : "none"; 1064 | document.getElementById("html5warning").style.display = (!usejavasound && !sup_webaudio && !sup_mozaudio) ? "block" : "none"; 1065 | } 1066 | 1067 | 1068 | function html5orjava(item, usejava) { 1069 | if (item == 0) { 1070 | // waterfall 1071 | if (usejavawaterfall == usejava) return; 1072 | usejavawaterfall = usejava; 1073 | var s = (usejavawaterfall ? "y" : "n") + (usejavasound ? "y" : "n"); 1074 | createCookie("usejava", s, 3652); 1075 | var i; 1076 | try { for (i = 0; i < nwaterfalls; i++) waterfallapplet[i].destroy(); } catch (e) { }; 1077 | document_waterfalls(); 1078 | } 1079 | if (item == 1) { 1080 | // sound 1081 | if (usejavasound == usejava) return; 1082 | usejavasound = usejava; 1083 | var s = (usejavawaterfall ? "y" : "n") + (usejavasound ? "y" : "n"); 1084 | createCookie("usejava", s, 3652); 1085 | try { soundapplet.destroy(); } catch (e) { }; 1086 | document_soundapplet(); 1087 | document.getElementById('record_span').style.display = usejavasound ? "none" : "inline"; 1088 | html5javawarn(); 1089 | } 1090 | } 1091 | 1092 | function checkjava() { 1093 | try { 1094 | if (navigator.javaEnabled && navigator.javaEnabled()) return "green"; 1095 | } catch (e) { }; 1096 | try { 1097 | var m = navigator.mimeTypes; 1098 | for (i = 0; i < m.length; i++) 1099 | if (m[i].type.match(/^application\/x-java-applet/)) return "green"; 1100 | return "red"; 1101 | } catch (e) { }; 1102 | return "black"; 1103 | } 1104 | 1105 | function iOS_audio_start() { 1106 | // Safari on iOS only plays webaudio after it has been started by clicking a button, so this function must be called from a button's onclick handler 1107 | if (!document.ct) document.ct = new webkitAudioContext(); 1108 | var s = document.ct.createBufferSource(); 1109 | s.connect(document.ct.destination); 1110 | try { s.start(0); } catch (e) { s.noteOn(0); } 1111 | } 1112 | 1113 | function chrome_audio_start() { 1114 | // Chrome only plays webaudio after it has been started by clicking a button, so this function must be called from a button's onclick handler 1115 | // Source: https://www.sdrutah.org/info/chrome_WebSDR_fix.html 1116 | if (!document.ct) document.ct = new webkitAudioContext(); 1117 | var s = document.ct.createBufferSource(); 1118 | s.connect(document.ct.destination); 1119 | document.ct.resume(); 1120 | try { s.start(0); } catch (e) { s.noteOn(0); } 1121 | } 1122 | 1123 | function html5orjavamenu() { 1124 | var s; 1125 | if (sup_webaudio) { 1126 | if (sup_webaudio) { 1127 | if (!document['ct']) document['ct'] = new sup_webaudio; 1128 | try { 1129 | var cc = document['ct'].createConvolver; 1130 | } catch (e) { 1131 | document['ct'] = null; // firefox 23 supports webaudio, but not yet createConvolver(), making it unusable. 1132 | sup_webaudio = false; 1133 | }; 1134 | } 1135 | } 1136 | sup_iOS = 0; // global! 1137 | sup_android = 0; // global! 1138 | sup_chrome = 0; // global! 1139 | try { 1140 | var n = navigator.userAgent.toLowerCase(); 1141 | if (n.indexOf('iphone') != -1) sup_iOS = 1; 1142 | if (n.indexOf('ipad') != -1) sup_iOS = 1; 1143 | if (n.indexOf('ipod') != -1) sup_iOS = 1; 1144 | if (n.indexOf('ios') != -1) sup_iOS = 1; 1145 | if (n.indexOf('android') != -1) sup_android = 1; 1146 | if (n.indexOf('chrome') != -1) sup_chrome = 1; 1147 | } catch (e) { }; 1148 | if (sup_iOS) isTouchDev = true; 1149 | var usecookie = readCookie('usejava'); 1150 | if (!usecookie) { 1151 | if (sup_socket && sup_canvas) usecookie = "n"; else usecookie = "y"; 1152 | if (sup_socket && (sup_webaudio || sup_mozaudio)) usecookie += "n"; else usecookie += "y"; 1153 | } 1154 | usejavawaterfall = (usecookie.substring(0, 1) == 'y'); 1155 | usejavasound = (usecookie.substring(1, 2) == 'y'); 1156 | 1157 | var javacolor = checkjava(); 1158 | s = 'Cascada:'; 1159 | s += 'Java'; 1160 | if (sup_socket && sup_canvas) s += ''; else s += ''; 1161 | s += 'HTML5'; 1162 | s += '   Sonido:'; 1163 | s += 'Java'; 1164 | if (sup_socket && sup_webaudio) s += ''; 1165 | else if (sup_socket && sup_mozaudio) s += ''; 1166 | else s += ''; 1167 | s += 'HTML5'; 1168 | if (sup_iOS && sup_socket && sup_webaudio) s += ''; 1169 | // if (sup_chrome && sup_socket && sup_webaudio) s+=''; 1170 | document.getElementById('html5choice').innerHTML = s; 1171 | document.getElementById('record_span').style.display = usejavasound ? "none" : "inline"; 1172 | } 1173 | 1174 | 1175 | function bodyonload() { 1176 | var s; 1177 | 1178 | html5orjavamenu(); 1179 | if ((sup_iOS || sup_android) && has_mobile) document.getElementById("mobilewarning").style.display = "block"; 1180 | 1181 | view = readCookie('view'); 1182 | if (view == null) view = Views.oneband; 1183 | if (nvbands >= 2) s = 'all bandsothers slowone band'; 1184 | else { 1185 | s = 'cascada'; 1186 | if (view == Views.othersslow || view == Views.allbands) view = Views.oneband; 1187 | } 1188 | s += 'apagado'; 1189 | document.getElementById('viewformbuttons').innerHTML = s; 1190 | if (nvbands >= 2) document.viewform.group[view].checked = true; 1191 | else document.viewform.group[view - 2].checked = true; 1192 | 1193 | var x = readCookie('username'); 1194 | var p = document.getElementById("please2"); 1195 | if (!x && p) p.innerHTML = "Please type a name or callsign in the box at the top of the page to identify your chat messages!"; 1196 | 1197 | uu_compactview = document.getElementById("compactviewcheckbox").checked; 1198 | document.getElementById("mutecheckbox").checked = false; 1199 | document.getElementById("squelchcheckbox").checked = false; 1200 | document.getElementById("autonotchcheckbox").checked = false; 1201 | 1202 | try { memories = JSON.parse(localStorage.getItem('memories')); } catch (e) { }; 1203 | if (!memories) memories = []; 1204 | else { 1205 | // conversion from old data format - should be removed later 1206 | var rew = false; 1207 | for (i = 0; i < memories.length; i++) { 1208 | if (memories[i].mode == 1) { memories[i].mode = "AM"; rew = true; } 1209 | if (memories[i].mode == 4) { memories[i].mode = "FM"; rew = true; } 1210 | if (memories[i].mode == 0) { 1211 | rew = true; 1212 | if (memories[i].hi - memories[i].lo < 1) memories[i].mode = "CW"; 1213 | else if (memories[i].hi + memories[i].lo > 0) memories[i].mode = "USB"; 1214 | else memories[i].mode = "LSB"; 1215 | } 1216 | if (!memories[i].nomfreq) memories[i].nomfreq = memories[i].freq + (memories[i].mode == "CW" ? 0.75 : 0); 1217 | } 1218 | if (rew) try { localStorage.setItem('memories', JSON.stringify(memories)); } catch (e) { }; 1219 | } 1220 | 1221 | passbandobj = document.getElementById('yellowbar'); 1222 | edgeupperobj = document.getElementById('edgeupper'); 1223 | edgelowerobj = document.getElementById('edgelower'); 1224 | edgeupperobj = document.getElementById('edgeupper'); 1225 | carrierobj = document.getElementById('carrier'); 1226 | smeterobj = document.getElementById('smeterbar'); 1227 | numericalsmeterobj = document.getElementById('numericalsmeter'); 1228 | smeterpeakobj = document.getElementById('smeterpeak'); 1229 | numericalsmeterpeakobj = document.getElementById('numericalsmeterpeak'); 1230 | smeterobj.style.top = smeterpeakobj.style.top; 1231 | smeterobj.style.left = smeterpeakobj.style.left; 1232 | 1233 | mem_show(); 1234 | 1235 | bi = bandinfo; 1236 | for (i = 0; i < nbands; i++) { 1237 | var e = bi[i]; 1238 | e.realband = i; 1239 | e.effcenterfreq = e.centerfreq; 1240 | e.effsamplerate = e.samplerate; 1241 | e.zoom = 0; 1242 | e.start = 0; 1243 | e.minzoom = 0; 1244 | } 1245 | 1246 | document.freqform.frequency.value = freq; 1247 | if (nbands > 1) document.freqform.group0[0].checked = true; 1248 | 1249 | html5javawarn(); 1250 | 1251 | chatboxobj = document.getElementById('chatbox'); 1252 | 1253 | statsobj = document.getElementById('stats'); 1254 | numusersobj = document.getElementById('numusers'); 1255 | usersobj = document.getElementById('users'); 1256 | 1257 | setview(view); 1258 | 1259 | if (!islsbband(band) && hi < 0) { var tmp = hi; hi = -lo; lo = -tmp; mode = "USB"; } 1260 | var tuneparam = (new RegExp("[?&]tune=([^&#]*)").exec(window.location.href)); 1261 | if (tuneparam) { 1262 | setfreqtune(tuneparam[1]); 1263 | } else if (ini_freq && ini_mode) { 1264 | setfreqif(ini_freq); 1265 | set_mode(ini_mode); 1266 | } 1267 | 1268 | document_soundapplet(); 1269 | 1270 | interval_ajax3 = setTimeout('ajaxFunction3()', 1000); 1271 | 1272 | setTimeout('javatest()', 2000); 1273 | 1274 | interval_updatesmeter = setInterval('updatesmeter()', 100); 1275 | 1276 | if (isTouchDev) { 1277 | registerTouchEvents("carrier", touchpassband, touchXYpassband); 1278 | registerTouchEvents("yellowbar", touchpassband, touchXYpassband); 1279 | registerTouchEvents("edgeupper", touchupper, touchXYupperedge); 1280 | registerTouchEvents("edgelower", touchlower, touchXYloweredge); 1281 | } 1282 | } 1283 | 1284 | function registerTouchEvents(id, touchStart, touchMove) { 1285 | var elem = document.getElementById(id); 1286 | elem.addEventListener('touchstart', touchStart); 1287 | elem.addEventListener('touchmove', touchMove); 1288 | elem.addEventListener('touchend', touchEnd); 1289 | } 1290 | 1291 | function setusernamecookie() { 1292 | createCookie('username', document.usernameform.username.value, 365 * 5); 1293 | var p = document.getElementById("please1"); 1294 | if (p) p.innerHTML = "Your name or callsign: "; 1295 | p = document.getElementById("please2"); 1296 | if (p) p.innerHTML = ""; 1297 | send_soundsettings_to_server(); 1298 | } 1299 | 1300 | //---------------------------------------------------------------------------------------- 1301 | // things related to interaction with the mouse (clicking & dragging on the frequency axes) 1302 | 1303 | var dragging = false; 1304 | var dragorigX; 1305 | var dragorigval; 1306 | var touchingLower = false; 1307 | 1308 | function getMouseXY(e) { 1309 | e = e || window.event; 1310 | if (e.pageX || e.pageY) return { x: e.pageX, y: e.pageY }; 1311 | return { 1312 | x: e.clientX + document.body.scrollLeft - document.body.clientLeft, 1313 | y: e.clientY + document.body.scrollTop - document.body.clientTop 1314 | }; 1315 | // from: http://www.webreference.com/programming/javascript/mk/column2/ 1316 | } 1317 | 1318 | 1319 | function useMouseXY(e) { 1320 | var pos = getMouseXY(e); 1321 | setfreq((pos.x - scaleobj.offsetParent.offsetLeft - 512) * khzperpixel + centerfreq - (hi + lo) / 2); 1322 | return cancelEvent(e); 1323 | } 1324 | 1325 | function touchXY(ev) { 1326 | ev.preventDefault(); 1327 | for (var i = 0; i < ev.touches.length; i++) { 1328 | var x = ev.touches[i].pageX; 1329 | setfreq((x - scaleobj.offsetParent.offsetLeft - 512) * khzperpixel + centerfreq - (hi + lo) / 2); 1330 | } 1331 | } 1332 | 1333 | function useMouseXYloweredge(e) { 1334 | var pos = getMouseXY(e); 1335 | lo = dragorigval + (pos.x - dragorigX) * khzperpixel; 1336 | updbw(); 1337 | return cancelEvent(e); 1338 | } 1339 | 1340 | function touchXYloweredge(ev) { 1341 | ev.preventDefault(); 1342 | for (var i = 0; i < ev.touches.length; i++) { 1343 | var x = ev.touches[i].pageX; 1344 | lo = dragorigval + (x - dragorigX) * khzperpixel; 1345 | updbw(); 1346 | } 1347 | } 1348 | 1349 | function useMouseXYupperedge(e) { 1350 | var pos = getMouseXY(e); 1351 | hi = dragorigval + (pos.x - dragorigX) * khzperpixel; 1352 | updbw(); 1353 | return cancelEvent(e); 1354 | } 1355 | 1356 | function touchXYupperedge(ev) { 1357 | ev.preventDefault(); 1358 | for (var i = 0; i < ev.touches.length; i++) { 1359 | var x = ev.touches[i].pageX; 1360 | hi = dragorigval + (x - dragorigX) * khzperpixel; 1361 | updbw(); 1362 | } 1363 | } 1364 | 1365 | function useMouseXYpassband(e) { 1366 | var pos = getMouseXY(e); 1367 | setfreq(dragorigval + (pos.x - dragorigX) * khzperpixel); 1368 | return cancelEvent(e); 1369 | } 1370 | 1371 | function touchXYpassband(ev) { 1372 | ev.preventDefault(); 1373 | for (var i = 0; i < ev.touches.length; i++) { 1374 | var x = ev.touches[i].pageX; 1375 | setfreq(dragorigval + (x - dragorigX) * khzperpixel); 1376 | } 1377 | } 1378 | 1379 | function mouseup(e) { 1380 | if (dragging) { 1381 | dragging = false; 1382 | document.onmousemove(e); 1383 | document.onmousemove = null; 1384 | } 1385 | } 1386 | 1387 | function touchEnd(ev) { 1388 | ev.preventDefault(); 1389 | if (dragging) { 1390 | dragging = false; 1391 | touchingLower = false; 1392 | } 1393 | } 1394 | 1395 | function imgmousedown(ev, bb) { 1396 | var b = id2band(bb); 1397 | dragging = true; 1398 | document.onmousemove = useMouseXY; 1399 | if (view != Views.oneband && band != b) { 1400 | if (view == Views.othersslow) waterfallspeed(waterslowness); 1401 | setband(b); 1402 | useMouseXY(ev); 1403 | } 1404 | } 1405 | 1406 | function imgtouch(ev) { 1407 | ev.preventDefault(); 1408 | 1409 | // recover waterfall instance number from event target 1410 | // is there a better way to do this? 1411 | var e = ev || window.event; 1412 | var img; 1413 | if (e.target) img = e.target; else 1414 | if (e.srcElement) img = e.srcElement; 1415 | if (img.nodeType == 3) img = img.parentNode; 1416 | var bb = 0; 1417 | if (img.name) bb = img.name.substring(6, 7); else // name="sncale[bb]" from HTML below 1418 | if (img.id) bb = img.id.substring(8, 9); // id="blackbar[bb]" from HTML below 1419 | 1420 | var b = id2band(bb); 1421 | if (view != Views.oneband && band != b) { 1422 | if (view == Views.othersslow) waterfallspeed(waterslowness); 1423 | setband(b); 1424 | } 1425 | 1426 | if (ev.targetTouches.length == 1) { 1427 | dragging = true; 1428 | dragorigX = ev.targetTouches[0].pageX; 1429 | touchXY(ev); 1430 | } 1431 | } 1432 | 1433 | function mousedownlower(ev) { 1434 | var pos = getMouseXY(ev); 1435 | dragging = true; 1436 | document.onmousemove = useMouseXYloweredge; 1437 | dragorigX = pos.x; 1438 | dragorigval = lo; 1439 | return cancelEvent(ev); 1440 | } 1441 | 1442 | function touchlower(ev) { 1443 | ev.preventDefault(); 1444 | if (ev.targetTouches.length == 1) { 1445 | touchingLower = true; 1446 | dragging = true; 1447 | dragorigX = ev.targetTouches[0].pageX; 1448 | dragorigval = lo; 1449 | } 1450 | } 1451 | 1452 | function mousedownupper(ev) { 1453 | var pos = getMouseXY(ev); 1454 | dragging = true; 1455 | document.onmousemove = useMouseXYupperedge; 1456 | dragorigX = pos.x; 1457 | dragorigval = hi; 1458 | return cancelEvent(ev); 1459 | } 1460 | 1461 | function touchupper(ev) { 1462 | ev.preventDefault(); 1463 | if (ev.targetTouches.length == 1) { 1464 | dragging = true; 1465 | dragorigX = ev.targetTouches[0].pageX; 1466 | dragorigval = hi; 1467 | } 1468 | } 1469 | 1470 | function mousedownpassband(ev) { 1471 | var pos = getMouseXY(ev); 1472 | dragging = true; 1473 | document.onmousemove = useMouseXYpassband; 1474 | dragorigX = pos.x; 1475 | dragorigval = freq; 1476 | return cancelEvent(ev); 1477 | } 1478 | 1479 | function touchpassband(ev) { 1480 | ev.preventDefault(); 1481 | if (ev.targetTouches.length == 1) { 1482 | dragging = true; 1483 | dragorigX = ev.targetTouches[0].pageX; 1484 | dragorigval = freq; 1485 | } 1486 | } 1487 | 1488 | 1489 | function docmousedown(ev) { 1490 | var fobj; 1491 | if (!ev) fobj = event.srcElement; // IE 1492 | else fobj = ev.target; // FF 1493 | if (fobj.className == "scale" || fobj.className == "scaleabs") return cancelEvent(ev); 1494 | return true; 1495 | } 1496 | 1497 | 1498 | var tprevwheel = 0; 1499 | var prevdir = 0; 1500 | var wheelstep = 1000; 1501 | function mousewheel(ev) { 1502 | var fobj; 1503 | // Win7/IE9 seems to have fixed the problem where 'ev' is null if not called directly, i.e. mousewheel(event) 1504 | if (!ev) { 1505 | ev = window.event; fobj = event.srcElement; // IE 1506 | } 1507 | else fobj = ev.target; // FF or IE9 1508 | 1509 | // In IE and Win7/Chrome the wheel event is not automatically passed on to the Java applet. 1510 | // This check will handle the mouse wheel event for any browser running on Windows (not just IE and Chrome) 1511 | // and hopefully that will not be a problem. 1512 | if (navigator.platform.substring(0, 3) == "Win" && fobj.tagName == 'APPLET' && fobj.name.substring(0, 15) == "waterfallapplet") { 1513 | var pos = getMouseXY(ev); 1514 | var x = pos.x - fobj.offsetParent.offsetLeft; 1515 | // scrollwheel while on the waterfallapplet; only needed in IE/Chrome because FF always passes these events on to the java applet 1516 | if (ev.wheelDelta > 0) document[fobj.name].setzoom(-2, x); 1517 | else if (ev.wheelDelta < 0) document[fobj.name].setzoom(-1, x); 1518 | return cancelEvent(ev); 1519 | } 1520 | 1521 | // this is needed for Mac/Safari and {Mac,Linux,Win7}/Chrome when positioned on the text of a dx label 1522 | if (fobj.nodeType == 3) fobj = fobj.parentNode; // 3=TEXT_NODE, i.e. text inside of a
1523 | 1524 | if (fobj.className == "scale" || fobj.className == "scaleabs" || fobj.className.substring(0, 8) == "statinfo") { 1525 | // this is for tuning using the scroll wheel when positioned on the tuning scale 1526 | var delta = ev.detail ? ev.detail : ev.wheelDelta / -40; 1527 | var t = new Date().getTime(); 1528 | var dt = t - tprevwheel; 1529 | if (dt < 10) dt = 10; 1530 | tprevwheel = t; 1531 | prevdir = delta; 1532 | if (Math.abs(delta) < wheelstep && delta != 0) wheelstep = Math.abs(delta); 1533 | delta /= wheelstep; 1534 | if (prevdir * delta > 0 && dt < 500) delta *= (500. / dt); 1535 | setfreq(freq - delta / 20); 1536 | return cancelEvent(ev); 1537 | } 1538 | 1539 | return true; 1540 | } 1541 | 1542 | if (document.addEventListener) { 1543 | window.addEventListener('DOMMouseScroll', mousewheel, false); 1544 | document.addEventListener('mousewheel', mousewheel, false); 1545 | // document.addEventListener('wheel', mousewheel, false); // note: "modern" browsers are supposed to use this event, but it seems to be incompatible with the old ones, and for now we'll have to support those anyway... 1546 | window.addEventListener('mouseup', mouseup, false); 1547 | window.addEventListener('mousedown', docmousedown, false); 1548 | } else { 1549 | window.onmousewheel = mousewheel; 1550 | document.onmousewheel = mousewheel; 1551 | document.onmouseup = mouseup; 1552 | document.onmousedown = docmousedown; 1553 | } 1554 | 1555 | 1556 | //---------------------------------------------------------------------------------------- 1557 | // direct control using keyboard: 1558 | var allowkeyboard; 1559 | 1560 | function keydown(e) { 1561 | if (!document.viewform.allowkeys.checked) return true; 1562 | e = e ? e : window.event; 1563 | if (!e.target) e.target = e.srcElement; 1564 | if (e.target.nodeName == "INPUT" && e.target.type == "text" && e.target.name != "frequency") return true; // don't intercept keys when typing in one of the text fields, except the frequency field 1565 | var st = 1; 1566 | if (e.shiftKey) st = 2; 1567 | if (e.ctrlKey || e.altKey || e.metaKey) st = 3; 1568 | switch (e.keyCode) { 1569 | case 37: // left arrow 1570 | case 74: freqstep(-st); return cancelEvent(e); // J 1571 | case 39: // right arrow 1572 | case 75: freqstep(st); return cancelEvent(e); // K 1573 | case 65: setmf('am', -4, 4); return cancelEvent(e); // A 1574 | case 70: setmf('fm', -8, 8); return cancelEvent(e); // F 1575 | case 67: setmf('cw', -0.95, -0.55); return cancelEvent(e); // C 1576 | case 76: setmf('lsb', -2.7, -0.3); return cancelEvent(e); // L 1577 | case 85: setmf('usb', 0.3, 2.7); return cancelEvent(e); // U 1578 | case 90: if (e.shiftKey) wfset(2); else wfset(4); return cancelEvent(e); // Z 1579 | case 71: document.freqform.frequency.value = ""; document.freqform.frequency.focus(); return cancelEvent(e); // G 1580 | case 66: if (e.shiftKey) setband((band - 1 + nbands) % nbands); // B 1581 | else setband((band + 1) % nbands); 1582 | return cancelEvent(e); 1583 | } 1584 | return true; 1585 | } 1586 | 1587 | window.onkeydown = keydown; 1588 | 1589 | //---------------------------------------------------------------------------------------- 1590 | // functions that create part of the HTML GUI 1591 | 1592 | function document_username() { 1593 | var x = readCookie('username'); 1594 | if (x) { 1595 | document.write('Your name or callsign: '); 1596 | document.write(''); 1597 | document.usernameform.username.value = x; 1598 | } else { 1599 | document.write('Ingrese su nombre o licencia aquí:<\/i><\/b> '); 1600 | document.write(''); 1601 | } 1602 | } 1603 | 1604 | 1605 | function document_waterfalls() { 1606 | if (view == Views.allbands || view == Views.othersslow) nwaterfalls = nvbands; 1607 | else if (view == Views.oneband) nwaterfalls = 1; 1608 | else { 1609 | nwaterfalls = 0; 1610 | document.getElementById('waterfalls').innerHTML = ""; 1611 | return; 1612 | } 1613 | 1614 | var i; 1615 | var b; 1616 | var s = ""; 1617 | for (i = 0; i < nwaterfalls; i++) { 1618 | b = id2band(i); 1619 | e = bi[b]; 1620 | j = e.realband; 1621 | s += 1622 | '
' + 1623 | '
' + 1624 | '' + 1625 | '' + 1626 | '
' + 1627 | '
<\/div>' + 1628 | '\n'; 1629 | waterfallapplet[i] = {}; 1630 | waterfallapplet[i].div = 'wfdiv' + i; 1631 | waterfallapplet[i].id = i; 1632 | waterfallapplet[i].band = b; 1633 | waterfallapplet[i].maxzoom = bi[b].maxzoom; 1634 | } 1635 | 1636 | waitingforwaterfalls = nwaterfalls; // this must be before the next line, to prevent a race 1637 | document.getElementById('waterfalls').innerHTML = s; 1638 | 1639 | if (usejavawaterfall) { 1640 | if (typeof prep_javawaterfalls == "function") prep_javawaterfalls(); 1641 | else { 1642 | script = document.createElement('script'); 1643 | script.src = 'websdr-javawaterfall.js'; 1644 | script.type = 'text/javascript'; 1645 | document.body.appendChild(script); 1646 | } 1647 | } else { 1648 | if (typeof prep_html5waterfalls == "function") prep_html5waterfalls(); 1649 | else { 1650 | script = document.createElement('script'); 1651 | script.src = 'websdr-waterfall.js'; 1652 | script.type = 'text/javascript'; 1653 | document.body.appendChild(script); 1654 | } 1655 | } 1656 | 1657 | for (i = 0; i < nwaterfalls; i++) { 1658 | scaleobjs[i] = document.getElementById('clipscale' + i); 1659 | scaleimgs0[i] = document.images["s0cale" + i]; 1660 | scaleimgs1[i] = document.images["s1cale" + i]; 1661 | if (isTouchDev) { 1662 | registerTouchEvents('clipscale' + i, imgtouch, touchXY); 1663 | registerTouchEvents('blackbar' + i, imgtouch, touchXY); 1664 | } 1665 | } 1666 | 1667 | } 1668 | 1669 | 1670 | function document_bandbuttons() { 1671 | if (nvbands > 1) { 1672 | document.write("
Band: ") 1673 | var i; 1674 | for (i = 0; i < nbands; i++) document.write("" + bandinfo[i].name + "\n"); 1675 | } 1676 | } 1677 | 1678 | 1679 | function document_soundapplet() { 1680 | if (usejavasound) { 1681 | if (typeof prep_javasound == "function") prep_javasound(); 1682 | else { 1683 | script = document.createElement('script'); 1684 | script.src = 'websdr-javasound.js'; 1685 | script.type = 'text/javascript'; 1686 | document.body.appendChild(script); 1687 | } 1688 | } else { 1689 | if (typeof prep_html5sound == "function") prep_html5sound(); 1690 | else { 1691 | script = document.createElement('script'); 1692 | script.src = 'websdr-sound.js'; 1693 | script.type = 'text/javascript'; 1694 | document.body.appendChild(script); 1695 | } 1696 | } 1697 | } 1698 | 1699 | 1700 | 1701 | //---------------------------------------------------------------------------------------- 1702 | // recording 1703 | 1704 | var rec_showtimer; 1705 | var rec_downloadurl; 1706 | 1707 | function record_show() { 1708 | document.getElementById('reccontrol').innerHTML = Math.round(soundapplet.rec_length_kB()) + " kB"; 1709 | } 1710 | 1711 | function record_start() { 1712 | document.getElementById('reccontrol').innerHTML = 0 + " kB"; 1713 | if (rec_downloadurl) { URL.revokeObjectURL(rec_downloadurl); rec_downloadurl = null; } 1714 | rec_showtimer = setInterval('record_show()', 250); 1715 | soundapplet.rec_start(); 1716 | } 1717 | 1718 | function record_stop() { 1719 | clearInterval(rec_showtimer); 1720 | var res = soundapplet.rec_finish(); 1721 | 1722 | var wavhead = new ArrayBuffer(44); 1723 | var dv = new DataView(wavhead); 1724 | var i = 0; 1725 | var sr = Math.round(res.sr); 1726 | dv.setUint8(i++, 82); dv.setUint8(i++, 73); dv.setUint8(i++, 70); dv.setUint8(i++, 70); // RIFF (is there really no less verbose way to initialize this thing?) 1727 | dv.setUint32(i, res.len + 44, true); i += 4; // total length; WAV files are little-endian 1728 | dv.setUint8(i++, 87); dv.setUint8(i++, 65); dv.setUint8(i++, 86); dv.setUint8(i++, 69); // WAVE 1729 | dv.setUint8(i++, 102); dv.setUint8(i++, 109); dv.setUint8(i++, 116); dv.setUint8(i++, 32); // fmt 1730 | dv.setUint32(i, 16, true); i += 4; // length of fmt 1731 | dv.setUint16(i, 1, true); i += 2; // PCM 1732 | dv.setUint16(i, 1, true); i += 2; // mono 1733 | dv.setUint32(i, sr, true); i += 4; // samplerate 1734 | dv.setUint32(i, 2 * sr, true); i += 4; // 2*samplerate 1735 | dv.setUint16(i, 2, true); i += 2; // bytes per sample 1736 | dv.setUint16(i, 16, true); i += 2; // bits per sample 1737 | dv.setUint8(i++, 100); dv.setUint8(i++, 97); dv.setUint8(i++, 116); dv.setUint8(i++, 97); // data 1738 | dv.setUint32(i, res.len, true); // length of data 1739 | 1740 | var wavdata = res.wavdata; 1741 | wavdata.unshift(wavhead); 1742 | 1743 | var mimetype = 'application/binary'; 1744 | var bb = new Blob(wavdata, { type: mimetype }); 1745 | if (!bb) document.getElementById('recwarning').style.display = "block"; 1746 | rec_downloadurl = window.URL.createObjectURL(bb); 1747 | if (rec_downloadurl.indexOf('http') >= 0) document.getElementById('recwarning').style.display = "block"; 1748 | var fname = ''; 1749 | try { 1750 | fname = (new Date().toISOString()).replace(/\.[0-9]{3}/, ""); 1751 | } catch (e) { }; 1752 | fname = "websdr_recording_" + fname + "_" + nominalfreq().toFixed(1) + "kHz.wav"; 1753 | document.getElementById('reccontrol').innerHTML = "download"; 1754 | } 1755 | 1756 | function record_click() { 1757 | var bt = document.getElementById('recbutton'); 1758 | if (bt.innerHTML == "stop") { 1759 | bt.innerHTML = "start"; 1760 | record_stop(); 1761 | } else { 1762 | bt.innerHTML = "stop"; 1763 | record_start(); 1764 | } 1765 | } 1766 | 1767 | 1768 | 1769 | //---------------------------------------------------------------------------------------- 1770 | // things not directly related to the SDR: chatbox, logbook 1771 | 1772 | function sendchat() { 1773 | timeout_idle_restart() 1774 | var xmlHttp; 1775 | try { xmlHttp = new XMLHttpRequest(); } 1776 | catch (e) { 1777 | try { xmlHttp = new ActiveXObject("Msxml2.XMLHTTP"); } 1778 | catch (e) { 1779 | try { xmlHttp = new ActiveXObject("Microsoft.XMLHTTP"); } 1780 | catch (e) { alert("Your browser does not support AJAX!"); return false; } 1781 | } 1782 | } 1783 | var url = "/~~chat"; 1784 | var msg = encodeURIComponent(document.chatform.chat.value); 1785 | url = url + "?name=" + encodeURIComponent(document.usernameform.username.value) + "&msg=" + encodeURIComponent(document.chatform.chat.value); 1786 | xmlHttp.open("GET", url, true); 1787 | xmlHttp.send(null); 1788 | document.chatform.chat.value = ""; 1789 | return false; 1790 | } 1791 | 1792 | function chatnewline(s) 1793 | // called by updates fetched from the server 1794 | { 1795 | var o = document.getElementById('chatboxnew'); 1796 | if (!o) return; 1797 | if (s[0] == '-') { 1798 | // remove line from chatbox 1799 | var div = document.createElement('div'); 1800 | div.innerHTML = s; 1801 | s = div.innerHTML; 1802 | var re = new RegExp('
' + s.substring(1).replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&") + '.*', 'g'); 1803 | o.innerHTML = o.innerHTML.replace(re, '
'); 1804 | return; 1805 | } 1806 | // add line to chatbox 1807 | o.innerHTML += '
' + s + '\n'; 1808 | o.scrollTop = o.scrollHeight; 1809 | } 1810 | 1811 | function sendlogclear() { 1812 | document.logform.comment.value = ""; 1813 | } 1814 | 1815 | function sendlog() { 1816 | var xmlHttp; 1817 | try { xmlHttp = new XMLHttpRequest(); } 1818 | catch (e) { 1819 | try { xmlHttp = new ActiveXObject("Msxml2.XMLHTTP"); } 1820 | catch (e) { 1821 | try { xmlHttp = new ActiveXObject("Microsoft.XMLHTTP"); } 1822 | catch (e) { alert("Your browser does not support AJAX!"); return false; } 1823 | } 1824 | } 1825 | var url = "/~~loginsert"; 1826 | url = url 1827 | + "?name=" + encodeURIComponent(document.usernameform.username.value) 1828 | + "&freq=" + nominalfreq() 1829 | + "&call=" + encodeURIComponent(document.logform.call.value) 1830 | + "&comment=" + encodeURIComponent(document.logform.comment.value) 1831 | ; 1832 | xmlHttp.open("GET", url, true); 1833 | xmlHttp.send(null); 1834 | document.logform.call.value = ""; 1835 | document.logform.comment.value = ""; 1836 | xmlHttp.onreadystatechange = function () { 1837 | if (xmlHttp.readyState == 4) { 1838 | document.logform.comment.value = xmlHttp.responseText; 1839 | } 1840 | } 1841 | setTimeout("document.logform.comment.value=''", 1000); 1842 | return false; 1843 | } 1844 | 1845 | 1846 | --------------------------------------------------------------------------------