diff --git a/README b/README index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..d248f6e27c05a99209a9d98935e6bb60763dde14 100644 --- a/README +++ b/README @@ -0,0 +1,2 @@ +This software is quite experimental. Contact the OpenWebRX Team at <ha5kfu@sdr.hu> + diff --git a/config_rtl.py b/config_rtl.py new file mode 100755 index 0000000000000000000000000000000000000000..52e572228395630a0dfd87bba2d4048e3ddf4f6e --- /dev/null +++ b/config_rtl.py @@ -0,0 +1,59 @@ +''' +This file is part of RTL Multi-User Server, + that makes multi-user access to your DVB-T dongle used as an SDR. +Copyright (c) 2013 by Andras Retzler <retzlerandras@gmail.com> + +RTL Multi-User Server is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +RTL Multi-User Server is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with RTL Multi-User Server. If not, see <http://www.gnu.org/licenses/>. +''' + +my_ip='' # all interfaces +my_listening_port = 7373 + +#send_first=chr(9)+chr(0)+chr(0)+chr(0)+chr(1) # set direct sampling +rtl_tcp_host = 'sdr3.sch.bme.hu' +rtl_tcp_port = 8888 +send_first="" + +setuid_on_start = 0 # we normally start with root privileges and setuid() to another user +uid = 999 # determine by issuing: $ id -u username +ignore_clients_without_commands = 1 # we won't serve data to telnet sessions and things like that + # we'll start to serve data after getting the first valid command +freq_allowed_ranges = [[0000000,2200000000]] +# Allow from all: freq_allowed_ranges = [[24000000,2200000000]] + +client_cant_set_until=0 +buffer_size=25000000 # per client +log_file_path = "/dev/null" # Might be set to /dev/null to turn off logging +''' +Allow any host to connect: + use_ip_access_control=0 + +Allow from specific ranges: + use_ip_access_control=1 + order_allow_deny=0 # deny and then allow + denied_ip_ranges=() # deny from all + allowed_ip_ranges=('192.168.','44.','127.0.0.1') # allow only from ... + +Deny from specific ranges: + use_ip_access_control=1 + order_allow_deny=0 # allow and then deny + allowed_ip_ranges=() # allow from all + denied_ip_ranges=('192.168.') # deny any hosts from ... +''' +use_ip_access_control=1 +order_allow_deny=0 +denied_ip_ranges=() # deny from all +allowed_ip_ranges=('152.66.','127.0.0.1') # allow only from local connections (from openwebrx) +allow_gain_set=1 + diff --git a/config_webrx.py b/config_webrx.py new file mode 100755 index 0000000000000000000000000000000000000000..088ebb0af1ab3d55d49ffca8b3d1ce036259e45a --- /dev/null +++ b/config_webrx.py @@ -0,0 +1,52 @@ +# -*- coding: utf-8 -*- + +""" +config_webrx: configuration options for OpenWebRX + +This file is part of OpenWebRX. + + OpenWebRX is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + OpenWebRX is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with OpenWebRX. If not, see <http://www.gnu.org/licenses/>. + +Authors: + Andras Retzler, HA7ILM <retzlerandras@gmail.com> + +""" + +receiver_name="HA5KFU Amateur Radio Club" +receiver_location="Budapest, Hungary" +receiver_qra="JN97ML" +receiver_asl=182 +receiver_ant="/dev/null" +receiver_device="RTL-SDR on Raspberry Pi" +receiver_admin="retzlerandras@gmail.com" +photo_title="Panorama of Budapest from Schönherz" +photo_desc="Weather: no information for now.<br/>Receiver: RTL-SDR (R820T)<br/><a href=\"http://ha5kfu.sch.bme.hu\" target=\"_blank\">http://ha5kfu.sch.bme.hu</a>" +photo_height=350 +fft_fps=8 +fft_size=4096 + + +change_admin_password_to="1234568" +admin_password_md5="" +start_rtl_thread=False + +web_port=8073 +server_hostname="sdr.sch.bme.hu" + +admin_param_dict={"samp_rate":250000,"center_freq":145525000,"rf_gain":10, "if_gain":10, "auto_gain":False} +#{"samp_rate":250000,"center_freq":145525000,"rf_gain":0, "if_gain":0, "auto_gain":True} +#admin_param_dict={"samp_rate":250000,"center_freq":14100000,"rf_gain":20, "if_gain":10, "auto_gain":True} +#admin_param_dict={"samp_rate":2048000,"center_freq":103000000,"rf_gain":20, "if_gain":10, "auto_gain":True} + + diff --git a/demod/__init__.py b/demod/__init__.py new file mode 100755 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/demod/am/__init__.py b/demod/am/__init__.py new file mode 100755 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/demod/am/client_am.grc b/demod/am/client_am.grc new file mode 100755 index 0000000000000000000000000000000000000000..85b5d2248d08aa22e629882bf37cad6cc87ac927 --- /dev/null +++ b/demod/am/client_am.grc @@ -0,0 +1,976 @@ +<?xml version='1.0' encoding='ASCII'?> +<flow_graph> + <timestamp>Sat Nov 23 01:06:14 2013</timestamp> + <block> + <key>options</key> + <param> + <key>id</key> + <value>top_block</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>title</key> + <value></value> + </param> + <param> + <key>author</key> + <value></value> + </param> + <param> + <key>description</key> + <value></value> + </param> + <param> + <key>window_size</key> + <value>1280, 1024</value> + </param> + <param> + <key>generate_options</key> + <value>no_gui</value> + </param> + <param> + <key>category</key> + <value>Custom</value> + </param> + <param> + <key>run_options</key> + <value>run</value> + </param> + <param> + <key>run</key> + <value>True</value> + </param> + <param> + <key>max_nouts</key> + <value>0</value> + </param> + <param> + <key>realtime_scheduling</key> + <value></value> + </param> + <param> + <key>_coordinate</key> + <value>(0, 8)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>variable</key> + <param> + <key>id</key> + <value>audio_rate</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>value</key> + <value>44100</value> + </param> + <param> + <key>_coordinate</key> + <value>(129, 98)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>variable</key> + <param> + <key>id</key> + <value>demod_rate</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>value</key> + <value>samp_rate/(samp_rate/16000)</value> + </param> + <param> + <key>_coordinate</key> + <value>(627, 101)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>variable</key> + <param> + <key>id</key> + <value>high_cut</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>value</key> + <value>4000</value> + </param> + <param> + <key>_coordinate</key> + <value>(532, 99)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>variable</key> + <param> + <key>id</key> + <value>samp_rate</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>value</key> + <value>2048000</value> + </param> + <param> + <key>_coordinate</key> + <value>(320, 29)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>variable</key> + <param> + <key>id</key> + <value>low_cut</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>value</key> + <value>-2000</value> + </param> + <param> + <key>_coordinate</key> + <value>(533, 26)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>variable</key> + <param> + <key>id</key> + <value>center_freq</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>value</key> + <value>103000000</value> + </param> + <param> + <key>_coordinate</key> + <value>(205, 29)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>variable</key> + <param> + <key>id</key> + <value>offset_freq</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>value</key> + <value>300000</value> + </param> + <param> + <key>_coordinate</key> + <value>(426, 28)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>variable</key> + <param> + <key>id</key> + <value>rf_gain</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>value</key> + <value>10</value> + </param> + <param> + <key>_coordinate</key> + <value>(234, 98)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>variable</key> + <param> + <key>id</key> + <value>if_gain</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>value</key> + <value>20</value> + </param> + <param> + <key>_coordinate</key> + <value>(321, 98)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>variable</key> + <param> + <key>id</key> + <value>auto_gain</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>value</key> + <value>True</value> + </param> + <param> + <key>_coordinate</key> + <value>(407, 98)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>blocks_message_sink</key> + <param> + <key>id</key> + <value>blocks_message_sink_0</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>type</key> + <value>short</value> + </param> + <param> + <key>dont_block</key> + <value>False</value> + </param> + <param> + <key>vlen</key> + <value>1</value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>minoutbuf</key> + <value>0</value> + </param> + <param> + <key>maxoutbuf</key> + <value>0</value> + </param> + <param> + <key>_coordinate</key> + <value>(883, 420)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>pad_sink</key> + <param> + <key>id</key> + <value>pad_sink_0</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>label</key> + <value>out</value> + </param> + <param> + <key>type</key> + <value></value> + </param> + <param> + <key>vlen</key> + <value>1</value> + </param> + <param> + <key>num_streams</key> + <value>1</value> + </param> + <param> + <key>optional</key> + <value>False</value> + </param> + <param> + <key>_coordinate</key> + <value>(1094, 420)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>rtlsdr_source</key> + <param> + <key>id</key> + <value>rtlsdr_source_0</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>type</key> + <value>fc32</value> + </param> + <param> + <key>args</key> + <value>rtl_tcp=127.0.0.1:7373</value> + </param> + <param> + <key>nchan</key> + <value>1</value> + </param> + <param> + <key>sample_rate</key> + <value>samp_rate</value> + </param> + <param> + <key>freq0</key> + <value>center_freq</value> + </param> + <param> + <key>corr0</key> + <value>0</value> + </param> + <param> + <key>dc_offset_mode0</key> + <value>0</value> + </param> + <param> + <key>iq_balance_mode0</key> + <value>0</value> + </param> + <param> + <key>gain_mode0</key> + <value>auto_gain</value> + </param> + <param> + <key>gain0</key> + <value>rf_gain</value> + </param> + <param> + <key>if_gain0</key> + <value>if_gain</value> + </param> + <param> + <key>bb_gain0</key> + <value>20</value> + </param> + <param> + <key>ant0</key> + <value></value> + </param> + <param> + <key>bw0</key> + <value>0</value> + </param> + <param> + <key>freq1</key> + <value>100e6</value> + </param> + <param> + <key>corr1</key> + <value>0</value> + </param> + <param> + <key>dc_offset_mode1</key> + <value>0</value> + </param> + <param> + <key>iq_balance_mode1</key> + <value>0</value> + </param> + <param> + <key>gain_mode1</key> + <value>0</value> + </param> + <param> + <key>gain1</key> + <value>10</value> + </param> + <param> + <key>if_gain1</key> + <value>20</value> + </param> + <param> + <key>bb_gain1</key> + <value>20</value> + </param> + <param> + <key>ant1</key> + <value></value> + </param> + <param> + <key>bw1</key> + <value>0</value> + </param> + <param> + <key>freq2</key> + <value>100e6</value> + </param> + <param> + <key>corr2</key> + <value>0</value> + </param> + <param> + <key>dc_offset_mode2</key> + <value>0</value> + </param> + <param> + <key>iq_balance_mode2</key> + <value>0</value> + </param> + <param> + <key>gain_mode2</key> + <value>0</value> + </param> + <param> + <key>gain2</key> + <value>10</value> + </param> + <param> + <key>if_gain2</key> + <value>20</value> + </param> + <param> + <key>bb_gain2</key> + <value>20</value> + </param> + <param> + <key>ant2</key> + <value></value> + </param> + <param> + <key>bw2</key> + <value>0</value> + </param> + <param> + <key>freq3</key> + <value>100e6</value> + </param> + <param> + <key>corr3</key> + <value>0</value> + </param> + <param> + <key>dc_offset_mode3</key> + <value>0</value> + </param> + <param> + <key>iq_balance_mode3</key> + <value>0</value> + </param> + <param> + <key>gain_mode3</key> + <value>0</value> + </param> + <param> + <key>gain3</key> + <value>10</value> + </param> + <param> + <key>if_gain3</key> + <value>20</value> + </param> + <param> + <key>bb_gain3</key> + <value>20</value> + </param> + <param> + <key>ant3</key> + <value></value> + </param> + <param> + <key>bw3</key> + <value>0</value> + </param> + <param> + <key>freq4</key> + <value>100e6</value> + </param> + <param> + <key>corr4</key> + <value>0</value> + </param> + <param> + <key>dc_offset_mode4</key> + <value>0</value> + </param> + <param> + <key>iq_balance_mode4</key> + <value>0</value> + </param> + <param> + <key>gain_mode4</key> + <value>0</value> + </param> + <param> + <key>gain4</key> + <value>10</value> + </param> + <param> + <key>if_gain4</key> + <value>20</value> + </param> + <param> + <key>bb_gain4</key> + <value>20</value> + </param> + <param> + <key>ant4</key> + <value></value> + </param> + <param> + <key>bw4</key> + <value>0</value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>minoutbuf</key> + <value>0</value> + </param> + <param> + <key>maxoutbuf</key> + <value>0</value> + </param> + <param> + <key>_coordinate</key> + <value>(14, 169)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>band_pass_filter</key> + <param> + <key>id</key> + <value>band_pass_filter_0</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>type</key> + <value>fir_filter_ccc</value> + </param> + <param> + <key>decim</key> + <value>1</value> + </param> + <param> + <key>interp</key> + <value>1</value> + </param> + <param> + <key>gain</key> + <value>1</value> + </param> + <param> + <key>samp_rate</key> + <value>demod_rate</value> + </param> + <param> + <key>low_cutoff_freq</key> + <value>low_cut</value> + </param> + <param> + <key>high_cutoff_freq</key> + <value>high_cut</value> + </param> + <param> + <key>width</key> + <value>500</value> + </param> + <param> + <key>win</key> + <value>firdes.WIN_HAMMING</value> + </param> + <param> + <key>beta</key> + <value>6.76</value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>minoutbuf</key> + <value>0</value> + </param> + <param> + <key>maxoutbuf</key> + <value>0</value> + </param> + <param> + <key>_coordinate</key> + <value>(563, 188)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>freq_xlating_fir_filter_xxx</key> + <param> + <key>id</key> + <value>freq_xlating_fir_filter_xxx_0</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>type</key> + <value>ccf</value> + </param> + <param> + <key>decim</key> + <value>samp_rate/demod_rate</value> + </param> + <param> + <key>taps</key> + <value>firdes.low_pass(1,samp_rate,demod_rate*0.8,demod_rate*0.3)</value> + </param> + <param> + <key>center_freq</key> + <value>offset_freq</value> + </param> + <param> + <key>samp_rate</key> + <value>samp_rate</value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>minoutbuf</key> + <value>0</value> + </param> + <param> + <key>maxoutbuf</key> + <value>0</value> + </param> + <param> + <key>_coordinate</key> + <value>(283, 210)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>analog_agc2_xx</key> + <param> + <key>id</key> + <value>analog_agc2_xx_0</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>type</key> + <value>complex</value> + </param> + <param> + <key>attack_rate</key> + <value>1e-3</value> + </param> + <param> + <key>decay_rate</key> + <value>208e-5</value> + </param> + <param> + <key>reference</key> + <value>1.0</value> + </param> + <param> + <key>gain</key> + <value>1.0</value> + </param> + <param> + <key>max_gain</key> + <value>20</value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>minoutbuf</key> + <value>0</value> + </param> + <param> + <key>maxoutbuf</key> + <value>0</value> + </param> + <param> + <key>_coordinate</key> + <value>(772, 225)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>fractional_resampler_xx</key> + <param> + <key>id</key> + <value>fractional_resampler_xx_0</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>type</key> + <value>float</value> + </param> + <param> + <key>phase_shift</key> + <value>0</value> + </param> + <param> + <key>resamp_ratio</key> + <value>demod_rate/float(audio_rate)</value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>minoutbuf</key> + <value>0</value> + </param> + <param> + <key>maxoutbuf</key> + <value>0</value> + </param> + <param> + <key>_coordinate</key> + <value>(440, 410)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>analog_am_demod_cf</key> + <param> + <key>id</key> + <value>analog_am_demod_cf_0</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>chan_rate</key> + <value>demod_rate</value> + </param> + <param> + <key>audio_decim</key> + <value>1</value> + </param> + <param> + <key>audio_pass</key> + <value>5000</value> + </param> + <param> + <key>audio_stop</key> + <value>5500</value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>minoutbuf</key> + <value>0</value> + </param> + <param> + <key>maxoutbuf</key> + <value>0</value> + </param> + <param> + <key>_coordinate</key> + <value>(964, 232)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>blocks_float_to_short</key> + <param> + <key>id</key> + <value>blocks_float_to_short_0</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>vlen</key> + <value>1</value> + </param> + <param> + <key>scale</key> + <value>5000</value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>minoutbuf</key> + <value>0</value> + </param> + <param> + <key>maxoutbuf</key> + <value>0</value> + </param> + <param> + <key>_coordinate</key> + <value>(680, 423)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <connection> + <source_block_id>rtlsdr_source_0</source_block_id> + <sink_block_id>freq_xlating_fir_filter_xxx_0</sink_block_id> + <source_key>0</source_key> + <sink_key>0</sink_key> + </connection> + <connection> + <source_block_id>blocks_float_to_short_0</source_block_id> + <sink_block_id>blocks_message_sink_0</sink_block_id> + <source_key>0</source_key> + <sink_key>0</sink_key> + </connection> + <connection> + <source_block_id>blocks_message_sink_0</source_block_id> + <sink_block_id>pad_sink_0</sink_block_id> + <source_key>msg</source_key> + <sink_key>0</sink_key> + </connection> + <connection> + <source_block_id>fractional_resampler_xx_0</source_block_id> + <sink_block_id>blocks_float_to_short_0</sink_block_id> + <source_key>0</source_key> + <sink_key>0</sink_key> + </connection> + <connection> + <source_block_id>band_pass_filter_0</source_block_id> + <sink_block_id>analog_agc2_xx_0</sink_block_id> + <source_key>0</source_key> + <sink_key>0</sink_key> + </connection> + <connection> + <source_block_id>freq_xlating_fir_filter_xxx_0</source_block_id> + <sink_block_id>band_pass_filter_0</sink_block_id> + <source_key>0</source_key> + <sink_key>0</sink_key> + </connection> + <connection> + <source_block_id>analog_agc2_xx_0</source_block_id> + <sink_block_id>analog_am_demod_cf_0</sink_block_id> + <source_key>0</source_key> + <sink_key>0</sink_key> + </connection> + <connection> + <source_block_id>analog_am_demod_cf_0</source_block_id> + <sink_block_id>fractional_resampler_xx_0</sink_block_id> + <source_key>0</source_key> + <sink_key>0</sink_key> + </connection> +</flow_graph> diff --git a/demod/am/top_block.py b/demod/am/top_block.py new file mode 100755 index 0000000000000000000000000000000000000000..d7f7e75c3ffbc41bc85dc7bbcedf27dfcb1a014a --- /dev/null +++ b/demod/am/top_block.py @@ -0,0 +1,168 @@ +#!/usr/bin/env python +################################################## +# Gnuradio Python Flow Graph +# Title: Top Block +# Generated: Mon Nov 25 22:15:07 2013 +################################################## + +from gnuradio import analog +from gnuradio import blocks +from gnuradio import eng_notation +from gnuradio import filter +from gnuradio import gr +from gnuradio.eng_option import eng_option +from gnuradio.filter import firdes +from optparse import OptionParser +import osmosdr + +class top_block(gr.top_block): + + def __init__(self,sample_rate_param): # added by grcconvert + gr.top_block.__init__(self, "Top Block") + + ################################################## + # Variables + ################################################## + self.samp_rate = samp_rate = sample_rate_param # added by grcconvert + self.rf_gain = rf_gain = 10 + self.offset_freq = offset_freq = 300000 + self.low_cut = low_cut = -2000 + self.if_gain = if_gain = 20 + self.high_cut = high_cut = 4000 + self.demod_rate = demod_rate = samp_rate/(samp_rate/16000) + self.center_freq = center_freq = 103000000 + self.auto_gain = auto_gain = True + self.audio_rate = audio_rate = 44100 + + ################################################## + # Message queues (added by grcconvert) + ################################################## + self.msgq_out = blocks_message_sink_0_msgq_out = gr.msg_queue(2) + + ################################################## + # Blocks + ################################################## + self.rtlsdr_source_0 = osmosdr.source( args="numchan=" + str(1) + " " + "rtl_tcp=127.0.0.1:7373" ) + self.rtlsdr_source_0.set_sample_rate(samp_rate) + self.rtlsdr_source_0.set_center_freq(center_freq, 0) + self.rtlsdr_source_0.set_freq_corr(0, 0) + self.rtlsdr_source_0.set_dc_offset_mode(0, 0) + self.rtlsdr_source_0.set_iq_balance_mode(0, 0) + self.rtlsdr_source_0.set_gain_mode(auto_gain, 0) + self.rtlsdr_source_0.set_gain(rf_gain, 0) + self.rtlsdr_source_0.set_if_gain(if_gain, 0) + self.rtlsdr_source_0.set_bb_gain(20, 0) + self.rtlsdr_source_0.set_antenna("", 0) + self.rtlsdr_source_0.set_bandwidth(0, 0) + + self.freq_xlating_fir_filter_xxx_0 = filter.freq_xlating_fir_filter_ccf(samp_rate/demod_rate, (firdes.low_pass(1,samp_rate,demod_rate*0.8,demod_rate*0.3)), offset_freq, samp_rate) + self.fractional_resampler_xx_0 = filter.fractional_resampler_ff(0, demod_rate/float(audio_rate)) + self.blocks_message_sink_0 = blocks.message_sink(gr.sizeof_short*1, blocks_message_sink_0_msgq_out, False) + self.blocks_float_to_short_0 = blocks.float_to_short(1, 5000) + self.band_pass_filter_0 = filter.fir_filter_ccc(1, firdes.complex_band_pass( + 1, demod_rate, low_cut, high_cut, 500, firdes.WIN_HAMMING, 6.76)) + self.analog_am_demod_cf_0 = analog.am_demod_cf( + channel_rate=demod_rate, + audio_decim=1, + audio_pass=5000, + audio_stop=5500, + ) + self.analog_agc2_xx_0 = analog.agc2_cc(1e-3, 208e-5, 1.0, 1.0) + self.analog_agc2_xx_0.set_max_gain(20) + + ################################################## + # Connections + ################################################## + self.connect((self.rtlsdr_source_0, 0), (self.freq_xlating_fir_filter_xxx_0, 0)) + self.connect((self.blocks_float_to_short_0, 0), (self.blocks_message_sink_0, 0)) + # removed by grcconvert: # self.connect((self.blocks_message_sink_0, msg), (self, 0)) + self.connect((self.fractional_resampler_xx_0, 0), (self.blocks_float_to_short_0, 0)) + self.connect((self.band_pass_filter_0, 0), (self.analog_agc2_xx_0, 0)) + self.connect((self.freq_xlating_fir_filter_xxx_0, 0), (self.band_pass_filter_0, 0)) + self.connect((self.analog_agc2_xx_0, 0), (self.analog_am_demod_cf_0, 0)) + self.connect((self.analog_am_demod_cf_0, 0), (self.fractional_resampler_xx_0, 0)) + + +# QT sink close method reimplementation + + def get_samp_rate(self): + return self.samp_rate + + def set_samp_rate(self, samp_rate): + self.samp_rate = samp_rate + self.set_demod_rate(self.samp_rate/(self.samp_rate/16000)) + self.rtlsdr_source_0.set_sample_rate(self.samp_rate) + self.freq_xlating_fir_filter_xxx_0.set_taps((firdes.low_pass(1,self.samp_rate,self.demod_rate*0.8,self.demod_rate*0.3))) + + def get_rf_gain(self): + return self.rf_gain + + def set_rf_gain(self, rf_gain): + self.rf_gain = rf_gain + self.rtlsdr_source_0.set_gain(self.rf_gain, 0) + + def get_offset_freq(self): + return self.offset_freq + + def set_offset_freq(self, offset_freq): + self.offset_freq = offset_freq + self.freq_xlating_fir_filter_xxx_0.set_center_freq(self.offset_freq) + + def get_low_cut(self): + return self.low_cut + + def set_low_cut(self, low_cut): + self.low_cut = low_cut + self.band_pass_filter_0.set_taps(firdes.complex_band_pass(1, self.demod_rate, self.low_cut, self.high_cut, 500, firdes.WIN_HAMMING, 6.76)) + + def get_if_gain(self): + return self.if_gain + + def set_if_gain(self, if_gain): + self.if_gain = if_gain + self.rtlsdr_source_0.set_if_gain(self.if_gain, 0) + + def get_high_cut(self): + return self.high_cut + + def set_high_cut(self, high_cut): + self.high_cut = high_cut + self.band_pass_filter_0.set_taps(firdes.complex_band_pass(1, self.demod_rate, self.low_cut, self.high_cut, 500, firdes.WIN_HAMMING, 6.76)) + + def get_demod_rate(self): + return self.demod_rate + + def set_demod_rate(self, demod_rate): + self.demod_rate = demod_rate + self.band_pass_filter_0.set_taps(firdes.complex_band_pass(1, self.demod_rate, self.low_cut, self.high_cut, 500, firdes.WIN_HAMMING, 6.76)) + self.freq_xlating_fir_filter_xxx_0.set_taps((firdes.low_pass(1,self.samp_rate,self.demod_rate*0.8,self.demod_rate*0.3))) + self.fractional_resampler_xx_0.set_resamp_ratio(self.demod_rate/float(self.audio_rate)) + + def get_center_freq(self): + return self.center_freq + + def set_center_freq(self, center_freq): + self.center_freq = center_freq + self.rtlsdr_source_0.set_center_freq(self.center_freq, 0) + + def get_auto_gain(self): + return self.auto_gain + + def set_auto_gain(self, auto_gain): + self.auto_gain = auto_gain + self.rtlsdr_source_0.set_gain_mode(self.auto_gain, 0) + + def get_audio_rate(self): + return self.audio_rate + + def set_audio_rate(self, audio_rate): + self.audio_rate = audio_rate + self.fractional_resampler_xx_0.set_resamp_ratio(self.demod_rate/float(self.audio_rate)) + +if __name__ == '__main__': + parser = OptionParser(option_class=eng_option, usage="%prog: [options]") + (options, args) = parser.parse_args() + tb = top_block() + tb.start() + tb.wait() + diff --git a/demod/nfm/__init__.py b/demod/nfm/__init__.py new file mode 100755 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/demod/nfm/client_nfm.grc b/demod/nfm/client_nfm.grc new file mode 100755 index 0000000000000000000000000000000000000000..b56607dffcde1046a97ec49a73e1b8a8e2a1f30d --- /dev/null +++ b/demod/nfm/client_nfm.grc @@ -0,0 +1,915 @@ +<?xml version='1.0' encoding='ASCII'?> +<flow_graph> + <timestamp>Mon Feb 17 22:04:30 2014</timestamp> + <block> + <key>options</key> + <param> + <key>id</key> + <value>top_block</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>title</key> + <value></value> + </param> + <param> + <key>author</key> + <value></value> + </param> + <param> + <key>description</key> + <value></value> + </param> + <param> + <key>window_size</key> + <value>1280, 1024</value> + </param> + <param> + <key>generate_options</key> + <value>no_gui</value> + </param> + <param> + <key>category</key> + <value>Custom</value> + </param> + <param> + <key>run_options</key> + <value>run</value> + </param> + <param> + <key>run</key> + <value>True</value> + </param> + <param> + <key>max_nouts</key> + <value>0</value> + </param> + <param> + <key>realtime_scheduling</key> + <value></value> + </param> + <param> + <key>_coordinate</key> + <value>(0, 8)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>variable</key> + <param> + <key>id</key> + <value>audio_rate</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>value</key> + <value>44100</value> + </param> + <param> + <key>_coordinate</key> + <value>(129, 98)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>variable</key> + <param> + <key>id</key> + <value>demod_rate</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>value</key> + <value>samp_rate/(samp_rate/16000)</value> + </param> + <param> + <key>_coordinate</key> + <value>(627, 101)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>variable</key> + <param> + <key>id</key> + <value>high_cut</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>value</key> + <value>4000</value> + </param> + <param> + <key>_coordinate</key> + <value>(532, 99)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>variable</key> + <param> + <key>id</key> + <value>samp_rate</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>value</key> + <value>2048000</value> + </param> + <param> + <key>_coordinate</key> + <value>(320, 29)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>variable</key> + <param> + <key>id</key> + <value>low_cut</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>value</key> + <value>-2000</value> + </param> + <param> + <key>_coordinate</key> + <value>(533, 26)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>variable</key> + <param> + <key>id</key> + <value>center_freq</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>value</key> + <value>103000000</value> + </param> + <param> + <key>_coordinate</key> + <value>(205, 29)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>variable</key> + <param> + <key>id</key> + <value>offset_freq</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>value</key> + <value>300000</value> + </param> + <param> + <key>_coordinate</key> + <value>(426, 28)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>variable</key> + <param> + <key>id</key> + <value>rf_gain</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>value</key> + <value>10</value> + </param> + <param> + <key>_coordinate</key> + <value>(234, 98)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>variable</key> + <param> + <key>id</key> + <value>if_gain</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>value</key> + <value>20</value> + </param> + <param> + <key>_coordinate</key> + <value>(321, 98)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>variable</key> + <param> + <key>id</key> + <value>auto_gain</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>value</key> + <value>True</value> + </param> + <param> + <key>_coordinate</key> + <value>(407, 98)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>blocks_message_sink</key> + <param> + <key>id</key> + <value>blocks_message_sink_0</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>type</key> + <value>short</value> + </param> + <param> + <key>dont_block</key> + <value>False</value> + </param> + <param> + <key>vlen</key> + <value>1</value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>minoutbuf</key> + <value>0</value> + </param> + <param> + <key>maxoutbuf</key> + <value>0</value> + </param> + <param> + <key>_coordinate</key> + <value>(883, 420)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>pad_sink</key> + <param> + <key>id</key> + <value>pad_sink_0</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>label</key> + <value>out</value> + </param> + <param> + <key>type</key> + <value></value> + </param> + <param> + <key>vlen</key> + <value>1</value> + </param> + <param> + <key>num_streams</key> + <value>1</value> + </param> + <param> + <key>optional</key> + <value>False</value> + </param> + <param> + <key>_coordinate</key> + <value>(1094, 420)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>rtlsdr_source</key> + <param> + <key>id</key> + <value>rtlsdr_source_0</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>type</key> + <value>fc32</value> + </param> + <param> + <key>args</key> + <value>rtl_tcp=127.0.0.1:7373</value> + </param> + <param> + <key>nchan</key> + <value>1</value> + </param> + <param> + <key>sample_rate</key> + <value>samp_rate</value> + </param> + <param> + <key>freq0</key> + <value>center_freq</value> + </param> + <param> + <key>corr0</key> + <value>0</value> + </param> + <param> + <key>dc_offset_mode0</key> + <value>0</value> + </param> + <param> + <key>iq_balance_mode0</key> + <value>0</value> + </param> + <param> + <key>gain_mode0</key> + <value>auto_gain</value> + </param> + <param> + <key>gain0</key> + <value>rf_gain</value> + </param> + <param> + <key>if_gain0</key> + <value>if_gain</value> + </param> + <param> + <key>bb_gain0</key> + <value>20</value> + </param> + <param> + <key>ant0</key> + <value></value> + </param> + <param> + <key>bw0</key> + <value>0</value> + </param> + <param> + <key>freq1</key> + <value>100e6</value> + </param> + <param> + <key>corr1</key> + <value>0</value> + </param> + <param> + <key>dc_offset_mode1</key> + <value>0</value> + </param> + <param> + <key>iq_balance_mode1</key> + <value>0</value> + </param> + <param> + <key>gain_mode1</key> + <value>0</value> + </param> + <param> + <key>gain1</key> + <value>10</value> + </param> + <param> + <key>if_gain1</key> + <value>20</value> + </param> + <param> + <key>bb_gain1</key> + <value>20</value> + </param> + <param> + <key>ant1</key> + <value></value> + </param> + <param> + <key>bw1</key> + <value>0</value> + </param> + <param> + <key>freq2</key> + <value>100e6</value> + </param> + <param> + <key>corr2</key> + <value>0</value> + </param> + <param> + <key>dc_offset_mode2</key> + <value>0</value> + </param> + <param> + <key>iq_balance_mode2</key> + <value>0</value> + </param> + <param> + <key>gain_mode2</key> + <value>0</value> + </param> + <param> + <key>gain2</key> + <value>10</value> + </param> + <param> + <key>if_gain2</key> + <value>20</value> + </param> + <param> + <key>bb_gain2</key> + <value>20</value> + </param> + <param> + <key>ant2</key> + <value></value> + </param> + <param> + <key>bw2</key> + <value>0</value> + </param> + <param> + <key>freq3</key> + <value>100e6</value> + </param> + <param> + <key>corr3</key> + <value>0</value> + </param> + <param> + <key>dc_offset_mode3</key> + <value>0</value> + </param> + <param> + <key>iq_balance_mode3</key> + <value>0</value> + </param> + <param> + <key>gain_mode3</key> + <value>0</value> + </param> + <param> + <key>gain3</key> + <value>10</value> + </param> + <param> + <key>if_gain3</key> + <value>20</value> + </param> + <param> + <key>bb_gain3</key> + <value>20</value> + </param> + <param> + <key>ant3</key> + <value></value> + </param> + <param> + <key>bw3</key> + <value>0</value> + </param> + <param> + <key>freq4</key> + <value>100e6</value> + </param> + <param> + <key>corr4</key> + <value>0</value> + </param> + <param> + <key>dc_offset_mode4</key> + <value>0</value> + </param> + <param> + <key>iq_balance_mode4</key> + <value>0</value> + </param> + <param> + <key>gain_mode4</key> + <value>0</value> + </param> + <param> + <key>gain4</key> + <value>10</value> + </param> + <param> + <key>if_gain4</key> + <value>20</value> + </param> + <param> + <key>bb_gain4</key> + <value>20</value> + </param> + <param> + <key>ant4</key> + <value></value> + </param> + <param> + <key>bw4</key> + <value>0</value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>minoutbuf</key> + <value>0</value> + </param> + <param> + <key>maxoutbuf</key> + <value>0</value> + </param> + <param> + <key>_coordinate</key> + <value>(14, 169)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>freq_xlating_fir_filter_xxx</key> + <param> + <key>id</key> + <value>freq_xlating_fir_filter_xxx_0</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>type</key> + <value>ccf</value> + </param> + <param> + <key>decim</key> + <value>samp_rate/demod_rate</value> + </param> + <param> + <key>taps</key> + <value>firdes.low_pass(1,samp_rate,demod_rate*0.8,demod_rate*0.3)</value> + </param> + <param> + <key>center_freq</key> + <value>offset_freq</value> + </param> + <param> + <key>samp_rate</key> + <value>samp_rate</value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>minoutbuf</key> + <value>0</value> + </param> + <param> + <key>maxoutbuf</key> + <value>0</value> + </param> + <param> + <key>_coordinate</key> + <value>(283, 210)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>band_pass_filter</key> + <param> + <key>id</key> + <value>band_pass_filter_0</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>type</key> + <value>fir_filter_ccc</value> + </param> + <param> + <key>decim</key> + <value>1</value> + </param> + <param> + <key>interp</key> + <value>1</value> + </param> + <param> + <key>gain</key> + <value>1</value> + </param> + <param> + <key>samp_rate</key> + <value>demod_rate</value> + </param> + <param> + <key>low_cutoff_freq</key> + <value>low_cut</value> + </param> + <param> + <key>high_cutoff_freq</key> + <value>high_cut</value> + </param> + <param> + <key>width</key> + <value>500</value> + </param> + <param> + <key>win</key> + <value>firdes.WIN_HAMMING</value> + </param> + <param> + <key>beta</key> + <value>6.76</value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>minoutbuf</key> + <value>0</value> + </param> + <param> + <key>maxoutbuf</key> + <value>0</value> + </param> + <param> + <key>_coordinate</key> + <value>(563, 188)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>analog_nbfm_rx</key> + <param> + <key>id</key> + <value>analog_nbfm_rx_0</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>audio_rate</key> + <value>demod_rate</value> + </param> + <param> + <key>quad_rate</key> + <value>demod_rate</value> + </param> + <param> + <key>tau</key> + <value>75e-6</value> + </param> + <param> + <key>max_dev</key> + <value>5e3</value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>minoutbuf</key> + <value>0</value> + </param> + <param> + <key>maxoutbuf</key> + <value>0</value> + </param> + <param> + <key>_coordinate</key> + <value>(998, 232)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>fractional_resampler_xx</key> + <param> + <key>id</key> + <value>fractional_resampler_xx_0</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>type</key> + <value>float</value> + </param> + <param> + <key>phase_shift</key> + <value>0</value> + </param> + <param> + <key>resamp_ratio</key> + <value>demod_rate/float(audio_rate)</value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>minoutbuf</key> + <value>0</value> + </param> + <param> + <key>maxoutbuf</key> + <value>0</value> + </param> + <param> + <key>_coordinate</key> + <value>(440, 410)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>blocks_float_to_short</key> + <param> + <key>id</key> + <value>blocks_float_to_short_0</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>vlen</key> + <value>1</value> + </param> + <param> + <key>scale</key> + <value>25000</value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>minoutbuf</key> + <value>0</value> + </param> + <param> + <key>maxoutbuf</key> + <value>0</value> + </param> + <param> + <key>_coordinate</key> + <value>(680, 421)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <connection> + <source_block_id>rtlsdr_source_0</source_block_id> + <sink_block_id>freq_xlating_fir_filter_xxx_0</sink_block_id> + <source_key>0</source_key> + <sink_key>0</sink_key> + </connection> + <connection> + <source_block_id>blocks_float_to_short_0</source_block_id> + <sink_block_id>blocks_message_sink_0</sink_block_id> + <source_key>0</source_key> + <sink_key>0</sink_key> + </connection> + <connection> + <source_block_id>blocks_message_sink_0</source_block_id> + <sink_block_id>pad_sink_0</sink_block_id> + <source_key>msg</source_key> + <sink_key>0</sink_key> + </connection> + <connection> + <source_block_id>fractional_resampler_xx_0</source_block_id> + <sink_block_id>blocks_float_to_short_0</sink_block_id> + <source_key>0</source_key> + <sink_key>0</sink_key> + </connection> + <connection> + <source_block_id>freq_xlating_fir_filter_xxx_0</source_block_id> + <sink_block_id>band_pass_filter_0</sink_block_id> + <source_key>0</source_key> + <sink_key>0</sink_key> + </connection> + <connection> + <source_block_id>band_pass_filter_0</source_block_id> + <sink_block_id>analog_nbfm_rx_0</sink_block_id> + <source_key>0</source_key> + <sink_key>0</sink_key> + </connection> + <connection> + <source_block_id>analog_nbfm_rx_0</source_block_id> + <sink_block_id>fractional_resampler_xx_0</sink_block_id> + <source_key>0</source_key> + <sink_key>0</sink_key> + </connection> +</flow_graph> diff --git a/demod/nfm/top_block.py b/demod/nfm/top_block.py new file mode 100755 index 0000000000000000000000000000000000000000..f279517830da7470d4eb1c291b0a136b17ef58f1 --- /dev/null +++ b/demod/nfm/top_block.py @@ -0,0 +1,165 @@ +#!/usr/bin/env python +################################################## +# Gnuradio Python Flow Graph +# Title: Top Block +# Generated: Mon Feb 17 22:04:32 2014 +################################################## + +from gnuradio import analog +from gnuradio import blocks +from gnuradio import eng_notation +from gnuradio import filter +from gnuradio import gr +from gnuradio.eng_option import eng_option +from gnuradio.filter import firdes +from optparse import OptionParser +import osmosdr + +class top_block(gr.top_block): + + def __init__(self,sample_rate_param): # added by grcconvert + gr.top_block.__init__(self, "Top Block") + + ################################################## + # Variables + ################################################## + self.samp_rate = samp_rate = sample_rate_param # added by grcconvert + self.rf_gain = rf_gain = 10 + self.offset_freq = offset_freq = 300000 + self.low_cut = low_cut = -2000 + self.if_gain = if_gain = 20 + self.high_cut = high_cut = 4000 + self.demod_rate = demod_rate = samp_rate/(samp_rate/16000) + self.center_freq = center_freq = 103000000 + self.auto_gain = auto_gain = True + self.audio_rate = audio_rate = 44100 + + ################################################## + # Message queues (added by grcconvert) + ################################################## + self.msgq_out = blocks_message_sink_0_msgq_out = gr.msg_queue(2) + + ################################################## + # Blocks + ################################################## + self.rtlsdr_source_0 = osmosdr.source( args="numchan=" + str(1) + " " + "rtl_tcp=127.0.0.1:7373" ) + self.rtlsdr_source_0.set_sample_rate(samp_rate) + self.rtlsdr_source_0.set_center_freq(center_freq, 0) + self.rtlsdr_source_0.set_freq_corr(0, 0) + self.rtlsdr_source_0.set_dc_offset_mode(0, 0) + self.rtlsdr_source_0.set_iq_balance_mode(0, 0) + self.rtlsdr_source_0.set_gain_mode(auto_gain, 0) + self.rtlsdr_source_0.set_gain(rf_gain, 0) + self.rtlsdr_source_0.set_if_gain(if_gain, 0) + self.rtlsdr_source_0.set_bb_gain(20, 0) + self.rtlsdr_source_0.set_antenna("", 0) + self.rtlsdr_source_0.set_bandwidth(0, 0) + + self.freq_xlating_fir_filter_xxx_0 = filter.freq_xlating_fir_filter_ccf(samp_rate/demod_rate, (firdes.low_pass(1,samp_rate,demod_rate*0.8,demod_rate*0.3)), offset_freq, samp_rate) + self.fractional_resampler_xx_0 = filter.fractional_resampler_ff(0, demod_rate/float(audio_rate)) + self.blocks_message_sink_0 = blocks.message_sink(gr.sizeof_short*1, blocks_message_sink_0_msgq_out, False) + self.blocks_float_to_short_0 = blocks.float_to_short(1, 25000) + self.band_pass_filter_0 = filter.fir_filter_ccc(1, firdes.complex_band_pass( + 1, demod_rate, low_cut, high_cut, 500, firdes.WIN_HAMMING, 6.76)) + self.analog_nbfm_rx_0 = analog.nbfm_rx( + audio_rate=demod_rate, + quad_rate=demod_rate, + tau=75e-6, + max_dev=5e3, + ) + + ################################################## + # Connections + ################################################## + self.connect((self.rtlsdr_source_0, 0), (self.freq_xlating_fir_filter_xxx_0, 0)) + self.connect((self.blocks_float_to_short_0, 0), (self.blocks_message_sink_0, 0)) + # removed by grcconvert: # self.connect((self.blocks_message_sink_0, msg), (self, 0)) + self.connect((self.fractional_resampler_xx_0, 0), (self.blocks_float_to_short_0, 0)) + self.connect((self.freq_xlating_fir_filter_xxx_0, 0), (self.band_pass_filter_0, 0)) + self.connect((self.band_pass_filter_0, 0), (self.analog_nbfm_rx_0, 0)) + self.connect((self.analog_nbfm_rx_0, 0), (self.fractional_resampler_xx_0, 0)) + + +# QT sink close method reimplementation + + def get_samp_rate(self): + return self.samp_rate + + def set_samp_rate(self, samp_rate): + self.samp_rate = samp_rate + self.set_demod_rate(self.samp_rate/(self.samp_rate/16000)) + self.rtlsdr_source_0.set_sample_rate(self.samp_rate) + self.freq_xlating_fir_filter_xxx_0.set_taps((firdes.low_pass(1,self.samp_rate,self.demod_rate*0.8,self.demod_rate*0.3))) + + def get_rf_gain(self): + return self.rf_gain + + def set_rf_gain(self, rf_gain): + self.rf_gain = rf_gain + self.rtlsdr_source_0.set_gain(self.rf_gain, 0) + + def get_offset_freq(self): + return self.offset_freq + + def set_offset_freq(self, offset_freq): + self.offset_freq = offset_freq + self.freq_xlating_fir_filter_xxx_0.set_center_freq(self.offset_freq) + + def get_low_cut(self): + return self.low_cut + + def set_low_cut(self, low_cut): + self.low_cut = low_cut + self.band_pass_filter_0.set_taps(firdes.complex_band_pass(1, self.demod_rate, self.low_cut, self.high_cut, 500, firdes.WIN_HAMMING, 6.76)) + + def get_if_gain(self): + return self.if_gain + + def set_if_gain(self, if_gain): + self.if_gain = if_gain + self.rtlsdr_source_0.set_if_gain(self.if_gain, 0) + + def get_high_cut(self): + return self.high_cut + + def set_high_cut(self, high_cut): + self.high_cut = high_cut + self.band_pass_filter_0.set_taps(firdes.complex_band_pass(1, self.demod_rate, self.low_cut, self.high_cut, 500, firdes.WIN_HAMMING, 6.76)) + + def get_demod_rate(self): + return self.demod_rate + + def set_demod_rate(self, demod_rate): + self.demod_rate = demod_rate + self.freq_xlating_fir_filter_xxx_0.set_taps((firdes.low_pass(1,self.samp_rate,self.demod_rate*0.8,self.demod_rate*0.3))) + self.band_pass_filter_0.set_taps(firdes.complex_band_pass(1, self.demod_rate, self.low_cut, self.high_cut, 500, firdes.WIN_HAMMING, 6.76)) + self.fractional_resampler_xx_0.set_resamp_ratio(self.demod_rate/float(self.audio_rate)) + + def get_center_freq(self): + return self.center_freq + + def set_center_freq(self, center_freq): + self.center_freq = center_freq + self.rtlsdr_source_0.set_center_freq(self.center_freq, 0) + + def get_auto_gain(self): + return self.auto_gain + + def set_auto_gain(self, auto_gain): + self.auto_gain = auto_gain + self.rtlsdr_source_0.set_gain_mode(self.auto_gain, 0) + + def get_audio_rate(self): + return self.audio_rate + + def set_audio_rate(self, audio_rate): + self.audio_rate = audio_rate + self.fractional_resampler_xx_0.set_resamp_ratio(self.demod_rate/float(self.audio_rate)) + +if __name__ == '__main__': + parser = OptionParser(option_class=eng_option, usage="%prog: [options]") + (options, args) = parser.parse_args() + tb = top_block() + tb.start() + tb.wait() + diff --git a/demod/spectrum/__init__.py b/demod/spectrum/__init__.py new file mode 100755 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/demod/spectrum/logpowfft.grc b/demod/spectrum/logpowfft.grc new file mode 100755 index 0000000000000000000000000000000000000000..2d8f0dd5ad5c3ae88ab6c9a8618f217fe64fdc2f --- /dev/null +++ b/demod/spectrum/logpowfft.grc @@ -0,0 +1,538 @@ +<?xml version='1.0' encoding='ASCII'?> +<flow_graph> + <timestamp>Fri Dec 13 23:28:34 2013</timestamp> + <block> + <key>options</key> + <param> + <key>id</key> + <value>top_block</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>title</key> + <value></value> + </param> + <param> + <key>author</key> + <value></value> + </param> + <param> + <key>description</key> + <value></value> + </param> + <param> + <key>window_size</key> + <value>1280, 1024</value> + </param> + <param> + <key>generate_options</key> + <value>no_gui</value> + </param> + <param> + <key>category</key> + <value>Custom</value> + </param> + <param> + <key>run_options</key> + <value>run</value> + </param> + <param> + <key>run</key> + <value>True</value> + </param> + <param> + <key>max_nouts</key> + <value>0</value> + </param> + <param> + <key>realtime_scheduling</key> + <value></value> + </param> + <param> + <key>_coordinate</key> + <value>(10, 10)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>variable</key> + <param> + <key>id</key> + <value>fps</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>value</key> + <value>8</value> + </param> + <param> + <key>_coordinate</key> + <value>(423, 16)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>variable</key> + <param> + <key>id</key> + <value>fft_size</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>value</key> + <value>4096</value> + </param> + <param> + <key>_coordinate</key> + <value>(325, 16)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>variable</key> + <param> + <key>id</key> + <value>samp_rate</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>value</key> + <value>2048000</value> + </param> + <param> + <key>_coordinate</key> + <value>(223, 9)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>blocks_message_sink</key> + <param> + <key>id</key> + <value>blocks_message_sink_0</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>type</key> + <value>float</value> + </param> + <param> + <key>dont_block</key> + <value>False</value> + </param> + <param> + <key>vlen</key> + <value>fft_size</value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>minoutbuf</key> + <value>0</value> + </param> + <param> + <key>maxoutbuf</key> + <value>0</value> + </param> + <param> + <key>_coordinate</key> + <value>(753, 186)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>pad_sink</key> + <param> + <key>id</key> + <value>pad_sink_0</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>label</key> + <value>out</value> + </param> + <param> + <key>type</key> + <value></value> + </param> + <param> + <key>vlen</key> + <value>1</value> + </param> + <param> + <key>num_streams</key> + <value>1</value> + </param> + <param> + <key>optional</key> + <value>False</value> + </param> + <param> + <key>_coordinate</key> + <value>(945, 193)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>rtlsdr_source</key> + <param> + <key>id</key> + <value>rtlsdr_source_0</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>type</key> + <value>fc32</value> + </param> + <param> + <key>args</key> + <value>rtl_tcp=127.0.0.1:7373</value> + </param> + <param> + <key>nchan</key> + <value>1</value> + </param> + <param> + <key>sample_rate</key> + <value>samp_rate</value> + </param> + <param> + <key>freq0</key> + <value>100e6</value> + </param> + <param> + <key>corr0</key> + <value>0</value> + </param> + <param> + <key>dc_offset_mode0</key> + <value>0</value> + </param> + <param> + <key>iq_balance_mode0</key> + <value>0</value> + </param> + <param> + <key>gain_mode0</key> + <value>0</value> + </param> + <param> + <key>gain0</key> + <value>10</value> + </param> + <param> + <key>if_gain0</key> + <value>20</value> + </param> + <param> + <key>bb_gain0</key> + <value>20</value> + </param> + <param> + <key>ant0</key> + <value></value> + </param> + <param> + <key>bw0</key> + <value>0</value> + </param> + <param> + <key>freq1</key> + <value>100e6</value> + </param> + <param> + <key>corr1</key> + <value>0</value> + </param> + <param> + <key>dc_offset_mode1</key> + <value>0</value> + </param> + <param> + <key>iq_balance_mode1</key> + <value>0</value> + </param> + <param> + <key>gain_mode1</key> + <value>0</value> + </param> + <param> + <key>gain1</key> + <value>10</value> + </param> + <param> + <key>if_gain1</key> + <value>20</value> + </param> + <param> + <key>bb_gain1</key> + <value>20</value> + </param> + <param> + <key>ant1</key> + <value></value> + </param> + <param> + <key>bw1</key> + <value>0</value> + </param> + <param> + <key>freq2</key> + <value>100e6</value> + </param> + <param> + <key>corr2</key> + <value>0</value> + </param> + <param> + <key>dc_offset_mode2</key> + <value>0</value> + </param> + <param> + <key>iq_balance_mode2</key> + <value>0</value> + </param> + <param> + <key>gain_mode2</key> + <value>0</value> + </param> + <param> + <key>gain2</key> + <value>10</value> + </param> + <param> + <key>if_gain2</key> + <value>20</value> + </param> + <param> + <key>bb_gain2</key> + <value>20</value> + </param> + <param> + <key>ant2</key> + <value></value> + </param> + <param> + <key>bw2</key> + <value>0</value> + </param> + <param> + <key>freq3</key> + <value>100e6</value> + </param> + <param> + <key>corr3</key> + <value>0</value> + </param> + <param> + <key>dc_offset_mode3</key> + <value>0</value> + </param> + <param> + <key>iq_balance_mode3</key> + <value>0</value> + </param> + <param> + <key>gain_mode3</key> + <value>0</value> + </param> + <param> + <key>gain3</key> + <value>10</value> + </param> + <param> + <key>if_gain3</key> + <value>20</value> + </param> + <param> + <key>bb_gain3</key> + <value>20</value> + </param> + <param> + <key>ant3</key> + <value></value> + </param> + <param> + <key>bw3</key> + <value>0</value> + </param> + <param> + <key>freq4</key> + <value>100e6</value> + </param> + <param> + <key>corr4</key> + <value>0</value> + </param> + <param> + <key>dc_offset_mode4</key> + <value>0</value> + </param> + <param> + <key>iq_balance_mode4</key> + <value>0</value> + </param> + <param> + <key>gain_mode4</key> + <value>0</value> + </param> + <param> + <key>gain4</key> + <value>10</value> + </param> + <param> + <key>if_gain4</key> + <value>20</value> + </param> + <param> + <key>bb_gain4</key> + <value>20</value> + </param> + <param> + <key>ant4</key> + <value></value> + </param> + <param> + <key>bw4</key> + <value>0</value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>minoutbuf</key> + <value>0</value> + </param> + <param> + <key>maxoutbuf</key> + <value>0</value> + </param> + <param> + <key>_coordinate</key> + <value>(190, 95)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>logpwrfft_x</key> + <param> + <key>id</key> + <value>logpwrfft_x_0</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>type</key> + <value>complex</value> + </param> + <param> + <key>sample_rate</key> + <value>samp_rate</value> + </param> + <param> + <key>fft_size</key> + <value>fft_size</value> + </param> + <param> + <key>ref_scale</key> + <value>2</value> + </param> + <param> + <key>frame_rate</key> + <value>fps</value> + </param> + <param> + <key>average</key> + <value>False</value> + </param> + <param> + <key>avg_alpha</key> + <value>1.0</value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>minoutbuf</key> + <value>0</value> + </param> + <param> + <key>maxoutbuf</key> + <value>0</value> + </param> + <param> + <key>_coordinate</key> + <value>(513, 157)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <connection> + <source_block_id>logpwrfft_x_0</source_block_id> + <sink_block_id>blocks_message_sink_0</sink_block_id> + <source_key>0</source_key> + <sink_key>0</sink_key> + </connection> + <connection> + <source_block_id>blocks_message_sink_0</source_block_id> + <sink_block_id>pad_sink_0</sink_block_id> + <source_key>msg</source_key> + <sink_key>0</sink_key> + </connection> + <connection> + <source_block_id>rtlsdr_source_0</source_block_id> + <sink_block_id>logpwrfft_x_0</sink_block_id> + <source_key>0</source_key> + <sink_key>0</sink_key> + </connection> +</flow_graph> diff --git a/demod/spectrum/testr.py b/demod/spectrum/testr.py new file mode 100755 index 0000000000000000000000000000000000000000..e8a43ac3b6347292a00088ebc9f1a4927f3f52a1 --- /dev/null +++ b/demod/spectrum/testr.py @@ -0,0 +1,8 @@ +import top_block +import os + +print 'Blocked waiting for GDB attach (pid = %d)' % (os.getpid(),) +raw_input ('Press Enter to continue: ') +print " ------- top_block() -------" +tb=top_block.top_block() +print " ------- start() -------" diff --git a/demod/spectrum/top_block.py b/demod/spectrum/top_block.py new file mode 100755 index 0000000000000000000000000000000000000000..7976dcd730b0a274f31355b01915b4723bb54dca --- /dev/null +++ b/demod/spectrum/top_block.py @@ -0,0 +1,96 @@ +#!/usr/bin/env python +################################################## +# Gnuradio Python Flow Graph +# Title: Top Block +# Generated: Fri Dec 13 23:30:36 2013 +################################################## + +from gnuradio import blocks +from gnuradio import eng_notation +from gnuradio import gr +from gnuradio.eng_option import eng_option +from gnuradio.fft import logpwrfft +from gnuradio.filter import firdes +from optparse import OptionParser +import osmosdr + +class top_block(gr.top_block): + + def __init__(self,sample_rate_param): # added by grcconvert + gr.top_block.__init__(self, "Top Block") + + ################################################## + # Variables + ################################################## + self.samp_rate = samp_rate = sample_rate_param # added by grcconvert + self.fps = fps = 8 + self.fft_size = fft_size = 4096 + + ################################################## + # Message queues (added by grcconvert) + ################################################## + self.msgq_out = blocks_message_sink_0_msgq_out = gr.msg_queue(2) + + ################################################## + # Blocks + ################################################## + self.rtlsdr_source_0 = osmosdr.source( args="numchan=" + str(1) + " " + "rtl_tcp=127.0.0.1:7373" ) + self.rtlsdr_source_0.set_sample_rate(samp_rate) + self.rtlsdr_source_0.set_center_freq(100e6, 0) + self.rtlsdr_source_0.set_freq_corr(0, 0) + self.rtlsdr_source_0.set_dc_offset_mode(0, 0) + self.rtlsdr_source_0.set_iq_balance_mode(0, 0) + self.rtlsdr_source_0.set_gain_mode(0, 0) + self.rtlsdr_source_0.set_gain(10, 0) + self.rtlsdr_source_0.set_if_gain(20, 0) + self.rtlsdr_source_0.set_bb_gain(20, 0) + self.rtlsdr_source_0.set_antenna("", 0) + self.rtlsdr_source_0.set_bandwidth(0, 0) + + self.logpwrfft_x_0 = logpwrfft.logpwrfft_c( + sample_rate=samp_rate, + fft_size=fft_size, + ref_scale=2, + frame_rate=fps, + avg_alpha=1.0, + average=False, + ) + self.blocks_message_sink_0 = blocks.message_sink(gr.sizeof_float*fft_size, blocks_message_sink_0_msgq_out, False) + + ################################################## + # Connections + ################################################## + self.connect((self.logpwrfft_x_0, 0), (self.blocks_message_sink_0, 0)) + # removed by grcconvert: # self.connect((self.blocks_message_sink_0, msg), (self, 0)) + self.connect((self.rtlsdr_source_0, 0), (self.logpwrfft_x_0, 0)) + + +# QT sink close method reimplementation + + def get_samp_rate(self): + return self.samp_rate + + def set_samp_rate(self, samp_rate): + self.samp_rate = samp_rate + self.rtlsdr_source_0.set_sample_rate(self.samp_rate) + self.logpwrfft_x_0.set_sample_rate(self.samp_rate) + + def get_fps(self): + return self.fps + + def set_fps(self, fps): + self.fps = fps + + def get_fft_size(self): + return self.fft_size + + def set_fft_size(self, fft_size): + self.fft_size = fft_size + +if __name__ == '__main__': + parser = OptionParser(option_class=eng_option, usage="%prog: [options]") + (options, args) = parser.parse_args() + tb = top_block() + tb.start() + tb.wait() + diff --git a/demod/ssb/__init__.py b/demod/ssb/__init__.py new file mode 100755 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/demod/ssb/client_ssb.grc b/demod/ssb/client_ssb.grc new file mode 100755 index 0000000000000000000000000000000000000000..3596b85bc2ef56e6eb1cda0d35cde4771d586934 --- /dev/null +++ b/demod/ssb/client_ssb.grc @@ -0,0 +1,1015 @@ +<?xml version='1.0' encoding='ASCII'?> +<flow_graph> + <timestamp>Sat Nov 23 18:35:41 2013</timestamp> + <block> + <key>options</key> + <param> + <key>id</key> + <value>top_block</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>title</key> + <value></value> + </param> + <param> + <key>author</key> + <value></value> + </param> + <param> + <key>description</key> + <value></value> + </param> + <param> + <key>window_size</key> + <value>1280, 1024</value> + </param> + <param> + <key>generate_options</key> + <value>no_gui</value> + </param> + <param> + <key>category</key> + <value>Custom</value> + </param> + <param> + <key>run_options</key> + <value>run</value> + </param> + <param> + <key>run</key> + <value>True</value> + </param> + <param> + <key>max_nouts</key> + <value>0</value> + </param> + <param> + <key>realtime_scheduling</key> + <value></value> + </param> + <param> + <key>_coordinate</key> + <value>(0, 8)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>variable</key> + <param> + <key>id</key> + <value>auto_gain</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>value</key> + <value>True</value> + </param> + <param> + <key>_coordinate</key> + <value>(407, 98)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>variable</key> + <param> + <key>id</key> + <value>if_gain</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>value</key> + <value>20</value> + </param> + <param> + <key>_coordinate</key> + <value>(321, 98)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>variable</key> + <param> + <key>id</key> + <value>rf_gain</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>value</key> + <value>10</value> + </param> + <param> + <key>_coordinate</key> + <value>(234, 98)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>variable</key> + <param> + <key>id</key> + <value>offset_freq</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>value</key> + <value>300000</value> + </param> + <param> + <key>_coordinate</key> + <value>(426, 28)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>variable</key> + <param> + <key>id</key> + <value>center_freq</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>value</key> + <value>103000000</value> + </param> + <param> + <key>_coordinate</key> + <value>(205, 29)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>variable</key> + <param> + <key>id</key> + <value>samp_rate</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>value</key> + <value>2048000</value> + </param> + <param> + <key>_coordinate</key> + <value>(320, 29)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>variable</key> + <param> + <key>id</key> + <value>demod_rate</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>value</key> + <value>samp_rate/(samp_rate/16000)</value> + </param> + <param> + <key>_coordinate</key> + <value>(627, 101)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>variable</key> + <param> + <key>id</key> + <value>audio_rate</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>value</key> + <value>44100</value> + </param> + <param> + <key>_coordinate</key> + <value>(129, 98)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>variable</key> + <param> + <key>id</key> + <value>low_cut</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>value</key> + <value>-3000</value> + </param> + <param> + <key>_coordinate</key> + <value>(533, 26)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>variable</key> + <param> + <key>id</key> + <value>high_cut</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>value</key> + <value>0</value> + </param> + <param> + <key>_coordinate</key> + <value>(532, 99)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>blocks_message_sink</key> + <param> + <key>id</key> + <value>blocks_message_sink_0</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>type</key> + <value>short</value> + </param> + <param> + <key>dont_block</key> + <value>False</value> + </param> + <param> + <key>vlen</key> + <value>1</value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>minoutbuf</key> + <value>0</value> + </param> + <param> + <key>maxoutbuf</key> + <value>0</value> + </param> + <param> + <key>_coordinate</key> + <value>(883, 420)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>pad_sink</key> + <param> + <key>id</key> + <value>pad_sink_0</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>label</key> + <value>out</value> + </param> + <param> + <key>type</key> + <value></value> + </param> + <param> + <key>vlen</key> + <value>1</value> + </param> + <param> + <key>num_streams</key> + <value>1</value> + </param> + <param> + <key>optional</key> + <value>False</value> + </param> + <param> + <key>_coordinate</key> + <value>(1094, 420)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>rtlsdr_source</key> + <param> + <key>id</key> + <value>rtlsdr_source_0</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>type</key> + <value>fc32</value> + </param> + <param> + <key>args</key> + <value>rtl_tcp=127.0.0.1:7373</value> + </param> + <param> + <key>nchan</key> + <value>1</value> + </param> + <param> + <key>sample_rate</key> + <value>samp_rate</value> + </param> + <param> + <key>freq0</key> + <value>center_freq</value> + </param> + <param> + <key>corr0</key> + <value>0</value> + </param> + <param> + <key>dc_offset_mode0</key> + <value>0</value> + </param> + <param> + <key>iq_balance_mode0</key> + <value>0</value> + </param> + <param> + <key>gain_mode0</key> + <value>auto_gain</value> + </param> + <param> + <key>gain0</key> + <value>rf_gain</value> + </param> + <param> + <key>if_gain0</key> + <value>if_gain</value> + </param> + <param> + <key>bb_gain0</key> + <value>20</value> + </param> + <param> + <key>ant0</key> + <value></value> + </param> + <param> + <key>bw0</key> + <value>0</value> + </param> + <param> + <key>freq1</key> + <value>100e6</value> + </param> + <param> + <key>corr1</key> + <value>0</value> + </param> + <param> + <key>dc_offset_mode1</key> + <value>0</value> + </param> + <param> + <key>iq_balance_mode1</key> + <value>0</value> + </param> + <param> + <key>gain_mode1</key> + <value>0</value> + </param> + <param> + <key>gain1</key> + <value>10</value> + </param> + <param> + <key>if_gain1</key> + <value>20</value> + </param> + <param> + <key>bb_gain1</key> + <value>20</value> + </param> + <param> + <key>ant1</key> + <value></value> + </param> + <param> + <key>bw1</key> + <value>0</value> + </param> + <param> + <key>freq2</key> + <value>100e6</value> + </param> + <param> + <key>corr2</key> + <value>0</value> + </param> + <param> + <key>dc_offset_mode2</key> + <value>0</value> + </param> + <param> + <key>iq_balance_mode2</key> + <value>0</value> + </param> + <param> + <key>gain_mode2</key> + <value>0</value> + </param> + <param> + <key>gain2</key> + <value>10</value> + </param> + <param> + <key>if_gain2</key> + <value>20</value> + </param> + <param> + <key>bb_gain2</key> + <value>20</value> + </param> + <param> + <key>ant2</key> + <value></value> + </param> + <param> + <key>bw2</key> + <value>0</value> + </param> + <param> + <key>freq3</key> + <value>100e6</value> + </param> + <param> + <key>corr3</key> + <value>0</value> + </param> + <param> + <key>dc_offset_mode3</key> + <value>0</value> + </param> + <param> + <key>iq_balance_mode3</key> + <value>0</value> + </param> + <param> + <key>gain_mode3</key> + <value>0</value> + </param> + <param> + <key>gain3</key> + <value>10</value> + </param> + <param> + <key>if_gain3</key> + <value>20</value> + </param> + <param> + <key>bb_gain3</key> + <value>20</value> + </param> + <param> + <key>ant3</key> + <value></value> + </param> + <param> + <key>bw3</key> + <value>0</value> + </param> + <param> + <key>freq4</key> + <value>100e6</value> + </param> + <param> + <key>corr4</key> + <value>0</value> + </param> + <param> + <key>dc_offset_mode4</key> + <value>0</value> + </param> + <param> + <key>iq_balance_mode4</key> + <value>0</value> + </param> + <param> + <key>gain_mode4</key> + <value>0</value> + </param> + <param> + <key>gain4</key> + <value>10</value> + </param> + <param> + <key>if_gain4</key> + <value>20</value> + </param> + <param> + <key>bb_gain4</key> + <value>20</value> + </param> + <param> + <key>ant4</key> + <value></value> + </param> + <param> + <key>bw4</key> + <value>0</value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>minoutbuf</key> + <value>0</value> + </param> + <param> + <key>maxoutbuf</key> + <value>0</value> + </param> + <param> + <key>_coordinate</key> + <value>(14, 169)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>analog_agc2_xx</key> + <param> + <key>id</key> + <value>analog_agc2_xx_0</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>type</key> + <value>complex</value> + </param> + <param> + <key>attack_rate</key> + <value>1e-3</value> + </param> + <param> + <key>decay_rate</key> + <value>208e-5</value> + </param> + <param> + <key>reference</key> + <value>1.0</value> + </param> + <param> + <key>gain</key> + <value>1.0</value> + </param> + <param> + <key>max_gain</key> + <value>20</value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>minoutbuf</key> + <value>0</value> + </param> + <param> + <key>maxoutbuf</key> + <value>0</value> + </param> + <param> + <key>_coordinate</key> + <value>(772, 225)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>blocks_complex_to_imag</key> + <param> + <key>id</key> + <value>blocks_complex_to_imag_0</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>vlen</key> + <value>1</value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>minoutbuf</key> + <value>0</value> + </param> + <param> + <key>maxoutbuf</key> + <value>0</value> + </param> + <param> + <key>_coordinate</key> + <value>(984, 256)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>freq_xlating_fir_filter_xxx</key> + <param> + <key>id</key> + <value>freq_xlating_fir_filter_xxx_0_0</value> + </param> + <param> + <key>_enabled</key> + <value>False</value> + </param> + <param> + <key>type</key> + <value>ccc</value> + </param> + <param> + <key>decim</key> + <value>samp_rate/demod_rate</value> + </param> + <param> + <key>taps</key> + <value>firdes.complex_band_pass(1,samp_rate,low_cut,high_cut,1000)</value> + </param> + <param> + <key>center_freq</key> + <value>offset_freq</value> + </param> + <param> + <key>samp_rate</key> + <value>samp_rate</value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>minoutbuf</key> + <value>0</value> + </param> + <param> + <key>maxoutbuf</key> + <value>0</value> + </param> + <param> + <key>_coordinate</key> + <value>(147, 372)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>band_pass_filter</key> + <param> + <key>id</key> + <value>band_pass_filter_0</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>type</key> + <value>fir_filter_ccc</value> + </param> + <param> + <key>decim</key> + <value>1</value> + </param> + <param> + <key>interp</key> + <value>1</value> + </param> + <param> + <key>gain</key> + <value>1</value> + </param> + <param> + <key>samp_rate</key> + <value>demod_rate</value> + </param> + <param> + <key>low_cutoff_freq</key> + <value>low_cut</value> + </param> + <param> + <key>high_cutoff_freq</key> + <value>high_cut</value> + </param> + <param> + <key>width</key> + <value>500</value> + </param> + <param> + <key>win</key> + <value>firdes.WIN_HAMMING</value> + </param> + <param> + <key>beta</key> + <value>6.76</value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>minoutbuf</key> + <value>0</value> + </param> + <param> + <key>maxoutbuf</key> + <value>0</value> + </param> + <param> + <key>_coordinate</key> + <value>(563, 188)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>fractional_resampler_xx</key> + <param> + <key>id</key> + <value>fractional_resampler_xx_0</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>type</key> + <value>float</value> + </param> + <param> + <key>phase_shift</key> + <value>0</value> + </param> + <param> + <key>resamp_ratio</key> + <value>demod_rate/float(audio_rate)</value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>minoutbuf</key> + <value>0</value> + </param> + <param> + <key>maxoutbuf</key> + <value>0</value> + </param> + <param> + <key>_coordinate</key> + <value>(440, 411)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>blocks_float_to_short</key> + <param> + <key>id</key> + <value>blocks_float_to_short_0</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>vlen</key> + <value>1</value> + </param> + <param> + <key>scale</key> + <value>3000</value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>minoutbuf</key> + <value>0</value> + </param> + <param> + <key>maxoutbuf</key> + <value>0</value> + </param> + <param> + <key>_coordinate</key> + <value>(680, 421)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>freq_xlating_fir_filter_xxx</key> + <param> + <key>id</key> + <value>freq_xlating_fir_filter_xxx_0</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>type</key> + <value>ccf</value> + </param> + <param> + <key>decim</key> + <value>samp_rate/demod_rate</value> + </param> + <param> + <key>taps</key> + <value>firdes.low_pass(1,samp_rate,demod_rate*0.8,demod_rate*0.3)</value> + </param> + <param> + <key>center_freq</key> + <value>offset_freq</value> + </param> + <param> + <key>samp_rate</key> + <value>samp_rate</value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>minoutbuf</key> + <value>0</value> + </param> + <param> + <key>maxoutbuf</key> + <value>0</value> + </param> + <param> + <key>_coordinate</key> + <value>(282, 211)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <connection> + <source_block_id>rtlsdr_source_0</source_block_id> + <sink_block_id>freq_xlating_fir_filter_xxx_0</sink_block_id> + <source_key>0</source_key> + <sink_key>0</sink_key> + </connection> + <connection> + <source_block_id>blocks_float_to_short_0</source_block_id> + <sink_block_id>blocks_message_sink_0</sink_block_id> + <source_key>0</source_key> + <sink_key>0</sink_key> + </connection> + <connection> + <source_block_id>blocks_message_sink_0</source_block_id> + <sink_block_id>pad_sink_0</sink_block_id> + <source_key>msg</source_key> + <sink_key>0</sink_key> + </connection> + <connection> + <source_block_id>analog_agc2_xx_0</source_block_id> + <sink_block_id>blocks_complex_to_imag_0</sink_block_id> + <source_key>0</source_key> + <sink_key>0</sink_key> + </connection> + <connection> + <source_block_id>fractional_resampler_xx_0</source_block_id> + <sink_block_id>blocks_float_to_short_0</sink_block_id> + <source_key>0</source_key> + <sink_key>0</sink_key> + </connection> + <connection> + <source_block_id>blocks_complex_to_imag_0</source_block_id> + <sink_block_id>fractional_resampler_xx_0</sink_block_id> + <source_key>0</source_key> + <sink_key>0</sink_key> + </connection> + <connection> + <source_block_id>band_pass_filter_0</source_block_id> + <sink_block_id>analog_agc2_xx_0</sink_block_id> + <source_key>0</source_key> + <sink_key>0</sink_key> + </connection> + <connection> + <source_block_id>freq_xlating_fir_filter_xxx_0</source_block_id> + <sink_block_id>band_pass_filter_0</sink_block_id> + <source_key>0</source_key> + <sink_key>0</sink_key> + </connection> +</flow_graph> diff --git a/demod/ssb/top_block.py b/demod/ssb/top_block.py new file mode 100755 index 0000000000000000000000000000000000000000..a3ce65b42fd22b400baaec2045eff2c35154f20d --- /dev/null +++ b/demod/ssb/top_block.py @@ -0,0 +1,163 @@ +#!/usr/bin/env python +################################################## +# Gnuradio Python Flow Graph +# Title: Top Block +# Generated: Mon Nov 25 22:14:50 2013 +################################################## + +from gnuradio import analog +from gnuradio import blocks +from gnuradio import eng_notation +from gnuradio import filter +from gnuradio import gr +from gnuradio.eng_option import eng_option +from gnuradio.filter import firdes +from optparse import OptionParser +import osmosdr + +class top_block(gr.top_block): + + def __init__(self,sample_rate_param): # added by grcconvert + gr.top_block.__init__(self, "Top Block") + + ################################################## + # Variables + ################################################## + self.samp_rate = samp_rate = sample_rate_param # added by grcconvert + self.rf_gain = rf_gain = 10 + self.offset_freq = offset_freq = 300000 + self.low_cut = low_cut = -3000 + self.if_gain = if_gain = 20 + self.high_cut = high_cut = 0 + self.demod_rate = demod_rate = samp_rate/(samp_rate/16000) + self.center_freq = center_freq = 103000000 + self.auto_gain = auto_gain = True + self.audio_rate = audio_rate = 44100 + + ################################################## + # Message queues (added by grcconvert) + ################################################## + self.msgq_out = blocks_message_sink_0_msgq_out = gr.msg_queue(2) + + ################################################## + # Blocks + ################################################## + self.rtlsdr_source_0 = osmosdr.source( args="numchan=" + str(1) + " " + "rtl_tcp=127.0.0.1:7373" ) + self.rtlsdr_source_0.set_sample_rate(samp_rate) + self.rtlsdr_source_0.set_center_freq(center_freq, 0) + self.rtlsdr_source_0.set_freq_corr(0, 0) + self.rtlsdr_source_0.set_dc_offset_mode(0, 0) + self.rtlsdr_source_0.set_iq_balance_mode(0, 0) + self.rtlsdr_source_0.set_gain_mode(auto_gain, 0) + self.rtlsdr_source_0.set_gain(rf_gain, 0) + self.rtlsdr_source_0.set_if_gain(if_gain, 0) + self.rtlsdr_source_0.set_bb_gain(20, 0) + self.rtlsdr_source_0.set_antenna("", 0) + self.rtlsdr_source_0.set_bandwidth(0, 0) + + self.freq_xlating_fir_filter_xxx_0 = filter.freq_xlating_fir_filter_ccf(samp_rate/demod_rate, (firdes.low_pass(1,samp_rate,demod_rate*0.8,demod_rate*0.3)), offset_freq, samp_rate) + self.fractional_resampler_xx_0 = filter.fractional_resampler_ff(0, demod_rate/float(audio_rate)) + self.blocks_message_sink_0 = blocks.message_sink(gr.sizeof_short*1, blocks_message_sink_0_msgq_out, False) + self.blocks_float_to_short_0 = blocks.float_to_short(1, 3000) + self.blocks_complex_to_imag_0 = blocks.complex_to_imag(1) + self.band_pass_filter_0 = filter.fir_filter_ccc(1, firdes.complex_band_pass( + 1, demod_rate, low_cut, high_cut, 500, firdes.WIN_HAMMING, 6.76)) + self.analog_agc2_xx_0 = analog.agc2_cc(1e-3, 208e-5, 1.0, 1.0) + self.analog_agc2_xx_0.set_max_gain(20) + + ################################################## + # Connections + ################################################## + self.connect((self.rtlsdr_source_0, 0), (self.freq_xlating_fir_filter_xxx_0, 0)) + self.connect((self.blocks_float_to_short_0, 0), (self.blocks_message_sink_0, 0)) + # removed by grcconvert: # self.connect((self.blocks_message_sink_0, msg), (self, 0)) + self.connect((self.analog_agc2_xx_0, 0), (self.blocks_complex_to_imag_0, 0)) + self.connect((self.fractional_resampler_xx_0, 0), (self.blocks_float_to_short_0, 0)) + self.connect((self.blocks_complex_to_imag_0, 0), (self.fractional_resampler_xx_0, 0)) + self.connect((self.band_pass_filter_0, 0), (self.analog_agc2_xx_0, 0)) + self.connect((self.freq_xlating_fir_filter_xxx_0, 0), (self.band_pass_filter_0, 0)) + + +# QT sink close method reimplementation + + def get_samp_rate(self): + return self.samp_rate + + def set_samp_rate(self, samp_rate): + self.samp_rate = samp_rate + self.set_demod_rate(self.samp_rate/(self.samp_rate/16000)) + self.rtlsdr_source_0.set_sample_rate(self.samp_rate) + self.freq_xlating_fir_filter_xxx_0.set_taps((firdes.low_pass(1,self.samp_rate,self.demod_rate*0.8,self.demod_rate*0.3))) + + def get_rf_gain(self): + return self.rf_gain + + def set_rf_gain(self, rf_gain): + self.rf_gain = rf_gain + self.rtlsdr_source_0.set_gain(self.rf_gain, 0) + + def get_offset_freq(self): + return self.offset_freq + + def set_offset_freq(self, offset_freq): + self.offset_freq = offset_freq + self.freq_xlating_fir_filter_xxx_0.set_center_freq(self.offset_freq) + + def get_low_cut(self): + return self.low_cut + + def set_low_cut(self, low_cut): + self.low_cut = low_cut + self.band_pass_filter_0.set_taps(firdes.complex_band_pass(1, self.demod_rate, self.low_cut, self.high_cut, 500, firdes.WIN_HAMMING, 6.76)) + + def get_if_gain(self): + return self.if_gain + + def set_if_gain(self, if_gain): + self.if_gain = if_gain + self.rtlsdr_source_0.set_if_gain(self.if_gain, 0) + + def get_high_cut(self): + return self.high_cut + + def set_high_cut(self, high_cut): + self.high_cut = high_cut + self.band_pass_filter_0.set_taps(firdes.complex_band_pass(1, self.demod_rate, self.low_cut, self.high_cut, 500, firdes.WIN_HAMMING, 6.76)) + + def get_demod_rate(self): + return self.demod_rate + + def set_demod_rate(self, demod_rate): + self.demod_rate = demod_rate + self.band_pass_filter_0.set_taps(firdes.complex_band_pass(1, self.demod_rate, self.low_cut, self.high_cut, 500, firdes.WIN_HAMMING, 6.76)) + self.fractional_resampler_xx_0.set_resamp_ratio(self.demod_rate/float(self.audio_rate)) + self.freq_xlating_fir_filter_xxx_0.set_taps((firdes.low_pass(1,self.samp_rate,self.demod_rate*0.8,self.demod_rate*0.3))) + + def get_center_freq(self): + return self.center_freq + + def set_center_freq(self, center_freq): + self.center_freq = center_freq + self.rtlsdr_source_0.set_center_freq(self.center_freq, 0) + + def get_auto_gain(self): + return self.auto_gain + + def set_auto_gain(self, auto_gain): + self.auto_gain = auto_gain + self.rtlsdr_source_0.set_gain_mode(self.auto_gain, 0) + + def get_audio_rate(self): + return self.audio_rate + + def set_audio_rate(self, audio_rate): + self.audio_rate = audio_rate + self.fractional_resampler_xx_0.set_resamp_ratio(self.demod_rate/float(self.audio_rate)) + +if __name__ == '__main__': + parser = OptionParser(option_class=eng_option, usage="%prog: [options]") + (options, args) = parser.parse_args() + tb = top_block() + tb.start() + tb.wait() + diff --git a/demod/wfm/__init__.py b/demod/wfm/__init__.py new file mode 100755 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/demod/wfm/client_wfm.grc b/demod/wfm/client_wfm.grc new file mode 100755 index 0000000000000000000000000000000000000000..6798bb300f037aeedd01cc3b350c53f583bad725 --- /dev/null +++ b/demod/wfm/client_wfm.grc @@ -0,0 +1,853 @@ +<?xml version='1.0' encoding='ASCII'?> +<flow_graph> + <timestamp>Sat Nov 23 18:26:30 2013</timestamp> + <block> + <key>options</key> + <param> + <key>id</key> + <value>top_block</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>title</key> + <value></value> + </param> + <param> + <key>author</key> + <value></value> + </param> + <param> + <key>description</key> + <value></value> + </param> + <param> + <key>window_size</key> + <value>1280, 1024</value> + </param> + <param> + <key>generate_options</key> + <value>no_gui</value> + </param> + <param> + <key>category</key> + <value>Custom</value> + </param> + <param> + <key>run_options</key> + <value>run</value> + </param> + <param> + <key>run</key> + <value>True</value> + </param> + <param> + <key>max_nouts</key> + <value>0</value> + </param> + <param> + <key>realtime_scheduling</key> + <value></value> + </param> + <param> + <key>_coordinate</key> + <value>(12, -1)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>variable</key> + <param> + <key>id</key> + <value>high_cut</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>value</key> + <value>0</value> + </param> + <param> + <key>_coordinate</key> + <value>(577, 14)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>variable</key> + <param> + <key>id</key> + <value>low_cut</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>value</key> + <value>0</value> + </param> + <param> + <key>_coordinate</key> + <value>(584, 75)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>variable</key> + <param> + <key>id</key> + <value>auto_gain</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>value</key> + <value>True</value> + </param> + <param> + <key>_coordinate</key> + <value>(494, 81)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>variable</key> + <param> + <key>id</key> + <value>demod_rate</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>value</key> + <value>samp_rate/(samp_rate/180000)</value> + </param> + <param> + <key>_coordinate</key> + <value>(10, 77)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>variable</key> + <param> + <key>id</key> + <value>audio_rate</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>value</key> + <value>48000</value> + </param> + <param> + <key>_coordinate</key> + <value>(123, 78)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>variable</key> + <param> + <key>id</key> + <value>samp_rate</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>value</key> + <value>2048000</value> + </param> + <param> + <key>_coordinate</key> + <value>(311, 10)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>variable</key> + <param> + <key>id</key> + <value>offset_freq</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>value</key> + <value>300000</value> + </param> + <param> + <key>_coordinate</key> + <value>(420, 10)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>variable</key> + <param> + <key>id</key> + <value>filter_bw</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>value</key> + <value>200000</value> + </param> + <param> + <key>_coordinate</key> + <value>(228, 77)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>variable</key> + <param> + <key>id</key> + <value>rf_gain</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>value</key> + <value>10</value> + </param> + <param> + <key>_coordinate</key> + <value>(321, 78)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>variable</key> + <param> + <key>id</key> + <value>if_gain</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>value</key> + <value>20</value> + </param> + <param> + <key>_coordinate</key> + <value>(411, 80)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>variable</key> + <param> + <key>id</key> + <value>center_freq</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>value</key> + <value>103000000</value> + </param> + <param> + <key>_coordinate</key> + <value>(202, 11)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>analog_wfm_rcv</key> + <param> + <key>id</key> + <value>analog_wfm_rcv_0</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>quad_rate</key> + <value>samp_rate/(int(samp_rate)/demod_rate)</value> + </param> + <param> + <key>audio_decimation</key> + <value>1</value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>minoutbuf</key> + <value>0</value> + </param> + <param> + <key>maxoutbuf</key> + <value>0</value> + </param> + <param> + <key>_coordinate</key> + <value>(670, 210)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>fractional_resampler_xx</key> + <param> + <key>id</key> + <value>fractional_resampler_xx_0</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>type</key> + <value>float</value> + </param> + <param> + <key>phase_shift</key> + <value>0</value> + </param> + <param> + <key>resamp_ratio</key> + <value>(samp_rate/(int(samp_rate)/demod_rate))/float(audio_rate)</value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>minoutbuf</key> + <value>0</value> + </param> + <param> + <key>maxoutbuf</key> + <value>0</value> + </param> + <param> + <key>_coordinate</key> + <value>(955, 221)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>pad_sink</key> + <param> + <key>id</key> + <value>pad_sink_0</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>label</key> + <value>out</value> + </param> + <param> + <key>type</key> + <value></value> + </param> + <param> + <key>vlen</key> + <value>1</value> + </param> + <param> + <key>num_streams</key> + <value>1</value> + </param> + <param> + <key>optional</key> + <value>False</value> + </param> + <param> + <key>_coordinate</key> + <value>(1169, 48)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>blocks_message_sink</key> + <param> + <key>id</key> + <value>blocks_message_sink_0</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>type</key> + <value>short</value> + </param> + <param> + <key>dont_block</key> + <value>False</value> + </param> + <param> + <key>vlen</key> + <value>1</value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>minoutbuf</key> + <value>0</value> + </param> + <param> + <key>maxoutbuf</key> + <value>0</value> + </param> + <param> + <key>_coordinate</key> + <value>(928, 48)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>blocks_float_to_short</key> + <param> + <key>id</key> + <value>blocks_float_to_short_0</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>vlen</key> + <value>1</value> + </param> + <param> + <key>scale</key> + <value>300000</value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>minoutbuf</key> + <value>0</value> + </param> + <param> + <key>maxoutbuf</key> + <value>0</value> + </param> + <param> + <key>_coordinate</key> + <value>(713, 48)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>freq_xlating_fir_filter_xxx</key> + <param> + <key>id</key> + <value>freq_xlating_fir_filter_xxx_0</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>type</key> + <value>ccc</value> + </param> + <param> + <key>decim</key> + <value>samp_rate/demod_rate</value> + </param> + <param> + <key>taps</key> + <value>firdes.low_pass(1,samp_rate,filter_bw,demod_rate*0.7)</value> + </param> + <param> + <key>center_freq</key> + <value>offset_freq</value> + </param> + <param> + <key>samp_rate</key> + <value>samp_rate</value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>minoutbuf</key> + <value>0</value> + </param> + <param> + <key>maxoutbuf</key> + <value>0</value> + </param> + <param> + <key>_coordinate</key> + <value>(320, 196)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>rtlsdr_source</key> + <param> + <key>id</key> + <value>rtlsdr_source</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>type</key> + <value>fc32</value> + </param> + <param> + <key>args</key> + <value>rtl_tcp=127.0.0.1:7373</value> + </param> + <param> + <key>nchan</key> + <value>1</value> + </param> + <param> + <key>sample_rate</key> + <value>samp_rate</value> + </param> + <param> + <key>freq0</key> + <value>center_freq</value> + </param> + <param> + <key>corr0</key> + <value>0</value> + </param> + <param> + <key>dc_offset_mode0</key> + <value>2</value> + </param> + <param> + <key>iq_balance_mode0</key> + <value>2</value> + </param> + <param> + <key>gain_mode0</key> + <value>auto_gain</value> + </param> + <param> + <key>gain0</key> + <value>rf_gain</value> + </param> + <param> + <key>if_gain0</key> + <value>if_gain</value> + </param> + <param> + <key>bb_gain0</key> + <value>20</value> + </param> + <param> + <key>ant0</key> + <value></value> + </param> + <param> + <key>bw0</key> + <value>0</value> + </param> + <param> + <key>freq1</key> + <value>100e6</value> + </param> + <param> + <key>corr1</key> + <value>0</value> + </param> + <param> + <key>dc_offset_mode1</key> + <value>0</value> + </param> + <param> + <key>iq_balance_mode1</key> + <value>0</value> + </param> + <param> + <key>gain_mode1</key> + <value>0</value> + </param> + <param> + <key>gain1</key> + <value>10</value> + </param> + <param> + <key>if_gain1</key> + <value>20</value> + </param> + <param> + <key>bb_gain1</key> + <value>20</value> + </param> + <param> + <key>ant1</key> + <value></value> + </param> + <param> + <key>bw1</key> + <value>0</value> + </param> + <param> + <key>freq2</key> + <value>100e6</value> + </param> + <param> + <key>corr2</key> + <value>0</value> + </param> + <param> + <key>dc_offset_mode2</key> + <value>0</value> + </param> + <param> + <key>iq_balance_mode2</key> + <value>0</value> + </param> + <param> + <key>gain_mode2</key> + <value>0</value> + </param> + <param> + <key>gain2</key> + <value>10</value> + </param> + <param> + <key>if_gain2</key> + <value>20</value> + </param> + <param> + <key>bb_gain2</key> + <value>20</value> + </param> + <param> + <key>ant2</key> + <value></value> + </param> + <param> + <key>bw2</key> + <value>0</value> + </param> + <param> + <key>freq3</key> + <value>100e6</value> + </param> + <param> + <key>corr3</key> + <value>0</value> + </param> + <param> + <key>dc_offset_mode3</key> + <value>0</value> + </param> + <param> + <key>iq_balance_mode3</key> + <value>0</value> + </param> + <param> + <key>gain_mode3</key> + <value>0</value> + </param> + <param> + <key>gain3</key> + <value>10</value> + </param> + <param> + <key>if_gain3</key> + <value>20</value> + </param> + <param> + <key>bb_gain3</key> + <value>20</value> + </param> + <param> + <key>ant3</key> + <value></value> + </param> + <param> + <key>bw3</key> + <value>0</value> + </param> + <param> + <key>freq4</key> + <value>100e6</value> + </param> + <param> + <key>corr4</key> + <value>0</value> + </param> + <param> + <key>dc_offset_mode4</key> + <value>0</value> + </param> + <param> + <key>iq_balance_mode4</key> + <value>0</value> + </param> + <param> + <key>gain_mode4</key> + <value>0</value> + </param> + <param> + <key>gain4</key> + <value>10</value> + </param> + <param> + <key>if_gain4</key> + <value>20</value> + </param> + <param> + <key>bb_gain4</key> + <value>20</value> + </param> + <param> + <key>ant4</key> + <value></value> + </param> + <param> + <key>bw4</key> + <value>0</value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>minoutbuf</key> + <value>0</value> + </param> + <param> + <key>maxoutbuf</key> + <value>0</value> + </param> + <param> + <key>_coordinate</key> + <value>(21, 154)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <connection> + <source_block_id>blocks_message_sink_0</source_block_id> + <sink_block_id>pad_sink_0</sink_block_id> + <source_key>msg</source_key> + <sink_key>0</sink_key> + </connection> + <connection> + <source_block_id>blocks_float_to_short_0</source_block_id> + <sink_block_id>blocks_message_sink_0</sink_block_id> + <source_key>0</source_key> + <sink_key>0</sink_key> + </connection> + <connection> + <source_block_id>freq_xlating_fir_filter_xxx_0</source_block_id> + <sink_block_id>analog_wfm_rcv_0</sink_block_id> + <source_key>0</source_key> + <sink_key>0</sink_key> + </connection> + <connection> + <source_block_id>rtlsdr_source</source_block_id> + <sink_block_id>freq_xlating_fir_filter_xxx_0</sink_block_id> + <source_key>0</source_key> + <sink_key>0</sink_key> + </connection> + <connection> + <source_block_id>analog_wfm_rcv_0</source_block_id> + <sink_block_id>fractional_resampler_xx_0</sink_block_id> + <source_key>0</source_key> + <sink_key>0</sink_key> + </connection> + <connection> + <source_block_id>fractional_resampler_xx_0</source_block_id> + <sink_block_id>blocks_float_to_short_0</sink_block_id> + <source_key>0</source_key> + <sink_key>0</sink_key> + </connection> +</flow_graph> diff --git a/demod/wfm/top_block.py b/demod/wfm/top_block.py new file mode 100755 index 0000000000000000000000000000000000000000..813fea06b17101929172e28c97622ce42c1ac224 --- /dev/null +++ b/demod/wfm/top_block.py @@ -0,0 +1,166 @@ +#!/usr/bin/env python +################################################## +# Gnuradio Python Flow Graph +# Title: Top Block +# Generated: Mon Nov 25 22:14:56 2013 +################################################## + +from gnuradio import analog +from gnuradio import blocks +from gnuradio import eng_notation +from gnuradio import filter +from gnuradio import gr +from gnuradio.eng_option import eng_option +from gnuradio.filter import firdes +from optparse import OptionParser +import osmosdr + +class top_block(gr.top_block): + + def __init__(self,sample_rate_param): # added by grcconvert + gr.top_block.__init__(self, "Top Block") + + ################################################## + # Variables + ################################################## + self.samp_rate = samp_rate = sample_rate_param # added by grcconvert + self.rf_gain = rf_gain = 10 + self.offset_freq = offset_freq = 300000 + self.low_cut = low_cut = 0 + self.if_gain = if_gain = 20 + self.high_cut = high_cut = 0 + self.filter_bw = filter_bw = 200000 + self.demod_rate = demod_rate = samp_rate/(samp_rate/180000) + self.center_freq = center_freq = 103000000 + self.auto_gain = auto_gain = True + self.audio_rate = audio_rate = 48000 + + ################################################## + # Message queues (added by grcconvert) + ################################################## + self.msgq_out = blocks_message_sink_0_msgq_out = gr.msg_queue(2) + + ################################################## + # Blocks + ################################################## + self.rtlsdr_source = osmosdr.source( args="numchan=" + str(1) + " " + "rtl_tcp=127.0.0.1:7373" ) + self.rtlsdr_source.set_sample_rate(samp_rate) + self.rtlsdr_source.set_center_freq(center_freq, 0) + self.rtlsdr_source.set_freq_corr(0, 0) + self.rtlsdr_source.set_dc_offset_mode(2, 0) + self.rtlsdr_source.set_iq_balance_mode(2, 0) + self.rtlsdr_source.set_gain_mode(auto_gain, 0) + self.rtlsdr_source.set_gain(rf_gain, 0) + self.rtlsdr_source.set_if_gain(if_gain, 0) + self.rtlsdr_source.set_bb_gain(20, 0) + self.rtlsdr_source.set_antenna("", 0) + self.rtlsdr_source.set_bandwidth(0, 0) + + self.freq_xlating_fir_filter_xxx_0 = filter.freq_xlating_fir_filter_ccc(samp_rate/demod_rate, (firdes.low_pass(1,samp_rate,filter_bw,demod_rate*0.7)), offset_freq, samp_rate) + self.fractional_resampler_xx_0 = filter.fractional_resampler_ff(0, (samp_rate/(int(samp_rate)/demod_rate))/float(audio_rate)) + self.blocks_message_sink_0 = blocks.message_sink(gr.sizeof_short*1, blocks_message_sink_0_msgq_out, False) + self.blocks_float_to_short_0 = blocks.float_to_short(1, 300000) + self.analog_wfm_rcv_0 = analog.wfm_rcv( + quad_rate=samp_rate/(int(samp_rate)/demod_rate), + audio_decimation=1, + ) + + ################################################## + # Connections + ################################################## + # removed by grcconvert: # self.connect((self.blocks_message_sink_0, msg), (self, 0)) + self.connect((self.blocks_float_to_short_0, 0), (self.blocks_message_sink_0, 0)) + self.connect((self.freq_xlating_fir_filter_xxx_0, 0), (self.analog_wfm_rcv_0, 0)) + self.connect((self.rtlsdr_source, 0), (self.freq_xlating_fir_filter_xxx_0, 0)) + self.connect((self.analog_wfm_rcv_0, 0), (self.fractional_resampler_xx_0, 0)) + self.connect((self.fractional_resampler_xx_0, 0), (self.blocks_float_to_short_0, 0)) + + +# QT sink close method reimplementation + + def get_samp_rate(self): + return self.samp_rate + + def set_samp_rate(self, samp_rate): + self.samp_rate = samp_rate + self.set_demod_rate(self.samp_rate/(self.samp_rate/180000)) + self.fractional_resampler_xx_0.set_resamp_ratio((self.samp_rate/(int(self.samp_rate)/self.demod_rate))/float(self.audio_rate)) + self.freq_xlating_fir_filter_xxx_0.set_taps((firdes.low_pass(1,self.samp_rate,self.filter_bw,self.demod_rate*0.7))) + self.rtlsdr_source.set_sample_rate(self.samp_rate) + + def get_rf_gain(self): + return self.rf_gain + + def set_rf_gain(self, rf_gain): + self.rf_gain = rf_gain + self.rtlsdr_source.set_gain(self.rf_gain, 0) + + def get_offset_freq(self): + return self.offset_freq + + def set_offset_freq(self, offset_freq): + self.offset_freq = offset_freq + self.freq_xlating_fir_filter_xxx_0.set_center_freq(self.offset_freq) + + def get_low_cut(self): + return self.low_cut + + def set_low_cut(self, low_cut): + self.low_cut = low_cut + + def get_if_gain(self): + return self.if_gain + + def set_if_gain(self, if_gain): + self.if_gain = if_gain + self.rtlsdr_source.set_if_gain(self.if_gain, 0) + + def get_high_cut(self): + return self.high_cut + + def set_high_cut(self, high_cut): + self.high_cut = high_cut + + def get_filter_bw(self): + return self.filter_bw + + def set_filter_bw(self, filter_bw): + self.filter_bw = filter_bw + self.freq_xlating_fir_filter_xxx_0.set_taps((firdes.low_pass(1,self.samp_rate,self.filter_bw,self.demod_rate*0.7))) + + def get_demod_rate(self): + return self.demod_rate + + def set_demod_rate(self, demod_rate): + self.demod_rate = demod_rate + self.fractional_resampler_xx_0.set_resamp_ratio((self.samp_rate/(int(self.samp_rate)/self.demod_rate))/float(self.audio_rate)) + self.freq_xlating_fir_filter_xxx_0.set_taps((firdes.low_pass(1,self.samp_rate,self.filter_bw,self.demod_rate*0.7))) + + def get_center_freq(self): + return self.center_freq + + def set_center_freq(self, center_freq): + self.center_freq = center_freq + self.rtlsdr_source.set_center_freq(self.center_freq, 0) + + def get_auto_gain(self): + return self.auto_gain + + def set_auto_gain(self, auto_gain): + self.auto_gain = auto_gain + self.rtlsdr_source.set_gain_mode(self.auto_gain, 0) + + def get_audio_rate(self): + return self.audio_rate + + def set_audio_rate(self, audio_rate): + self.audio_rate = audio_rate + self.fractional_resampler_xx_0.set_resamp_ratio((self.samp_rate/(int(self.samp_rate)/self.demod_rate))/float(self.audio_rate)) + +if __name__ == '__main__': + parser = OptionParser(option_class=eng_option, usage="%prog: [options]") + (options, args) = parser.parse_args() + tb = top_block() + tb.start() + tb.wait() + diff --git a/grcconvert.py b/grcconvert.py new file mode 100755 index 0000000000000000000000000000000000000000..ea89929a26b180d8081e0d59ad2e116ecdfb27cd --- /dev/null +++ b/grcconvert.py @@ -0,0 +1,88 @@ +""" +Copyright (c) 2013 Andras Retzler + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +Version history +--------------- +2013-08 +- initial version + +2013-11-12 +- updated to GNU Radio 3.7 + - changed gr_message_sink_0_msgq_out to blocks_message_sink_0_msgq_out + +2013-11-25 +- almost rewritten, now supports multiple message sinks, and regexp_replaces + +""" + +import sys +import re + +def main(filename, regexp_replaces=[]): + f=open(filename,"r") + data=f.read() + f.close() + + #is it already converted? + if data.count("(added by grcconvert)"): + print "[grcconvert:info] File has already been converted: "+filename + return + + # process regexps + for anything in regexp_replaces: + data=re.sub(anything[0],anything[1],data) + + # count how many message sinks we have + n_sinks=data.count("self.connect((self.blocks_message_sink_") + print "[grcconvert:info] "+filename+" has {0} message sinks.".format(n_sinks) + data=data.replace("self.connect((self.blocks_message_sink_","# removed by grcconvert: # self.connect((self.blocks_message_sink_") + if not n_sinks: + print "[grcconvert:error] No message sinks found." + return + + lines=data.split("\n") + + # we find out where we should insert our line + insert_here=0 + for i, l in enumerate(lines): + if l.count("# Blocks"): + insert_here=i + break + insert_here-=1 + if insert_here<=0: + print "[grcconvert:error] Not a GNUradio-companion generated top_level.py file." + return + + text=" ##################################################\n # Message queues (added by grcconvert)\n ##################################################\n" + for i in range(0,n_sinks): text+=" self.msgq_out = blocks_message_sink_{0}_msgq_out = gr.msg_queue(2)\n".format(i) + lines=lines[:insert_here]+[text]+lines[insert_here:] + + f=open(filename,"w") + + f.write("\n".join(lines)) + f.close() + print "[grcconvert:info] converted:", filename + +if __name__=="__main__": + print "[grcconvert:info] msgq code inserter for GNUradio-companion generated top_level.py files" + filename=sys.argv[1] if len(sys.argv)>1 else "top_block.py" + main(filename) + diff --git a/htdocs/favicon.ico b/htdocs/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..f8c9a2ae9562c79442e884e6e4b23fc43ec14f42 Binary files /dev/null and b/htdocs/favicon.ico differ diff --git a/htdocs/gfx/get_unibody8pro.txt b/htdocs/gfx/get_unibody8pro.txt new file mode 100644 index 0000000000000000000000000000000000000000..c1a4d3c9100de96e7ecd634cafad4b2977c8c466 --- /dev/null +++ b/htdocs/gfx/get_unibody8pro.txt @@ -0,0 +1,20 @@ +You can get that font from here: + +http://www.underware.nl/fonts/unibody/download/ + +You will have to convert it to a web font by using this tool: + +http://www.fontsquirrel.com/tools/webfont-generator + +You will get: + +unibody8pro-regular-webfont.eot +unibody8pro-regular-webfont.ttf + +Copy these over here, and frequency will look okay. + +As we don't like these kind of dependencies, later this font will be replaced with an open-source one. + +Regards, + +Andris, HA7ILM diff --git a/htdocs/gfx/openwebrx-avatar-background.png b/htdocs/gfx/openwebrx-avatar-background.png new file mode 100755 index 0000000000000000000000000000000000000000..e52cb0b951a27ae1e759d229c802e98b16ae5067 Binary files /dev/null and b/htdocs/gfx/openwebrx-avatar-background.png differ diff --git a/htdocs/gfx/openwebrx-avatar.png b/htdocs/gfx/openwebrx-avatar.png new file mode 100755 index 0000000000000000000000000000000000000000..91486a61d0d47522ce07d48c579771a825f9efef Binary files /dev/null and b/htdocs/gfx/openwebrx-avatar.png differ diff --git a/htdocs/gfx/openwebrx-background-cool-blue.png b/htdocs/gfx/openwebrx-background-cool-blue.png new file mode 100755 index 0000000000000000000000000000000000000000..7430bd8a8461217a5c07ef86558afc2bcfcf98c3 Binary files /dev/null and b/htdocs/gfx/openwebrx-background-cool-blue.png differ diff --git a/htdocs/gfx/openwebrx-background-lingrad.png b/htdocs/gfx/openwebrx-background-lingrad.png new file mode 100755 index 0000000000000000000000000000000000000000..48537f7a0ffbe3921191cda68f325e30420b5943 Binary files /dev/null and b/htdocs/gfx/openwebrx-background-lingrad.png differ diff --git a/htdocs/gfx/openwebrx-logo-big.png b/htdocs/gfx/openwebrx-logo-big.png new file mode 100755 index 0000000000000000000000000000000000000000..dcafb2ee3778bc1d880ab32e650d36fab91b0957 Binary files /dev/null and b/htdocs/gfx/openwebrx-logo-big.png differ diff --git a/htdocs/gfx/openwebrx-rx-details-arrow-up.png b/htdocs/gfx/openwebrx-rx-details-arrow-up.png new file mode 100755 index 0000000000000000000000000000000000000000..0baccd041dbba4f6807b3ba87436f6998af9e540 Binary files /dev/null and b/htdocs/gfx/openwebrx-rx-details-arrow-up.png differ diff --git a/htdocs/gfx/openwebrx-rx-details-arrow.png b/htdocs/gfx/openwebrx-rx-details-arrow.png new file mode 100755 index 0000000000000000000000000000000000000000..9995118f0d1c4e705687a3ed9a5dfe7952e6e13c Binary files /dev/null and b/htdocs/gfx/openwebrx-rx-details-arrow.png differ diff --git a/htdocs/gfx/openwebrx-scale-background.png b/htdocs/gfx/openwebrx-scale-background.png new file mode 100755 index 0000000000000000000000000000000000000000..7fbb4d2490317a99bde1d4882e72713ae0ce31f4 Binary files /dev/null and b/htdocs/gfx/openwebrx-scale-background.png differ diff --git a/htdocs/gfx/openwebrx-top-logo.png b/htdocs/gfx/openwebrx-top-logo.png new file mode 100755 index 0000000000000000000000000000000000000000..477242524b9434230467dcccadfbd489c37607e0 Binary files /dev/null and b/htdocs/gfx/openwebrx-top-logo.png differ diff --git a/htdocs/gfx/openwebrx-top-photo.jpg b/htdocs/gfx/openwebrx-top-photo.jpg new file mode 100755 index 0000000000000000000000000000000000000000..cf521c75251ca17ab939a00643212f61358c662d Binary files /dev/null and b/htdocs/gfx/openwebrx-top-photo.jpg differ diff --git a/htdocs/gfx/webrx-bottom-bar.png b/htdocs/gfx/webrx-bottom-bar.png new file mode 100755 index 0000000000000000000000000000000000000000..023a16a719a63466eae092911703ef858e05a742 Binary files /dev/null and b/htdocs/gfx/webrx-bottom-bar.png differ diff --git a/htdocs/gfx/webrx-ha5kfu-top-logo.png b/htdocs/gfx/webrx-ha5kfu-top-logo.png new file mode 100755 index 0000000000000000000000000000000000000000..2686eef0aa16eceddfa68cc79dbcd626de4bec4d Binary files /dev/null and b/htdocs/gfx/webrx-ha5kfu-top-logo.png differ diff --git a/htdocs/gfx/webrx-top-photo.jpg b/htdocs/gfx/webrx-top-photo.jpg new file mode 100755 index 0000000000000000000000000000000000000000..47846bc80fff59b51284b266ab31a00352f179b0 Binary files /dev/null and b/htdocs/gfx/webrx-top-photo.jpg differ diff --git a/htdocs/index.wrx b/htdocs/index.wrx new file mode 100755 index 0000000000000000000000000000000000000000..a3c617dd618fc6fd9f0120dca8145d18b84015e0 --- /dev/null +++ b/htdocs/index.wrx @@ -0,0 +1,95 @@ +<!DOCTYPE HTML> +<!-- +OpenWebRX (c) Copyright 2013 Andras Retzler <ha7ilm@sdr.hu> + +This file is part of OpenWebRX. + + OpenWebRX is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + OpenWebRX is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with OpenWebRX. If not, see <http://www.gnu.org/licenses/>. +--> +<html> + <head> + <title>OpenWebRX | Open Source Web-based SDR for everyone!</title> + <script type="text/javascript"> + //Local variables + client_id="%[CLIENT_ID]"; + ws_url="%[WS_URL]"; + rx_photo_height=%[RX_PHOTO_HEIGHT]; + </script> + <script src="openwebrx.js"></script> + <link rel="stylesheet" type="text/css" href="openwebrx.css" /> + <meta charset="utf-8"> + </head> + <body onload="webrx_init();" onresize="webrx_resize();"> +<div id="webrx-page-container"> + <div id="webrx-top-container"> + <div id="webrx-top-photo-clip"> + <img src="gfx/openwebrx-top-photo.jpg" id="webrx-top-photo"/> + <div id="webrx-rx-photo-title">%[RX_PHOTO_TITLE]</div> + <div id="webrx-rx-photo-desc">%[RX_PHOTO_DESC]</div> + </div> + <div id="webrx-top-bar-background" class="webrx-top-bar-parts"></div> + <div id="webrx-top-bar" class="webrx-top-bar-parts"> + <a href="http://openwebrx.org/" target="_blank"><img src="gfx/openwebrx-top-logo.png" id="webrx-top-logo" /></a> + <a href="http://ha5kfu.sch.bme.hu/" target="_blank"><img src="gfx/webrx-ha5kfu-top-logo.png" id="webrx-ha5kfu-top-logo" /></a> + <img id="webrx-rx-avatar-background" src="gfx/openwebrx-avatar-background.png" onclick="toggle_rx_photo();"/> + <img id="webrx-rx-avatar" src="gfx/openwebrx-avatar.png" onclick="toggle_rx_photo();"/> + <div id="webrx-rx-title" onclick="toggle_rx_photo();">%[RX_TITLE]</div> + <div id="webrx-rx-desc" onclick="toggle_rx_photo();">%[RX_DESC]</div> + <div id="openwebrx-rx-details-arrow"> + <a id="openwebrx-rx-details-arrow-up" onclick="toggle_rx_photo();"><img src="gfx/openwebrx-rx-details-arrow-up.png" /></a> + <a id="openwebrx-rx-details-arrow-down" onclick="toggle_rx_photo();"><img src="gfx/openwebrx-rx-details-arrow.png" /></a> + </div> + </div> + </div> + <div id="webrx-main-container"> + <div id="openwebrx-scale-container"> + <canvas id="openwebrx-scale-canvas" width="0" height="0"></canvas> + </div> + <div id="webrx-canvas-container"> + <div id="openwebrx-phantom-canvas"></div> + <!-- add canvas here by javascript --> + </div> + <div id="openwebrx-panels-container"> + <div class="openwebrx-panel" data-panel-name="client-params" data-panel-pos="right" data-panel-order="0" data-panel-size="245,70"> + <div id="webrx-actual-freq">---.--- MHz</div> + <div id="webrx-mouse-freq">---.--- MHz</div> + <!--<div class="openwebrx-button" onclick="ws.send('SET mod=wfm');" >WFM</div>--> + <div class="openwebrx-button" onclick="demodulator_analog_replace('nfm');">FM</div> + <div class="openwebrx-button" onclick="demodulator_analog_replace('am');">AM</div> + <div class="openwebrx-button" onclick="demodulator_analog_replace('lsb');">LSB</div> + <div class="openwebrx-button" onclick="demodulator_analog_replace('usb');">USB</div> + <div class="openwebrx-button" onclick="demodulator_analog_replace('cw');">CW</div> + </div> + <div class="openwebrx-panel" id="webrx-config" data-panel-name="debug" data-panel-pos="left" data-panel-order="0" data-panel-size="570,180"> + <div class="openwebrx-panel-inner"> + <strong> openwebrx.js (pre-alpha) client log </strong><br /> + Authors: <a href="javascript:sendmail2('pi7qtu=alz$pc');">HA7ILM</a>; please send us bug reports and suggestions.<br/> + Warning! OpenWebRX is still in the pre-alpha stage.<br/> + Client status: <span id="openwebrx-client-status"> + audio received with <span id="openwebrx-audio-sps">-</span> sps + <span id="openwebrx-problems"></span><br/> + Server status: <span id="openwebrx-server-status">no information</span><br/> + Your client ID is: %[CLIENT_ID]<br /> + <div id="openwebrx-debugdiv"></div> + </div> + </div> + <div class="openwebrx-panel" data-panel-name="client-under-devel" data-panel-pos="none" data-panel-order="0" data-panel-size="245,55" style="background-color: Red;"> + <span style="font-size: 15pt; font-weight: bold;">Under construction</span> + <br />We're working on the code right now, so the application might fail. + </div> + </div> + </div> +</div> + </body> +</html> diff --git a/htdocs/openwebrx.css b/htdocs/openwebrx.css new file mode 100755 index 0000000000000000000000000000000000000000..3c9f67d23dd8698d6151ed617d317832bd35eba8 --- /dev/null +++ b/htdocs/openwebrx.css @@ -0,0 +1,414 @@ +/* +OpenWebRX (c) Copyright 2013 Andras Retzler <ha7ilm@sdr.hu> + +This file is part of OpenWebRX. + + OpenWebRX is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + OpenWebRX is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with OpenWebRX. If not, see <http://www.gnu.org/licenses/>. +*/ + +html, body +{ + margin: 0; + padding: 0; + height: 100%; + font-family: "DejaVu Sans", Verdana, Geneva, sans-serif; + overflow: hidden; +} + +#webrx-top-container +{ + position: relative; + z-index:1000; +} + +.webrx-top-bar-parts +{ + position: absolute; + top: 0px; + left: 0px; + width:100%; + height:67px; +} + +#webrx-top-bar-background +{ + background-color: #808080; + opacity: 0.15; + filter:alpha(opacity=15); +} + +#webrx-top-bar +{ + margin:0; + padding:0; +} + + +#webrx-top-logo +{ + position: absolute; + top: 12px; + left: 15px; +} + +#webrx-ha5kfu-top-logo +{ + position: absolute; + top: 19px; + right: 15px; +} + +#webrx-top-photo +{ + width: 100%; + display: block; +} + +#webrx-rx-avatar-background +{ + cursor:pointer; + position: absolute; + left: 285px; + top: 6px; +} + +#webrx-rx-avatar +{ + cursor:pointer; + position: absolute; + left: 289px; + top: 10px; + width: 46px; + height: 46px; +} + +#webrx-top-photo-clip +{ + max-height: 350px; + overflow: hidden; + position: relative; +} + +/*#webrx-bottom-bar +{ + position: absolute; + bottom: 0px; + width: 100%; + height: 117px; + background-image:url(gfx/webrx-bottom-bar.png); +}*/ + +#webrx-page-container +{ + min-height:100%; + position:relative; +} + +/*#webrx-photo-gradient-left +{ + position: absolute; + bottom: 0px; + left: 0px; + background-image:url(gfx/webrx-photo-gradient-corner.png); + width: 59px; + height: 92px; + +} + +#webrx-photo-gradient-middle +{ + position: absolute; + bottom: 0px; + left: 59px; + right: 59px; + height: 92px; + background-image:url(gfx/webrx-photo-gradient-middle.png); +} + +#webrx-photo-gradient-right +{ + position: absolute; + bottom: 0px; + right: 0px; + background-image:url(gfx/webrx-photo-gradient-corner.png); + width: 59px; + height: 92px; + -webkit-transform:scaleX(-1); + -moz-transform:scaleX(-1); + -ms-transform:scaleX(-1); + -o-transform:scaleX(-1); + transform:scaleX(-1); +}*/ + +#webrx-rx-photo-title +{ + position: absolute; + left: 15px; + top: 78px; + color: White; + font-size: 16pt; + text-shadow: 1px 1px 4px #444; + opacity: 1; +} + +#webrx-rx-photo-desc +{ + position: absolute; + left: 15px; + top: 105px; + color: White; + font-size: 10pt; + font-weight: bold; + text-shadow: 0px 0px 6px #444; + opacity: 1; +} + +#webrx-rx-photo-desc a +{ + color: #5ca8ff; + text-shadow: none; +} + +#webrx-rx-title +{ + white-space:nowrap; + overflow: hidden; + cursor:pointer; + position: absolute; + left: 350px; + top: 13px; + font-family: "DejaVu Sans", Verdana, Geneva, sans-serif; + color: #909090; + font-size: 11pt; + font-weight: bold; +} + +#webrx-rx-desc +{ + white-space:nowrap; + overflow: hidden; + cursor:pointer; + font-size: 10pt; + color: #909090; + position: absolute; + left: 350px; + top: 34px; +} + +#openwebrx-rx-details-arrow +{ + cursor:pointer; + position: absolute; + left: 470px; + top: 51px; +} + +#openwebrx-rx-details-arrow a +{ + margin: 0; + padding: 0; +} + +#openwebrx-rx-details-arrow-down +{ + display:none; +} + +/*canvas#waterfall-canvas +{ + border-style: none; + border-width: 1px; + height: 150px; + width: 100%; +}*/ + +#openwebrx-scale-container +{ + height: 47px; + background-image: url("gfx/openwebrx-scale-background.png"); + background-repeat: repeat-x; + overflow: hidden; + z-index:1000; + position: relative; +} + +#webrx-canvas-container +{ + /*background-image:url('gfx/openwebrx-blank-background-1.jpg');*/ + position: relative; + height: 2000px; + overflow-y: scroll; + overflow-x: hidden; + /*background-color: #646464;*/ + /*background-image: -webkit-linear-gradient(top, rgba(247,247,247,1) 0%, rgba(0,0,0,1) 100%);*/ + background-image: url('gfx/openwebrx-background-cool-blue.png'); + background-repeat: no-repeat; + background-color: #1e5f7f; + cursor: crosshair; +} + +#webrx-canvas-container canvas +{ + position: absolute; + border-style: none; +} + +#openwebrx-phantom-canvas +{ + position: absolute; + width: 0px; + height: 0px; +} + +/*#openwebrx-canvas-gradient-background +{ + overflow: hidden; + width: 100%; + height: 396px; +}*/ + +/*#webrx-debugdiv +{ + font-size: 10pt; + /*overflow-y:scroll;*/ +/*}*/ + +#webrx-main-container +{ + position: relative; + width: 100%; + margin: 0; + padding: 0; +} + +.webrx-error +{ + font-weight: bold; + color: #ff6262; +} + +#openwebrx-problems span +{ + background: #ff6262; + padding: 3px; + font-size: 8pt; + color: white; + font-weight: bold; + border-radius: 4px; + -moz-border-radius: 4px; + margin: 0px 2px 0px 2px; +} + +/*#webrx-freq-show +{ + visibility: hidden; + position: absolute; + top: 0px; + left: 0px; + padding: 5px; + font-weight: bold; + border-radius: 10px; + -moz-border-radius: 10px; + background-color: #999999; + color: White; + z-index:9999; /*should be higher? + +}*/ + +@font-face { + font-family: 'unibody_8_pro_regregular'; + src: url('gfx/unibody8pro-regular-webfont.eot'); + src: url('gfx/unibody8pro-regular-webfont.ttf'); + font-weight: normal; + font-style: normal; +} + +#webrx-actual-freq +{ + width: 100%; + text-align: left; + font-size: 16pt; + font-family: 'unibody_8_pro_regregular'; + padding: 0; + margin: 0; + line-height:22px; + +} + +#webrx-mouse-freq +{ + width: 100%; + text-align: left; + font-size: 10pt; + color: #AAA; + font-family: 'unibody_8_pro_regregular'; + margin-bottom: 5px; +} + +.openwebrx-panel +{ + visibility: hidden; + background-color: #575757; + padding: 10px; + color: white; + position: fixed; + font-size: 10pt; + border-radius: 15px; + -moz-border-radius: 15px; +} + +.openwebrx-panel a +{ + color: #5ca8ff; + text-shadow: none; +} + +.openwebrx-panel-inner +{ + overflow-y: auto; + overflow-x: hidden; + height: 100%; +} + +.openwebrx-button +{ + background-color: #373737; + padding: 5px; + border-radius: 5px; + -moz-border-radius: 5px; + color: White; + font-weight: bold; + width: auto; + float: left; + margin-right: 5px; + cursor: pointer; + background:-webkit-gradient( linear, left top, left bottom, color-stop(0.0 , #373737), color-stop(1, #4F4F4F) ); + background:-moz-linear-gradient( center top, #373737 0%, #4F4F4F 100% ); +} + +.openwebrx-button:hover +{ + /*background:-webkit-gradient( linear, left top, left bottom, color-stop(0.0 , #3F3F3F), color-stop(1, #777777) ); + background:-moz-linear-gradient( center top, #373737 5%, #4F4F4F 100% );*/ + background: #474747; + color: #FFFF50; +} + +.openwebrx-button:active +{ + background: #777777; + color: #FFFF50; +} + +. diff --git a/htdocs/openwebrx.js b/htdocs/openwebrx.js new file mode 100755 index 0000000000000000000000000000000000000000..db5c265285bbda372781280852c641289e4c985c --- /dev/null +++ b/htdocs/openwebrx.js @@ -0,0 +1,1532 @@ +/* + +OpenWebRX (c) Copyright 2013 Andras Retzler <ha7ilm@sdr.hu> + +This file is part of OpenWebRX. + + OpenWebRX is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + OpenWebRX is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with OpenWebRX. If not, see <http://www.gnu.org/licenses/>. + +*/ + +function arrayBufferToString(buf) { + //http://stackoverflow.com/questions/6965107/converting-between-strings-and-arraybuffers + return String.fromCharCode.apply(null, new Uint8Array(buf)); +} + +//Chrome console: +//ws.send("SET center_freq=100000000") + +var bandwidth; +var center_freq; +var audio_buffer_current_size_debug=0; +var audio_buffer_current_size=0; +var fft_size; +var fft_fps; +var waterfall_setup_done=0; +var waterfall_queue = []; +var waterfall_timer; + +/*function fade(something,from,to,time_ms,fps) +{ + something.style.opacity=from; + something.fade_i=0; + n_of_iters=time_ms/(1000/fps); + change=(to-from)/(n_of_iters-1); + + something.fade_timer=window.setInterval( + function(){ + if(something.fade_i++<n_of_iters) + something.style.opacity=parseFloat(something.style.opacity)+change; + else + {something.style.opacity=to; window.clearInterval(something.fade_timer); } + },1000/fps); +}*/ + +var rx_photo_state=1; + +function e(what) { return document.getElementById(what); } + +function init_rx_photo() +{ + e("webrx-top-photo-clip").style.maxHeight=rx_photo_height.toString()+"px"; + window.setTimeout(function() { animate(e("webrx-rx-photo-title"),"opacity","",1,0,1,500,30); },1000); + window.setTimeout(function() { animate(e("webrx-rx-photo-desc"),"opacity","",1,0,1,500,30); },1500); + window.setTimeout(function() { close_rx_photo() },2500); +} + +function toggle_rx_photo() +{ + if(rx_photo_state) close_rx_photo(); + else open_rx_photo() +} + +function close_rx_photo() +{ + rx_photo_state=0; + animate_to(e("webrx-top-photo-clip"),"maxHeight","px",67,0.93,1000,60,function(){resize_waterfall_container(true);}); + e("openwebrx-rx-details-arrow-down").style.display="block"; + e("openwebrx-rx-details-arrow-up").style.display="none"; +} + +function open_rx_photo() +{ + rx_photo_state=1; + e("webrx-rx-photo-desc").style.opacity=1; + e("webrx-rx-photo-title").style.opacity=1; + animate_to(e("webrx-top-photo-clip"),"maxHeight","px",rx_photo_height,0.93,1000,60,function(){resize_waterfall_container(true);}); + e("openwebrx-rx-details-arrow-down").style.display="none"; + e("openwebrx-rx-details-arrow-up").style.display="block"; +} + +function style_value(of_what,which) +{ + if(of_what.currentStyle) return of_what.currentStyle[which]; + else if (window.getComputedStyle) return document.defaultView.getComputedStyle(of_what,null).getPropertyValue(which); +} + +// ======================================================== +// ================= ANIMATION ROUTINES ================= +// ======================================================== + +function animate(object,style_name,unit,from,to,accel,time_ms,fps,to_exec) +{ + //console.log(object.className); + if(typeof to_exec=="undefined") to_exec=0; + object.style[style_name]=from.toString()+unit; + object.anim_i=0; + n_of_iters=time_ms/(1000/fps); + change=(to-from)/(n_of_iters); + if(typeof object.anim_timer!="undefined") { window.clearInterval(object.anim_timer); } + object.anim_timer=window.setInterval( + function(){ + if(object.anim_i++<n_of_iters) + { + if(accel==1) object.style[style_name]=(parseFloat(object.style[style_name])+change).toString()+unit; + else + { + remain=parseFloat(object.style[style_name])-to; + if(Math.abs(remain)>9||unit!="px") new_val=(to+accel*remain); + else {if(Math.abs(remain)<2) new_val=to; + else new_val=to+remain-(remain/Math.abs(remain));} + object.style[style_name]=new_val.toString()+unit; + } + } + else + {object.style[style_name]=to.toString()+unit; window.clearInterval(object.anim_timer); delete object.anim_timer; } + if(to_exec!=0) to_exec(); + },1000/fps); +} + +function animate_to(object,style_name,unit,to,accel,time_ms,fps,to_exec) +{ + from=parseFloat(style_value(object,style_name)); + animate(object,style_name,unit,from,to,accel,time_ms,fps,to_exec); +} + + +// ======================================================== +// ================ DEMODULATOR ROUTINES ================ +// ======================================================== + +demodulators=[] + +demodulator_color_index=0; +demodulator_colors=["#ffff00", "#00ff00", "#00ffff", "#058cff", "#ff9600", "#a1ff39", "#ff4e39", "#ff5dbd"] +function demodulators_get_next_color() +{ + if(demodulator_color_index>=demodulator_colors.length) demodulator_color_index=0; + return(demodulator_colors[demodulator_color_index++]); +} + +function demod_envelope_draw(range, from, to, color, line) +{ // ____ + // Draws a standard filter envelope like this: _/ \_ + // Parameters are given in offset frequency (Hz). + // Envelope is drawn on the scale canvas. + // A "drag range" object is returned, containing information about the draggable areas of the envelope + // (beginning, ending and the line showing the offset frequency). + if(typeof color == "undefined") color="#ffff00"; //yellow + env_bounding_line_w=5; // + env_att_w=5; // _______ ___env_h2 in px ___|_____ + env_h1=17; // _/| \_ ___env_h1 in px _/ |_ \_ + env_h2=5; // |||env_att_line_w |_env_lineplus + env_lineplus=1; // ||env_bounding_line_w + env_line_click_area=6; + //range=get_visible_freq_range(); + from_px=scale_px_from_freq(from,range); + to_px=scale_px_from_freq(to,range); + if(to_px<from_px) /* swap'em */ { temp_px=to_px; to_px=from_px; from_px=temp_px; } + + /*from_px-=env_bounding_line_w/2; + to_px+=env_bounding_line_w/2;*/ + from_px-=(env_att_w+env_bounding_line_w); + to_px+=(env_att_w+env_bounding_line_w); + // do drawing: + scale_ctx.lineWidth=3; + scale_ctx.strokeStyle=color; + scale_ctx.fillStyle = color; + var drag_ranges={ envelope_on_screen: false, line_on_screen: false }; + if(!(to_px<0||from_px>window.innerWidth)) // out of screen? + { + drag_ranges.beginning={x1:from_px, x2: from_px+env_bounding_line_w+env_att_w}; + drag_ranges.ending={x1:to_px-env_bounding_line_w-env_att_w, x2: to_px}; + drag_ranges.whole_envelope={x1:from_px, x2: to_px}; + drag_ranges.envelope_on_screen=true; + scale_ctx.beginPath(); + scale_ctx.moveTo(from_px,env_h1); + scale_ctx.lineTo(from_px+env_bounding_line_w, env_h1); + scale_ctx.lineTo(from_px+env_bounding_line_w+env_att_w, env_h2); + scale_ctx.lineTo(to_px-env_bounding_line_w-env_att_w, env_h2); + scale_ctx.lineTo(to_px-env_bounding_line_w, env_h1); + scale_ctx.lineTo(to_px, env_h1); + scale_ctx.globalAlpha = 0.3; + scale_ctx.fill(); + scale_ctx.globalAlpha = 1; + scale_ctx.stroke(); + } + if(typeof line != "undefined") // out of screen? + { + line_px=scale_px_from_freq(line,range); + if(!(line_px<0||line_px>window.innerWidth)) + { + drag_ranges.line={x1:line_px-env_line_click_area/2, x2: line_px+env_line_click_area/2}; + drag_ranges.line_on_screen=true; + scale_ctx.moveTo(line_px,env_h1+env_lineplus); + scale_ctx.lineTo(line_px,env_h2-env_lineplus); + scale_ctx.stroke(); + } + } + return drag_ranges; +} + +function demod_envelope_where_clicked(x, drag_ranges, key_modifiers) +{ // Check exactly what the user has clicked based on ranges returned by demod_envelope_draw(). + in_range=function(x,range) { return range.x1<=x&&range.x2>=x; } + dr=demodulator.draggable_ranges; + + if(key_modifiers.shiftKey) + { + //Check first: shift + center drag emulates BFO knob + if(drag_ranges.line_on_screen&&in_range(x,drag_ranges.line)) return dr.bfo; + //Check second: shift + envelope drag emulates PBF knob + if(drag_ranges.envelope_on_screen&&in_range(x,drag_ranges.whole_envelope)) return dr.pbs; + } + if(drag_ranges.envelope_on_screen) + { + // For low and high cut: + if(in_range(x,drag_ranges.beginning)) return dr.beginning; + if(in_range(x,drag_ranges.ending)) return dr.ending; + // Last priority: having clicked anything else on the envelope, without holding the shift key + if(in_range(x,drag_ranges.whole_envelope)) return dr.anything_else; + } + return dr.none; //User doesn't drag the envelope for this demodulator +} + +//******* class demodulator ******* +// this can be used as a base class for ANY demodulator +demodulator=function(offset_frequency) +{ + //console.log("this too"); + this.offset_frequency=offset_frequency; + this.has_audio_output=true; + this.has_text_output=false; + this.envelope={}; + this.color=demodulators_get_next_color(); + this.stop=function(){}; +} +//ranges on filter envelope that can be dragged: +demodulator.draggable_ranges={none: 0, beginning:1 /*from*/, ending: 2 /*to*/, anything_else: 3, bfo: 4 /*line (while holding shift)*/, pbs: 5 } //to which parameter these correspond in demod_envelope_draw() + +//******* class demodulator_default_analog ******* +// This can be used as a base for basic audio demodulators. +// It already supports most basic modulations used for ham radio and commercial services: AM/FM/LSB/USB + +demodulator_response_time=100; +//in ms; if we don't limit the number of SETs sent to the server, audio will underrun (possibly output buffer is cleared on SETs in GNU Radio + +function demodulator_default_analog(offset_frequency,subtype) +{ + //console.log("hopefully this happens"); + //http://stackoverflow.com/questions/4152931/javascript-inheritance-call-super-constructor-or-use-prototype-chain + demodulator.call(this,offset_frequency); + this.subtype=subtype; + this.filter={ + min_passband: 100, + high_cut_limit: audio_context.sampleRate/2, + low_cut_limit: -audio_context.sampleRate/2 + }; + //Subtypes only define some filter parameters and the mod string sent to server, + //so you may set these parameters in your custom child class. + //Why? As of demodulation is done on the server, difference is mainly on the server side. + this.server_mod=subtype; + if(subtype=="lsb") + { + this.low_cut=-3000; + this.high_cut=-300; + this.server_mod="ssb"; + } + else if(subtype=="usb") + { + this.low_cut=300; + this.high_cut=3000; + this.server_mod="ssb"; + } + else if(subtype=="cw") + { + this.low_cut=700; + this.high_cut=900; + this.server_mod="ssb"; + } + else if(subtype=="nfm") + { + this.low_cut=-4000; + this.high_cut=4000; + } + else if(subtype=="am") + { + this.low_cut=-4000; + this.high_cut=4000; + } + + this.wait_for_timer=false; + this.set_after=false; + this.set=function() + { //set() is a wrapper to call doset(), but it ensures that doset won't execute more frequently than demodulator_response_time. + if(!this.wait_for_timer) + { + this.doset(false); + this.set_after=false; + this.wait_for_timer=true; + timeout_this=this; //http://stackoverflow.com/a/2130411 + window.setTimeout(function() { + timeout_this.wait_for_timer=false; + if(timeout_this.set_after) timeout_this.set(); + },demodulator_response_time); + } + else + { + this.set_after=true; + } + } + + this.doset=function(first_time) + { //this function sends demodulator parameters to the server + ws.send("SET"+((first_time)?" mod="+this.server_mod:"")+ + " low_cut="+this.low_cut.toString()+" high_cut="+this.high_cut.toString()+ + " offset_freq="+this.offset_frequency.toString()); + } + this.doset(true); //we set parameters on object creation + + //******* envelope object ******* + // for drawing the filter envelope above scale + this.envelope.parent=this; + + this.envelope.draw=function(visible_range) + { + this.visible_range=visible_range; + this.drag_ranges=demod_envelope_draw(range, + center_freq+this.parent.offset_frequency+this.parent.low_cut, + center_freq+this.parent.offset_frequency+this.parent.high_cut, + this.color,center_freq+this.parent.offset_frequency); + }; + + // event handlers + this.envelope.drag_start=function(x, key_modifiers) + { + this.key_modifiers=key_modifiers; + this.dragged_range=demod_envelope_where_clicked(x,this.drag_ranges, key_modifiers); + //console.log("dragged_range: "+this.dragged_range.toString()); + this.drag_origin={ + x: x, + low_cut: this.parent.low_cut, + high_cut: this.parent.high_cut, + offset_frequency: this.parent.offset_frequency + }; + return this.dragged_range!=demodulator.draggable_ranges.none; + }; + + this.envelope.drag_move=function(x) + { + dr=demodulator.draggable_ranges; + if(this.dragged_range==dr.none) return false; // we return if user is not dragging (us) at all + freq_change=Math.round(this.visible_range.hps*(x-this.drag_origin.x)); + /*if(this.dragged_range==dr.beginning||this.dragged_range==dr.ending) + { + //we don't let the passband be too small + if(this.parent.low_cut+new_freq_change<=this.parent.high_cut-this.parent.filter.min_passband) this.freq_change=new_freq_change; + else return; + } + var new_value;*/ + + //dragging the line in the middle of the filter envelope while holding Shift does emulate + //the BFO knob on radio equipment: moving offset frequency, while passband remains unchanged + //Filter passband moves in the opposite direction than dragged, hence the minus below. + minus=(this.dragged_range==dr.bfo)?-1:1; + //dragging any other parts of the filter envelope while holding Shift does emulate the PBS knob + //(PassBand Shift) on radio equipment: PBS does move the whole passband without moving the offset + //frequency. + if(this.dragged_range==dr.beginning||this.dragged_range==dr.bfo||this.dragged_range==dr.pbs) + { + //we don't let low_cut go beyond its limits + if((new_value=this.drag_origin.low_cut+minus*freq_change)<this.parent.filter.low_cut_limit) return true; + //nor the filter passband be too small + if(this.parent.high_cut-new_value<this.parent.filter.min_passband) return true; + //sanity check to prevent GNU Radio "firdes check failed: fa <= fb" + if(new_value>=this.parent.high_cut) return true; + this.parent.low_cut=new_value; + } + if(this.dragged_range==dr.ending||this.dragged_range==dr.bfo||this.dragged_range==dr.pbs) + { + //we don't let high_cut go beyond its limits + if((new_value=this.drag_origin.high_cut+minus*freq_change)>this.parent.filter.high_cut_limit) return true; + //nor the filter passband be too small + if(new_value-this.parent.low_cut<this.parent.filter.min_passband) return true; + //sanity check to prevent GNU Radio "firdes check failed: fa <= fb" + if(new_value<=this.parent.low_cut) return true; + this.parent.high_cut=new_value; + } + if(this.dragged_range==dr.anything_else||this.dragged_range==dr.bfo) + { + //when any other part of the envelope is dragged, the offset frequency is changed (whole passband also moves with it) + new_value=this.drag_origin.offset_frequency+freq_change; + if(new_value>bandwidth/2||new_value<-bandwidth/2) return true; //we don't allow tuning above Nyquist frequency :-) + this.parent.offset_frequency=new_value; + } + //now do the actual modifications: + mkenvelopes(this.visible_range); + this.parent.set(); + //will have to change this when changing to multi-demodulator mode: + e("webrx-actual-freq").innerHTML=format_frequency("{x} MHz",center_freq+this.parent.offset_frequency,1e6,4); + return true; + }; + + this.envelope.drag_end=function(x) + { //in this demodulator we've already changed values in the drag_move() function so we shouldn't do too much here. + to_return=this.dragged_range!=demodulator.draggable_ranges.none; //this part is required for cliking anywhere on the scale to set offset + this.dragged_range=demodulator.draggable_ranges.none; + return to_return; + }; + +} + +demodulator_default_analog.prototype=new demodulator(); + +function mkenvelopes(visible_range) //called from mkscale +{ + scale_ctx.clearRect(0,0,scale_ctx.canvas.width,22); //clear the upper part of the canvas (where filter envelopes reside) + for (var i=0;i<demodulators.length;i++) + { + demodulators[i].envelope.draw(visible_range); + } +} + +function demodulator_remove(which) +{ + demodulators[which].stop(); + demodulators.splice(which,1); +} + +function demodulator_add(what) +{ + demodulators.push(what); + mkenvelopes(get_visible_freq_range()); +} + +function demodulator_analog_replace(subtype) +{ //this function should only exist until the multi-demodulator capability is added + var temp_offset=0; + if(demodulators.length) + { + temp_offset=demodulators[0].offset_frequency; + demodulator_remove(0); + } + demodulator_add(new demodulator_default_analog(temp_offset,subtype)); +} + +function demodulator_set_offset_frequency(which,to_what) +{ + if(to_what>bandwidth/2||to_what<-bandwidth/2) return; + demodulators[0].offset_frequency=Math.round(to_what); + demodulators[0].set(); + mkenvelopes(get_visible_freq_range()); +} + + +// ======================================================== +// =================== SCALE ROUTINES =================== +// ======================================================== + +var scale_ctx; +var scale_canvas; + +function scale_setup() +{ + e("webrx-actual-freq").innerHTML=format_frequency("{x} MHz",canvas_get_frequency(window.innerWidth/2),1e6,4); + scale_canvas=e("openwebrx-scale-canvas"); + scale_ctx=scale_canvas.getContext("2d"); + scale_canvas.addEventListener("mousedown", scale_canvas_mousedown, false); + scale_canvas.addEventListener("mousemove", scale_canvas_mousemove, false); + scale_canvas.addEventListener("mouseup", scale_canvas_mouseup, false); + resize_scale(); +} + +var scale_canvas_drag_params={ + mouse_down: false, + drag: false, + start_x: 0, + key_modifiers: {shiftKey:false, altKey: false, ctrlKey: false} +}; + +function scale_canvas_mousedown(evt) +{ + with(scale_canvas_drag_params) + { + mouse_down=true; + drag=false; + start_x=evt.pageX; + key_modifiers.shiftKey=evt.shiftKey; + key_modifiers.altKey=evt.altKey; + key_modifiers.ctrlKey=evt.ctrlKey; + } + evt.preventDefault(); +} + +function scale_offset_freq_from_px(x, visible_range) +{ + if(typeof visible_range === "undefined") visible_range=get_visible_freq_range(); + return (visible_range.start+visible_range.bw*(x/canvas_container.clientWidth))-center_freq; +} + +function scale_canvas_mousemove(evt) +{ + var event_handled; + if(scale_canvas_drag_params.mouse_down&&!scale_canvas_drag_params.drag&&Math.abs(evt.pageX-scale_canvas_drag_params.start_x)>canvas_drag_min_delta) + //we can use the main drag_min_delta thing of the main canvas + { + scale_canvas_drag_params.drag=true; + //call the drag_start for all demodulators (and they will decide if they're dragged, based on X coordinate) + for (var i=0;i<demodulators.length;i++) event_handled|=demodulators[i].envelope.drag_start(evt.pageX,scale_canvas_drag_params.key_modifiers); + scale_canvas.style.cursor="move"; + } + else if(scale_canvas_drag_params.drag) + { + //call the drag_move for all demodulators (and they will decide if they're dragged) + for (var i=0;i<demodulators.length;i++) event_handled|=demodulators[i].envelope.drag_move(evt.pageX); + if (!event_handled) demodulator_set_offset_frequency(0,scale_offset_freq_from_px(evt.pageX)); + } + +} + +function scale_canvas_end_drag(x) +{ + canvas_container.style.cursor="default"; + scale_canvas_drag_params.drag=false; + scale_canvas_drag_params.mouse_down=false; + var event_handled=false; + for (var i=0;i<demodulators.length;i++) event_handled|=demodulators[i].envelope.drag_end(x); + //console.log(event_handled); + if (!event_handled) demodulator_set_offset_frequency(0,scale_offset_freq_from_px(x)); +} + +function scale_canvas_mouseup(evt) +{ + scale_canvas_end_drag(evt.pageX); +} + +function scale_px_from_freq(f,range) { return Math.round(((f-range.start)/range.bw)*canvas_container.clientWidth); } + +function get_visible_freq_range() +{ + out={}; + fcalc=function(x) { return Math.round(((-zoom_offset_px+x)/canvases[0].clientWidth)*bandwidth)+(center_freq-bandwidth/2); } + out.start=fcalc(0); + out.center=fcalc(canvas_container.clientWidth/2); + out.end=fcalc(canvas_container.clientWidth); + out.bw=out.end-out.start; + out.hps=out.bw/canvas_container.clientWidth; + return out; +} + +var scale_markers_levels=[ + { + "large_marker_per_hz":10000000, //large + "estimated_text_width":70, + "format":"{x} MHz", + "pre_divide":1000000, + "decimals":0 + }, + { + "large_marker_per_hz":5000000, + "estimated_text_width":70, + "format":"{x} MHz", + "pre_divide":1000000, + "decimals":0 + }, + { + "large_marker_per_hz":1000000, + "estimated_text_width":70, + "format":"{x} MHz", + "pre_divide":1000000, + "decimals":0 + }, + { + "large_marker_per_hz":500000, + "estimated_text_width":70, + "format":"{x} MHz", + "pre_divide":1000000, + "decimals":1 + }, + { + "large_marker_per_hz":100000, + "estimated_text_width":70, + "format":"{x} MHz", + "pre_divide":1000000, + "decimals":1 + }, + { + "large_marker_per_hz":50000, + "estimated_text_width":70, + "format":"{x} MHz", + "pre_divide":1000000, + "decimals":2 + }, + { + "large_marker_per_hz":10000, + "estimated_text_width":70, + "format":"{x} MHz", + "pre_divide":1000000, + "decimals":2 + }, + { + "large_marker_per_hz":5000, + "estimated_text_width":70, + "format":"{x} MHz", + "pre_divide":1000000, + "decimals":3 + }, + { + "large_marker_per_hz":1000, + "estimated_text_width":70, + "format":"{x} MHz", + "pre_divide":1000000, + "decimals":1 + } +]; +var scale_min_space_bw_texts=50; +var scale_min_space_bw_small_markers=7; + +function get_scale_mark_spacing(range) +{ + out={}; + fcalc=function(freq) + { + out.numlarge=(range.bw/freq); + out.large=canvas_container.clientWidth/out.numlarge; //distance between large markers (these have text) + out.ratio=5; //(ratio-1) small markers exist per large marker + out.small=out.large/out.ratio; //distance between small markers + if(out.small<scale_min_space_bw_small_markers) return false; + if(out.small/2>=scale_min_space_bw_small_markers&&freq.toString()[0]!="5") {out.small/=2; out.ratio*=2; } + out.smallbw=freq/out.ratio; + return true; + } + for(i=scale_markers_levels.length-1;i>=0;i--) + { + mp=scale_markers_levels[i]; + if (!fcalc(mp.large_marker_per_hz)) continue; + //console.log(mp.large_marker_per_hz); + //console.log(out); + if (out.large-mp.estimated_text_width>scale_min_space_bw_texts) break; + } + out.params=mp; + return out; +} + +function mkscale() +{ + //clear the lower part of the canvas (where frequency scale resides; the upper part is used by filter envelopes): + range=get_visible_freq_range(); + mkenvelopes(range); //when scale changes we will always have to redraw filter envelopes, too + scale_ctx.clearRect(0,22,scale_ctx.canvas.width,scale_ctx.canvas.height-22); + scale_ctx.strokeStyle = "#fff"; + scale_ctx.font = "bold 11px sans-serif"; + scale_ctx.textBaseline = "top"; + scale_ctx.fillStyle = "#fff"; + spacing=get_scale_mark_spacing(range); + //console.log(spacing); + marker_hz=Math.ceil(range.start/spacing.smallbw)*spacing.smallbw; + var text_to_draw; + var ftext=function(f) {text_to_draw=format_frequency(spacing.params.format,f,spacing.params.pre_divide,spacing.params.decimals);} + var last_large; + for(;;) + { + var x=scale_px_from_freq(marker_hz,range); + if(x>window.innerWidth) break; + scale_ctx.beginPath(); + scale_ctx.moveTo(x, 22); + if(marker_hz%spacing.params.large_marker_per_hz==0) + { //large marker + if(typeof first_large == "undefined") var first_large=marker_hz; + last_large=marker_hz; + scale_ctx.lineWidth=3.5; + scale_ctx.lineTo(x,22+11); + ftext(marker_hz); + var text_measured=scale_ctx.measureText(text_to_draw); + scale_ctx.textAlign = "center"; + //advanced text drawing begins + if(zoom_level==0&&range.start+spacing.smallbw*spacing.ratio>marker_hz) + { //if this is the first overall marker when zoomed out + if(x<text_measured.width/2) + { //and if it would be clipped off the screen + if(scale_px_from_freq(marker_hz+spacing.smallbw*spacing.ratio,range)-text_measured.width>=scale_min_space_bw_texts) + { //and if we have enough space to draw it correctly without clipping + scale_ctx.textAlign = "left"; + scale_ctx.fillText(text_to_draw, 0, 22+10); + } + } + } + else if(zoom_level==0&&range.end-spacing.smallbw*spacing.ratio<marker_hz) + { //if this is the last overall marker when zoomed out + if(x>window.innerWidth-text_measured.width/2) + { //and if it would be clipped off the screen + if(window.innerWidth-text_measured.width-scale_px_from_freq(marker_hz-spacing.smallbw*spacing.ratio,range)>=scale_min_space_bw_texts) + { //and if we have enough space to draw it correctly without clipping + scale_ctx.textAlign = "right"; + scale_ctx.fillText(text_to_draw, window.innerWidth, 22+10); + } + } + } + else scale_ctx.fillText(text_to_draw, x, 22+10); //draw text normally + } + else + { //small marker + scale_ctx.lineWidth=2; + scale_ctx.lineTo(x,22+8); + } + marker_hz+=spacing.smallbw; + scale_ctx.stroke(); + } + if(zoom_level!=0) + { // if zoomed, we don't want the texts to disappear because their markers can't be seen + // on the left side + scale_ctx.textAlign = "center"; + var f=first_large-spacing.smallbw*spacing.ratio; + var x=scale_px_from_freq(f,range); + ftext(f); + var w=scale_ctx.measureText(text_to_draw).width; + if(x+w/2>0) scale_ctx.fillText(text_to_draw, x, 22+10); + // on the right side + f=last_large+spacing.smallbw*spacing.ratio; + x=scale_px_from_freq(f,range); + ftext(f); + w=scale_ctx.measureText(text_to_draw).width; + if(x-w/2<window.innerWidth) scale_ctx.fillText(text_to_draw, x, 22+10); + } +} + +function resize_scale() +{ + scale_ctx.canvas.width = window.innerWidth; + scale_ctx.canvas.height = 47; + mkscale(); +} + +function canvas_mouseover(evt) +{ + if(!waterfall_setup_done) return; + //e("webrx-freq-show").style.visibility="visible"; +} + +function canvas_mouseout(evt) +{ + if(!waterfall_setup_done) return; + //e("webrx-freq-show").style.visibility="hidden"; +} + +function canvas_get_freq_offset(relativeX) +{ + rel=(relativeX/canvases[0].clientWidth); + return Math.round((bandwidth*rel)-(bandwidth/2)); +} + +function canvas_get_frequency(relativeX) +{ + return center_freq+canvas_get_freq_offset(relativeX); +} + +/*function canvas_format_frequency(relativeX) +{ + return (canvas_get_frequency(relativeX)/1e6).toFixed(3)+" MHz"; +}*/ + +function format_frequency(format, freq_hz, pre_divide, decimals) +{ + out=format.replace("{x}",(freq_hz/pre_divide).toFixed(decimals)); + at=out.indexOf(".")+4; + while(decimals>3) + { + out=out.substr(0,at)+","+out.substr(at); + at+=4; + decimals-=3; + } + return out; +} + +canvas_drag=false; +canvas_drag_min_delta=1; +canvas_mouse_down=false; + +function canvas_mousedown(evt) +{ + canvas_mouse_down=true; + canvas_drag=false; + canvas_drag_last_x=canvas_drag_start_x=evt.pageX; + canvas_drag_last_y=canvas_drag_start_y=evt.pageY; + evt.preventDefault(); //don't show text selection mouse pointer +} + +function canvas_mousemove(evt) +{ + if(!waterfall_setup_done) return; + //element=e("webrx-freq-show"); + relativeX=(evt.offsetX)?evt.offsetX:evt.layerX; + /*realX=(relativeX-element.clientWidth/2); + maxX=(canvases[0].clientWidth-element.clientWidth); + if(realX>maxX) realX=maxX; + if(realX<0) realX=0; + element.style.left=realX.toString()+"px";*/ + if(canvas_mouse_down) + { + if(!canvas_drag&&Math.abs(evt.pageX-canvas_drag_start_x)>canvas_drag_min_delta) + { + canvas_drag=true; + canvas_container.style.cursor="move"; + } + if(canvas_drag) + { + var deltaX=canvas_drag_last_x-evt.pageX; + var deltaY=canvas_drag_last_y-evt.pageY; + //zoom_center_where=zoom_center_where_calc(evt.pageX); + var dpx=range.hps*deltaX; + if( + !(zoom_center_rel+dpx>(bandwidth/2-canvas_container.clientWidth*(1-zoom_center_where)*range.hps)) && + !(zoom_center_rel+dpx<-bandwidth/2+canvas_container.clientWidth*zoom_center_where*range.hps) + ) { zoom_center_rel+=dpx; } +// -((canvases_new_width*(0.5+zoom_center_rel/bandwidth))-(winsize*zoom_center_where)); + resize_canvases(false); + canvas_drag_last_x=evt.pageX; + canvas_drag_last_y=evt.pageY; + mkscale(); + } + } + else e("webrx-mouse-freq").innerHTML=format_frequency("{x} MHz",canvas_get_frequency(relativeX),1e6,4); +} + +function canvas_container_mouseout(evt) +{ + canvas_end_drag(); +} + +//function body_mouseup() { canvas_end_drag(); console.log("body_mouseup"); } +//function window_mouseout() { canvas_end_drag(); console.log("document_mouseout"); } + +function canvas_mouseup(evt) +{ + if(!waterfall_setup_done) return; + relativeX=(evt.offsetX)?evt.offsetX:evt.layerX; + + if(!canvas_drag) + { + //ws.send("SET offset_freq="+canvas_get_freq_offset(relativeX).toString()); + //e("webrx-actual-freq").innerHTML=format_frequency("{x} MHz",canvas_get_frequency(relativeX),1e6,4); + demodulator_set_offset_frequency(0, canvas_get_freq_offset(relativeX)); + } + else + { + canvas_end_drag(); + } + canvas_mouse_down=false; +} + +function canvas_end_drag() +{ + canvas_container.style.cursor="crosshair"; + canvas_mouse_down=false; +} + +function zoom_center_where_calc(screenposX) +{ + //return (screenposX-(window.innerWidth-canvas_container.clientWidth))/canvas_container.clientWidth; + return screenposX/canvas_container.clientWidth; +} + +function canvas_mousewheel(evt) +{ + if(!waterfall_setup_done) return; + var i=Math.abs(evt.wheelDelta); + var dir=(i/evt.wheelDelta)<0; + var relativeX=(evt.offsetX)?evt.offsetX:evt.layerX; + i/=120; + while (i--) zoom_step(dir, relativeX, zoom_center_where_calc(evt.pageX)); + evt.preventDefault(); + //evt.returnValue = false; //disable scrollbar move +} + + +zoom_max_level_hps=33; //Hz/pixel +zoom_levels_count=5; + +function get_zoom_coeff_from_hps(hps) +{ + var shown_bw=(window.innerWidth*hps); + return bandwidth/shown_bw; +} + +zoom_levels=[1]; +zoom_level=0; +zoom_freq=0; +zoom_offset_px=0; +zoom_center_rel=0; +zoom_center_where=0; + +function mkzoomlevels() +{ + zoom_levels=[1]; + maxc=get_zoom_coeff_from_hps(zoom_max_level_hps); + if(maxc<1) return; + for(i=1;i<zoom_levels_count;i++) + zoom_levels.push(1+(maxc-1)*(i/(zoom_levels_count-1))); +} + +function zoom_step(out, where, onscreen) +{ + if((out&&zoom_level==0)||(!out&&zoom_level>=zoom_levels_count-1)) return; + + if(out) --zoom_level; + else ++zoom_level; + zoom_center_rel=canvas_get_freq_offset(where); + //console.log("zoom_step || zlevel: "+zoom_level.toString()+" zlevel_val: "+zoom_levels[zoom_level].toString()+" zoom_center_rel: "+zoom_center_rel.toString()); + zoom_center_where=onscreen; + resize_canvases(true); + mkscale(); +} + +function zoom_calc() +{ + winsize=canvas_container.clientWidth; + var canvases_new_width=winsize*zoom_levels[zoom_level]; + zoom_offset_px=-((canvases_new_width*(0.5+zoom_center_rel/bandwidth))-(winsize*zoom_center_where)); + if(zoom_offset_px>0) zoom_offset_px=0; + if(zoom_offset_px<winsize-canvases_new_width) + zoom_offset_px=winsize-canvases_new_width; + //console.log("zoom_calc || zopx:"+zoom_offset_px.toString()+ " maxoff:"+(winsize-canvases_new_width).toString()+" relval:"+(0.5+zoom_center_rel/bandwidth).toString() ); +} + +function resize_waterfall_container(check_init) +{ + if(check_init&&!waterfall_setup_done) return; + canvas_container.style.height=(window.innerHeight-e("webrx-top-container").clientHeight-e("openwebrx-scale-container").clientHeight).toString()+"px"; +} + + +function on_ws_recv(evt) +{ + /*var f = new FileReader(); + f.onload = function(e) { + //alert(e.target.result); + }; + f.readAsText(evt.data);*/ + if(!(evt.data instanceof ArrayBuffer)) + { divlog("on_ws_recv(): Not ArrayBuffer received...",1); return; } + stringData=arrayBufferToString(evt.data); + firstChars=stringData.substring(0,3); + //alert(firstChars); + if(firstChars=="CLI" && stringData.substring(0,16)=="CLIENT DE SERVER") + divlog("Acknowledged WebSocket connection: "+stringData); + if(firstChars=="AUD") + { + audio_received[audio_received.length] = new Int16Array(evt.data,4); + var audio_recv_len=audio_received[audio_received.length-1].length; + //console.log("on_ws_recv() :: recv: "+audio_recv_len.toString()); + audio_buffer_current_size_debug+=audio_recv_len; + audio_buffer_current_size+=audio_recv_len; + if(audio_initialized==0 && audio_received.length>10) audio_init() + } + else if(firstChars=="FFT") + { + //alert("Yupee! Doing FFT"); + var floatArray = new Float32Array(evt.data,4); + waterfall_add_queue(floatArray); + } else if(firstChars=="MSG") + { + /*try + {*/ + params=stringData.substring(4).split(" "); + for(i=0;i<params.length;i++) + { + param=params[i].split("="); + switch(param[0]) + { + case "setup": + waterfall_init(); + break; + case "bandwidth": + bandwidth=parseInt(param[1]) + break; + case "center_freq": + center_freq=parseInt(param[1]) + break; + case "fft_size": + fft_size=parseInt(param[1]) + break; + case "fft_fps": + fft_fps=parseInt(param[1]) + break; + + } + } + /*} + catch(err) + { + divlog("Received invalid message over WebSocket."); + }*/ + } + +} + +function add_problem(what) +{ + problems_span=e("openwebrx-problems"); + for(var i=0;i<problems_span.children.length;i++) if(problems_span.children[i].innerHTML==what) return; + new_span = document.createElement("span"); + new_span.innerHTML=what; + problems_span.appendChild(new_span); + window.setTimeout(function(ps,ns) { ps.removeChild(ns); }, 1000,problems_span,new_span); +} + +function waterfall_add_queue(what) +{ + waterfall_queue.push(what); +} + +function waterfall_dequeue() +{ + if(waterfall_queue.length) waterfall_add(waterfall_queue.shift()); + if(waterfall_queue.length>fft_fps/2) //in case of emergency + { + add_problem("fft overflow"); + while(waterfall_queue.length) waterfall_add(waterfall_queue.shift()); + } +} + +function on_ws_opened() +{ + ws.send("SERVER DE CLIENT openwebrx.js"); + divlog("WebSocket opened to "+ws_url); +} + +function divlog(what, is_error) +{ + if(typeof is_error !== undefined && is_error == 1) what="<span class=\"webrx-error\">"+what+"</span>"; + e("openwebrx-debugdiv").innerHTML+=what+"<br />"; +} + +var audio_context; +var audio_initialized=0; + +var audio_received = Array(); +var audio_buffer_index = 0; +var audio_resampler; +var audio_node; +//var audio_received_sample_rate = 48000; +var audio_input_buffer_size; + +// Optimalise these if audio lags or is choppy: +var audio_buffer_size = 8192;//2048 was choppy +var audio_buffer_maximal_length_sec=2; //actual number of samples are calculated from sample rate +var audio_flush_interval_ms=250; //the interval in which audio_flush() is called + +function audio_onprocess(e) +{ + //https://github.com/0xfe/experiments/blob/master/www/tone/js/sinewave.js + if(audio_received.length==0) + { add_problem("audio underrun"); return; } + output = e.outputBuffer.getChannelData(0); + int_buffer = audio_received[0]; + read_remain = audio_buffer_size; + //audio_buffer_maximal_length=120; + + obi=0; //output buffer index + debug_str="" + while(1) + { + if(int_buffer.length-audio_buffer_index>read_remain) + { + for (i=audio_buffer_index; i<audio_buffer_index+read_remain; i++) + output[obi++] = int_buffer[i]/32768; + //debug_str+="added whole ibl="+int_buffer.length.toString()+" abi="+audio_buffer_index.toString()+" "+(int_buffer.length-audio_buffer_index).toString()+">"+read_remain.toString()+" obi="+obi.toString()+"\n"; + audio_buffer_index+=read_remain; + break; + } + else + { + for (i=audio_buffer_index; i<int_buffer.length; i++) + output[obi++] = int_buffer[i]/32768; + read_remain-=(int_buffer.length-audio_buffer_index); + audio_buffer_current_size-=audio_received[0].length; + /*if (audio_received.length>audio_buffer_maximal_length) + { + add_problem("audio overrun"); + audio_received.splice(0,audio_received.length-audio_buffer_maximal_length); + } + else*/ + audio_received.splice(0,1); + //debug_str+="added remain, remain="+read_remain.toString()+" abi="+audio_buffer_index.toString()+" alen="+int_buffer.length.toString()+" i="+i.toString()+" arecva="+audio_received.length.toString()+" obi="+obi.toString()+"\n"; + audio_buffer_index = 0; + if(audio_received.length == 0 || read_remain == 0) return; + int_buffer = audio_received[0]; + } + } + //debug_str+="obi="+obi.toString(); + //alert(debug_str); +} + +function audio_flush() +{ + if (audio_buffer_current_size>audio_buffer_maximal_length_sec*audio_context.sampleRate) + { + add_problem("audio overrun"); + console.log("audio_flush() :: size: "+audio_buffer_current_size.toString()+" allowed: "+(audio_buffer_maximal_length_sec*audio_context.sampleRate).toString()); + while (audio_buffer_current_size>audio_buffer_maximal_length_sec*audio_context.sampleRate*0.5) + { + audio_buffer_current_size-=audio_received[0].length; + audio_received.splice(0,1); + } + } + +} + +function webrx_set_param(what, value) +{ + ws.send("SET "+what+"="+value.toString()); +} + +function audio_init() +{ + //https://github.com/0xfe/experiments/blob/master/www/tone/js/sinewave.js + audio_initialized=1; // only tell on_ws_recv() not to call it again + try + { + window.AudioContext = window.AudioContext||window.webkitAudioContext; + audio_context = new AudioContext(); + } + catch(e) + { + divlog('Your browser does not support Web Audio API, which is required for WebRX to run. Please upgrade to a HTML5 compatible browser.', 1); + } + audio_node = audio_context.createJavaScriptNode(audio_buffer_size, 0, 1); + audio_node.onaudioprocess = audio_onprocess; + audio_node.connect(audio_context.destination); + // --- Resampling --- + //https://github.com/grantgalitz/XAudioJS/blob/master/XAudioServer.js + //audio_resampler = new Resampler(audio_received_sample_rate, audio_context.sampleRate, 1, audio_buffer_size, true); + //audio_input_buffer_size = audio_buffer_size*(audio_received_sample_rate/audio_context.sampleRate); + webrx_set_param("audio_rate",audio_context.sampleRate); //Don't try to resample + window.setInterval(audio_flush,audio_flush_interval_ms); + divlog('Web Audio API succesfully initialized, sample rate: '+audio_context.sampleRate.toString()); + /*audio_source=audio_context.createBufferSource(); + audio_buffer = audio_context.createBuffer(xhr.response, false); + audio_source.buffer = buffer; + audio_source.noteOn(0);*/ + demodulator_analog_replace('nfm'); //needs audio_context.sampleRate to exist +} + +function on_ws_closed() +{ + try + { + audio_node.disconnect(); + } + catch (dont_care) {} + divlog("WebSocket has closed unexpectedly. Please reload the page.", 1); +} + +function on_ws_error(event) +{ + divlog(event.toString(),1); +} + +function open_websocket() +{ + if (!("WebSocket" in window)) + divlog("Your browser does not support WebSocket, which is required for WebRX to run. Please upgrade to a HTML5 compatible browser."); + ws = new WebSocket(ws_url+client_id); + ws.onopen = on_ws_opened; + ws.onmessage = on_ws_recv; + ws.onclose = on_ws_closed; + ws.binaryType = "arraybuffer"; + window.onbeforeunload = function() { //http://stackoverflow.com/questions/4812686/closing-websocket-correctly-html5-javascript + ws.onclose = function () {}; + ws.close(); + }; + //ws.onerror = on_ws_error; +} + +//var color_scale=[0xFFFFFFFF, 0x000000FF]; +//var color_scale=[0x000000FF, 0x000000FF, 0x3a0090ff, 0x10c400ff, 0xffef00ff, 0xff5656ff]; +//var color_scale=[0x000000FF, 0x000000FF, 0x534b37ff, 0xcedffaff, 0x8899a9ff, 0xfff775ff, 0xff8a8aff, 0xb20000ff]; +var color_scale=[0x2e6893ff, 0x69a5d0ff, 0x214b69ff, 0x9dc4e0ff, 0xfff775ff, 0xff8a8aff, 0xb20000ff]; +//var color_scale=[ 0x000000FF, 0xff5656ff, 0xffffffff]; + +function waterfall_mkcolor(db_value) +{ + min_value=-100; //in dB + max_value=10 + if(db_value<min_value) db_value=min_value + if(db_value>max_value) db_value=max_value + full_scale=max_value-min_value; + relative_value=db_value-min_value; + value_percent=relative_value/full_scale; + percent_for_one_color=1/(color_scale.length-1); + index=Math.floor(value_percent/percent_for_one_color); + remain=(value_percent-percent_for_one_color*index)/percent_for_one_color; + return color_between(color_scale[index+1],color_scale[index],remain); +} + +function color_between(first, second, percent) +{ + output=0; + for(i=0;i<4;i++) + { + add = ((((first&(0xff<<(i*8)))>>>0)*percent) + (((second&(0xff<<(i*8)))>>>0)*(1-percent))) & (0xff<<(i*8)); + output |= add>>>0; + } + return output>>>0; +} + + +var canvas_context; +var canvases = []; +var canvas_default_height = 200; +var canvas_container; +var canvas_phantom; + +function add_canvas() +{ + new_canvas = document.createElement("canvas"); + new_canvas.width=fft_size; + new_canvas.height=canvas_default_height; + canvas_actual_line=canvas_default_height-1; + new_canvas.style.width=(canvas_container.clientWidth*zoom_levels[zoom_level]).toString()+"px"; + new_canvas.style.left=zoom_offset_px.toString()+"px"; + new_canvas.style.height=canvas_default_height.toString()+"px"; + new_canvas.openwebrx_top=(-canvas_default_height+1); + new_canvas.style.top=new_canvas.openwebrx_top.toString()+"px"; + canvas_context = new_canvas.getContext("2d"); + canvas_container.appendChild(new_canvas); + new_canvas.addEventListener("mouseover", canvas_mouseover, false); + new_canvas.addEventListener("mouseout", canvas_mouseout, false); + new_canvas.addEventListener("mousemove", canvas_mousemove, false); + new_canvas.addEventListener("mouseup", canvas_mouseup, false); + new_canvas.addEventListener("mousedown", canvas_mousedown, false); + new_canvas.addEventListener("mousewheel",canvas_mousewheel, false); + canvases.push(new_canvas); +} + +function init_canvas_container() +{ + canvas_container=e("webrx-canvas-container"); + canvas_container.addEventListener("mouseout",canvas_container_mouseout, false); + //window.addEventListener("mouseout",window_mouseout,false); + //document.body.addEventListener("mouseup",body_mouseup,false); + canvas_phantom=e("openwebrx-phantom-canvas"); + canvas_phantom.addEventListener("mouseover", canvas_mouseover, false); + canvas_phantom.addEventListener("mouseout", canvas_mouseout, false); + canvas_phantom.addEventListener("mousemove", canvas_mousemove, false); + canvas_phantom.addEventListener("mouseup", canvas_mouseup, false); + canvas_phantom.addEventListener("mousedown", canvas_mousedown, false); + canvas_phantom.addEventListener("mousewheel",canvas_mousewheel, false); + canvas_phantom.style.width=canvas_container.clientWidth+"px"; + add_canvas(); +} + +canvas_maxshift=0; + +function shift_canvases() +{ + canvases.forEach(function(p) + { + p.style.top=(p.openwebrx_top++).toString()+"px"; + }); + canvas_maxshift++; + if(canvas_container.clientHeight>canvas_maxshift) + { + canvas_phantom.style.top=canvas_maxshift.toString()+"px"; + canvas_phantom.style.height=(canvas_container.clientHeight-canvas_maxshift).toString()+"px"; + canvas_phantom.style.display="block"; + } + else + canvas_phantom.style.display="none"; + + + //canvas_container.style.height=(((canvases.length-1)*canvas_default_height)+(canvas_default_height-canvas_actual_line)).toString()+"px"; + //canvas_container.style.height="100%"; +} + +function resize_canvases(zoom) +{ + if(typeof zoom == "undefined") zoom=false; + if(!zoom) mkzoomlevels(); + zoom_calc(); + new_width=(canvas_container.clientWidth*zoom_levels[zoom_level]).toString()+"px"; + var zoom_value=zoom_offset_px.toString()+"px"; + canvases.forEach(function(p) + { + p.style.width=new_width; + p.style.left=zoom_value; + }); + canvas_phantom.style.width=new_width; + canvas_phantom.style.left=zoom_value; +} + +function waterfall_init() +{ + init_canvas_container(); + waterfall_timer = window.setInterval(waterfall_dequeue,900/fft_fps); + resize_waterfall_container(false); /* then */ resize_canvases(); + scale_setup(); + mkzoomlevels(); + waterfall_setup_done=1; +} + +var waterfall_dont_scale=0; + +function waterfall_add(data) +{ + if(!waterfall_setup_done) return; + var w=fft_size; + + //waterfall_shift(); + // ==== do scaling if required ==== + /*if(waterfall_dont_scale) + { + scaled=data; + for(i=scaled.length;i<w;i++) scaled[i]=-100; + } + else + { + if ((to-from)==w) + { + scaled=data; + } + else if ((to-from)<w) + { //make line bigger + pixel_per_point=w/(to-from); + scaled=Array(); + j=0; + remain=pixel_per_point; + for(i=0; i<w; i++) + { + //thiscolor=data[j]*(remain-floor(remain))+data[j+1]*(1-(remain-floor(remain))) + //nextcolor=data[j+1]*(remain-floor(remain))+data[j+2]*(1-(remain-floor(remain))) + if(remain>1) + { + scaled[i]=data[j]*(remain/pixel_per_point)+data[j+1]*((1-remain)/pixel_per_point); + remain--; + } + else + { + j++; + scaled[i]=data[j]*(remain/pixel_per_point)+data[j+1]*((1-remain)/pixel_per_point); + remain=pixel_per_point-(1-remain); + } + } + + } + else + { //make line smaller (linear decimation, moving average) + point_per_pixel=(to-from)/w; + scaled=Array(); + j=0; + remain=point_per_pixel; + last_pixel=0; + for(i=from; i<to; i++) + { + if(remain>1) + { + last_pixel+=data[i]; + remain--; + } + else + { + last_pixel+=data[i]*remain; + scaled[j++]=last_pixel/point_per_pixel; + last_pixel=data[i]*(1-remain); + remain=point_per_pixel-(1-remain); //? + } + } + } + } + + //Add line to waterfall image + base=(h-1)*w*4; + for(x=0;x<w;x++) + { + color=waterfall_mkcolor(scaled[x]); + for(i=0;i<4;i++) + waterfall_image.data[base+x*4+i] = ((color>>>0)>>((3-i)*8))&0xff; + }*/ + + //Add line to waterfall image + oneline_image = canvas_context.createImageData(w,1); + for(x=0;x<w;x++) + { + color=waterfall_mkcolor(data[x]); + for(i=0;i<4;i++) + oneline_image.data[x*4+i] = ((color>>>0)>>((3-i)*8))&0xff; + } + + + //Draw image + canvas_context.putImageData(oneline_image, 0, canvas_actual_line--); + shift_canvases(); + if(canvas_actual_line<0) add_canvas(); + //divlog("Drawn FFT"); +} + +/* +function waterfall_shift() +{ + w=canvas.width; + h=canvas.height; + for(y=0; y<h-1; y++) + { + for(i=0; i<w*4; i++) + waterfall_image.data[y*w*4+i] = waterfall_image.data[(y+1)*w*4+i]; + } +}*/ + +function check_top_bar_congestion() +{ + var wt=e("webrx-rx-title"); + var tl=e("webrx-ha5kfu-top-logo"); + if(wt.offsetLeft+wt.offsetWidth>tl.offsetLeft-20) tl.style.display="none"; + else tl.style.display="block"; +} + +function webrx_resize() +{ + resize_canvases(); + resize_waterfall_container(true); + resize_scale(); + check_top_bar_congestion(); +} + +function webrx_init() +{ + init_rx_photo(); + open_websocket(); + place_panels(); + window.setInterval(debug_audio,1000); +} + +/* +window.setInterval(function(){ + sum=0; + for(i=0;i<audio_received.length;i++) + sum+=audio_received[i].length; + divlog("audio buffer bytes: "+sum); +}, 2000);*/ + +/*function email(what) +{ + //| http://stackoverflow.com/questions/617647/where-is-my-one-line-implementation-of-rot13-in-javascript-going-wrong + what=what.replace(/[a-zA-Z]/g,function(c){return String.fromCharCode((c<="Z"?90:122)>=(c=c.charCodeAt(0)+13)?c:c-26);}); + window.location.href="mailto:"+what; +}*/ + +var rt = function (s,n) {return s.replace(/[a-zA-Z]/g,function(c){return String.fromCharCode((c<="Z"?90:122)>=(c=c.charCodeAt(0)+n)?c:c-26);});} +var irt = function (s,n) {return s.replace(/[a-zA-Z]/g,function(c){return String.fromCharCode((c>="a"?97:65)<=(c=c.charCodeAt(0)-n)?c:c+26);});} +var sendmail2 = function (s) { window.location.href="mailto:"+irt(s.replace("=",String.fromCharCode(0100)).replace("$","."),8); } + +function debug_audio() +{ + + e("openwebrx-audio-sps").innerHTML=audio_buffer_current_size_debug.toString(); + audio_buffer_current_size_debug=0; +} + +// ======================================================== +// ======================= PANELS ======================= +// ======================================================== + +panel_margin=10; + +function pop_bottommost_panel(from) +{ + min_order=parseInt(from[0].dataset.panelOrder); + min_index=0; + for(i=0;i<from.length;i++) + { + actual_order=parseInt(from[i].dataset.panelOrder); + if(actual_order<min_order) + { + min_index=i; + min_order=actual_order; + } + } + to_return=from[min_index]; + from.splice(min_index,1); + return to_return; +} + +function place_panels() +{ + var left_col=[]; + var right_col=[]; + var plist=e("openwebrx-panels-container").children; + for(i=0;i<plist.length;i++) + { + c=plist[i]; + if(c.className=="openwebrx-panel") + { + newSize=c.dataset.panelSize.split(","); + if (c.dataset.panelPos=="left") { left_col.push(c); } + else if(c.dataset.panelPos=="right") { right_col.push(c); } + c.style.width=newSize[0]+"px"; + c.style.height=newSize[1]+"px"; + c.style.margin=panel_margin.toString()+"px"; + c.openwebrxPanelWidth=parseInt(newSize[0]); + c.openwebrxPanelHeight=parseInt(newSize[1]); + } + } + y=0; + while(left_col.length>0) + { + p=pop_bottommost_panel(left_col); + p.style.left="0px"; + p.style.bottom=y.toString()+"px"; + p.style.visibility="visible"; + y+=p.openwebrxPanelHeight+3*panel_margin; + } + y=0; + while(right_col.length>0) + { + p=pop_bottommost_panel(right_col); + p.style.right="10px"; + p.style.bottom=y.toString()+"px"; + p.style.visibility="visible"; + y+=p.openwebrxPanelHeight+3*panel_margin; + } +} + diff --git a/htdocs/upgrade.html b/htdocs/upgrade.html new file mode 100644 index 0000000000000000000000000000000000000000..b9a498d7cfd30fef532eb899b7812824c8ecfc53 --- /dev/null +++ b/htdocs/upgrade.html @@ -0,0 +1,93 @@ +<html> +<!-- +OpenWebRX (c) Copyright 2013 Andras Retzler <ha7ilm@sdr.hu> + +This file is part of OpenWebRX. + + OpenWebRX is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + OpenWebRX is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with OpenWebRX. If not, see <http://www.gnu.org/licenses/>. +--> +<head><title>OpenWebRX</title><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> +<style> +html, body +{ + font-family: "DejaVu Sans", Verdana, Geneva, sans-serif; + width: 100%; + text-align: center; + margin: 0; + padding: 0; +} +img.logo +{ + margin-top: 120px; +} +div.frame +{ + text-align: left; + margin:0px auto; + width: 800px; +} + +div.panel +{ + text-align: center; + background-color:#777777; + border-radius: 15px; + padding: 12px; + font-weight: bold; + color: White; + font-size: 13pt; + /*text-shadow: 1px 1px 4px #444;*/ + font-family: sans; +} + +div.alt +{ + font-size: 10pt; + padding-top: 10px; +} + + +body div a +{ + color: #5ca8ff; + text-shadow: none; +} + +span.browser +{ +} + +</style> +<script> +var irt = function (s,n) {return s.replace(/[a-zA-Z]/g,function(c){return String.fromCharCode((c>="a"?97:65)<=(c=c.charCodeAt(0)-n)?c:c+26);});} +var sendmail2 = function (s) { window.location.href="mailto:"+irt(s.replace("=",String.fromCharCode(0100)).replace("$","."),8); } +</script> + +</head> +<body> + +<div class="frame"> + <img class="logo" src="gfx/openwebrx-logo-big.png" style="height: 60px;"/> + <div class="panel"> + Only the latest <span class="browser">Google Chrome</span> browser is supported at the moment.<br/> + Please <a href="http://chrome.google.com/">download and install Google Chrome.</a><br /> + <div class="alt"> + Alternatively, you may proceed to OpenWebRX, but it's not supposed to work as expected. <br /> + <a href="/?unsupported">Click here</a> if you still want to try OpenWebRX.</a> + </div> + </div> +</div> +</body> +</html> + diff --git a/lwebrx b/lwebrx new file mode 100755 index 0000000000000000000000000000000000000000..cdf0c2598f8e0107428d22213767bb5f777c2d99 --- /dev/null +++ b/lwebrx @@ -0,0 +1,7 @@ +#!/bin/bash +#sudo rmmod dvb_usb_rtl28xxu +#sudo killall -9 rtl_tcp +while true; do python openwebrx.py; done +#echo "Quit at:" +#date +#sudo killall -9 rtl_tcp diff --git a/openwebrx.py b/openwebrx.py new file mode 100755 index 0000000000000000000000000000000000000000..89a9d27ab02bab757f4c3e3ec2d74278a69f57b4 --- /dev/null +++ b/openwebrx.py @@ -0,0 +1,467 @@ + +""" +OpenWebRX: open-source web based SDR for everyone! + +This file is part of OpenWebRX. + + OpenWebRX is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + OpenWebRX is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Foobar. If not, see <http://www.gnu.org/licenses/>. + +Authors: + Andras Retzler, HA7ILM <retzlerandras@gmail.com> + +""" + +# http://www.codeproject.com/Articles/462525/Simple-HTTP-Server-and-Client-in-Python +# some ideas are used from the artice above + +import grcconvert +import os +import code +import importlib + +regexp_for_grcconvert=((r"def __init__\(self\):",r"def __init__(self,sample_rate_param): # added by grcconvert"),(r"self.samp_rate = samp_rate = ([0-9]*)","self.samp_rate = samp_rate = sample_rate_param # added by grcconvert")) + +demods=[] +import demod +for subdir in os.listdir("demod"): + if os.path.isdir("demod/"+subdir) and not subdir[0]=="_": + exact_path="demod/"+subdir+"/top_block.py" + if os.path.isfile(exact_path): + grcconvert.main(exact_path,regexp_for_grcconvert) + if(subdir!="spectrum"): demods.append(subdir) + importlib.import_module("demod."+subdir+".top_block") + #setattr(demod, subdir, __import__("demod."+subdir)) + +import thread +import time +import subprocess +import os +from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer +from SocketServer import ThreadingMixIn +import fcntl +import time +import md5 +import random +import threading +import dl +import sys +import traceback +from collections import namedtuple +import Queue + +#import rtl_mus +import rxws +import uuid +import config_webrx + +class MultiThreadHTTPServer(ThreadingMixIn, HTTPServer): + pass + +def main(): + global clients + global clients_mutex + global server_hostname + global web_port + global config_webrx + global admin_param_dict + + try: + for libcpath in ["/lib/i386-linux-gnu/libc.so.6","/lib/libc.so.6"]: + if os.path.exists(libcpath): + libc = dl.open(libcpath) + libc.call("prctl", 15, "openwebrx-sdr", 0, 0, 0) + break + except: + pass + + #Configuration + config_file=open("config_webrx.py","r") + config_lines=config_file.readlines() + config_changed=0 + admin_pw_md5="" + for i in range(0,len(config_lines)): + if(config_lines[i][:17]=="receiver_token=\"\""): + my_uid=str(uuid.uuid4()).replace("-","") + config_lines[i]="receiver_token=\""+my_uid+"\" # generated on the first run\n" + config_changed=1 + print "[openwebrx-main] New receiver token generated and written into config_webrx.py: "+my_uid + elif(config_lines[i][:24]=="change_admin_password_to" and config_lines[i][27:28]!="\"\""): + line_parts=config_lines[i].split("\"") + admin_pw_md5=md5.md5(line_parts[1]).hexdigest() + config_lines[i]=line_parts[0]+"\"\""+line_parts[2] + print "[openwebrx-main] New admin password md5 hashed." + elif(config_lines[i][:19]=="admin_password_md5=" and config_lines[i][27:28]!="\"\""): + if admin_pw_md5!="": + config_lines[i]="admin_password_md5=\""+admin_pw_md5+"\"\n" + config_changed=1 + print "[openwebrx-main] Old md5 hash replaced." + if(config_changed): + config_file.close() + config_file=open("config_webrx.py","w") + config_file.writelines(config_lines) + config_file.close() + print "[openwebrx-main] Changes have been written to config_webrx.py" + config_webrx=__import__("config_webrx") + + admin_param_dict=config_webrx.admin_param_dict + print admin_param_dict, "main" + + if config_webrx.start_rtl_thread: + rtl_tcp_thread=threading.Thread(target = lambda:subprocess.Popen(config_webrx.start_rtl_tcp_command, shell=True), args=()) + rtl_tcp_thread.start() + print "[openwebrx-main] Started rtl_tcp thread: "+config_webrx.start_rtl_tcp_command + + #rtl_mus.cfg=__import__("config_rtl") + #rtl_mus_thread=threading.Thread(target = rtl_mus.main, args = ()) + #rtl_mus_thread.start() + rtl_mus_thread=threading.Thread(target = lambda:subprocess.Popen("python rtl_mus.py config_rtl", shell=True), args=()) + rtl_mus_thread.start() # The new feature in GNU Radio 3.7: top_block() locks up ALL python threads until it gets the TCP connection. + print "[openwebrx-main] Started rtl_mus" + + clients=[] + clients_mutex=threading.Lock() + + print "[openwebrx-main] Starting spectrum thread. (You should get another message about success, or the program has hanged up.)" + spectrum_thread=threading.Thread(target = spectrum_thread_function, args = ()) + spectrum_thread.start() + + #threading.Thread(target = measure_thread_function, args = ()).start() + + httpd = MultiThreadHTTPServer(('', config_webrx.web_port), WebRXHandler) + print('[openwebrx-main] Starting HTTP server.') + httpd.serve_forever() + + +# This is a debug function below: +measure_value=0 +def measure_thread_function(): + global measure_value + while True: + print "[openwebrx-measure] value is",measure_value + measure_value=0 + time.sleep(1) + + +def spectrum_thread_function(): + global clients_mutex + global clients + tb=demod.spectrum.top_block.top_block(admin_param_dict["samp_rate"]) + tb.set_fps(config_webrx.fft_fps) + tb.set_fft_size(config_webrx.fft_size) + sleep_sec=0.87/config_webrx.fft_fps + print "[openwebrx-spectrum] Spectrum thread initialized successfully." + tb.start() + print "[openwebrx-spectrum] Spectrum thread started." + while True: + data=tb.msgq_out.delete_head().to_string() + #print "gotcha",len(data),"bytes of spectrum data via spectrum_thread_function()" + clients_mutex.acquire() + for i in range(0,len(clients)): + if (clients[i].ws_started): + if clients[i].spectrum_queue.full(): + close_client(i, False) + else: + clients[i].spectrum_queue.put([data]) # add new string by "reference" to all clients + clients_mutex.release() + +def get_client_by_id(client_id, use_mutex=True): + global clients_mutex + global clients + output=-1 + if use_mutex: clients_mutex.acquire() + for i in range(0,len(clients)): + if(clients[i].id==client_id): + output=i + break + if use_mutex: clients_mutex.release() + if output==-1: + raise ClientNotFoundException + else: + return output + +def log_client(client, what): + print "[openwebrx-httpd] client {0}#{1} :: {2}".format(client.ip,client.id,what) + +def cleanup_clients(): + # if client doesn't open websocket for too long time, we drop it + global clients_mutex + global clients + clients_mutex.acquire() + correction=0 + for i in range(0,len(clients)): + i-=correction + #print "cleanup_clients:: len(clients)=", len(clients), "i=", i + if (not clients[i].ws_started) and (time.time()-clients[i].gen_time)>180: + close_client(i, False) + correction+=1 + clients_mutex.release() + +def generate_client_id(ip): + #add a client + global clients + global clients_mutex + new_client=namedtuple("ClientStruct", "id gen_time ws_started sprectum_queue ip") + new_client.id=md5.md5(str(random.random())).hexdigest() + new_client.gen_time=time.time() + new_client.ws_started=False # to check whether client has ever tried to open the websocket + new_client.spectrum_queue=Queue.Queue(1000) + new_client.ip=ip + clients_mutex.acquire() + clients.append(new_client) + log_client(new_client,"client added. Clients now: {0}".format(len(clients))) + clients_mutex.release() + cleanup_clients() + return new_client.id + +def close_client(i, use_mutex=True): + global clients_mutex + global clients + log_client(clients[i],"client being closed.") + if use_mutex: clients_mutex.acquire() + del clients[i] + if use_mutex: clients_mutex.release() + +class WebRXHandler(BaseHTTPRequestHandler): + def proc_read_thread(): + pass + + def do_GET(self): + print "does GET" + global admin_param_dict + rootdir = 'htdocs' + self.path=self.path.replace("..","") + path_temp_parts=self.path.split("?") + self.path=path_temp_parts[0] + request_param=path_temp_parts[1] if(len(path_temp_parts)>1) else "" + try: + if self.path=="/": + self.path="/index.wrx" + # there's even another cool tip at http://stackoverflow.com/questions/4419650/how-to-implement-timeout-in-basehttpserver-basehttprequesthandler-python + if self.path[:4]=="/ws/": + try: + try: + rxws.handshake(self) + clients_mutex.acquire() + client_i=get_client_by_id(self.path[4:], False) + myclient=clients[client_i] + clients_mutex.release() + except WebSocketException: + self.send_error(400, 'Bad request.') + return + except ClientNotFoundException: + self.send_error(400, 'Bad request.') + return + modulation="null" + #tb=client_ssb.top_block.top_block() + #tb.start() + #data=tb.msgq_out.delete_head().to_string() + data="" + rxws.send(self, "CLIENT DE SERVER openwebrx.py") + client_ans=rxws.recv(self, True) + if client_ans[:16]!="SERVER DE CLIENT": + rxws.send("ERR Bad answer.") + return + myclient.ws_started=True + rxws.send(self, "MSG center_freq={0} bandwidth={1} fft_size={2} fft_fps={3} setup".format(str(admin_param_dict["center_freq"]),str(admin_param_dict["samp_rate"]),config_webrx.fft_size,config_webrx.fft_fps)) + session_param_dict={ "offset_freq":(0,0,0), "audio_rate":(44100,0,48000), "low_cut":(-4000,-8000,8000), "high_cut":(4000,-8000,8000) } + while True: + if modulation!="null": + rxws.send(self, tb.msgq_out.delete_head().to_string(), "AUD ") + else: + print "modulation is null, didn't send audio" + while not myclient.spectrum_queue.empty(): + spectrum_data=myclient.spectrum_queue.get() + spectrum_data_mid=len(spectrum_data[0])/2 + rxws.send(self, spectrum_data[0][spectrum_data_mid:]+spectrum_data[0][:spectrum_data_mid], "FFT ") + # (it seems GNU Radio exchanges the first and second part of the FFT output, we correct it) + while True: + if modulation=="null": + rdata="SET mod=nfm" + else: + rdata=rxws.recv(self, False) + if not rdata: break + #try: + if rdata[:3]=="GET": + if rdata=="GET info": + for key,val in admin_param_dict.iteritems(): + print key,"=",getattr(tb, "get_"+key)(),"| should be",val + for key,val in session_param_dict.iteritems(): + print key,"=",getattr(tb, "get_"+key)(),"| should be",val[0] + elif rdata[:3]=="SET": + print rdata + pairs=rdata[4:].split(" ") + for pair in pairs: + param_name, param_value = pair.split("=") + if param_name in admin_param_dict: #TODO check if admin + setter_method = getattr(tb, "set_"+param_name) + setter_method(int(param_value)) + admin_param_dict[param_name]=int(param_value) + elif param_name in session_param_dict: + pval=int(param_value) + pbefore, pmin, pmax = session_param_dict[param_name] + if (pmax==0 and pmin==0) or (pval>=pmin and pval<=pmax): + if param_name=="low_cut" and pval>=tb.get_high_cut(): + tb.set_high_cut(tb.get_demod_rate()/2-1) + print "saved the wold today (lc); set_high_cut =",tb.get_demod_rate()/2-1 + elif param_name=="high_cut" and pval<=tb.get_low_cut(): + tb.set_low_cut(-tb.get_demod_rate()/2+1) + print "saved the wold today (hc); set_low_cut =",-tb.get_demod_rate()/2+1 + setter_method = getattr(tb, "set_"+param_name) + setter_method(pval) + session_param_dict[param_name]=(pval,pmin,pmax) + elif param_name=="mod": + if modulation!="null": + tb.stop() + invalid_modulation=0 + if param_value in demods: + tb=getattr(demod, param_value).top_block.top_block(admin_param_dict["samp_rate"]) + # you don't have any chance to set decimation rate in frequency_xlating_... block after having called + # top_block(), so I solved this by adding samplerate (of which decimation rate is calculated) + # as a parameter to top_block() + else: + print "ERR invalid modulation =", param_value + invalid_modulation=1 + if not invalid_modulation: + modulation=param_value + #code.interact(local=locals()) + tb.set_low_cut(-tb.get_demod_rate()/2+1) + tb.set_high_cut(tb.get_demod_rate()/2-1) + for key,val in admin_param_dict.iteritems(): + print key, val, "anew demod set" + setter_method = getattr(tb, "set_"+key) + setter_method(val) + for key,val in session_param_dict.iteritems(): + print key, val[0], "new demod set" + if key+"=" in rdata: continue + setter_method = getattr(tb, "set_"+key) + print tb.low_cut, tb.high_cut, tb.demod_rate + setter_method(val[0]) + tb.start() #tried tb.start(768) but is caused audio lagging sometimes + else: + print "[openwebrx-httpd:ws] invalid parameter" + except: + exc_type, exc_value, exc_traceback = sys.exc_info() + if exc_value[0]==32: #"broken pipe", client disconnected + pass + elif exc_value[0]==11: #"resource unavailable" on recv, client disconnected + pass + else: + print "[openwebrx-httpd] error: ",exc_type,exc_value + traceback.print_tb(exc_traceback) + #delete disconnected client + try: + if modulation!="null": tb.stop() + del tb + except: + pass + clients_mutex.acquire() + id_to_close=get_client_by_id(myclient.id,False) + close_client(id_to_close,False) + clients_mutex.release() + return + else: + f=open(rootdir+self.path) + data=f.read() + extension=self.path[(len(self.path)-4):len(self.path)] + extension=extension[2:] if extension[1]=='.' else extension[1:] + if extension == "wrx" and (not self.headers['user-agent'].count("Chrome")) and (not request_param.count("unsupported")): + self.send_response(302) + self.send_header('Content-type','text/html') + self.send_header("Location", "http://{0}:{1}/upgrade.html".format(config_webrx.server_hostname,config_webrx.web_port)) + self.end_headers() + self.wfile.write("<html><body><h1>Object moved</h1>Please <a href=\"/upgrade.html\">click here</a> to continue.</body></html>") + return + self.send_response(200) + if(("wrx","html","htm").count(extension)): + self.send_header('Content-type','text/html') + elif(extension=="js"): + self.send_header('Content-type','text/javascript') + elif(extension=="css"): + self.send_header('Content-type','text/css') + self.end_headers() + if extension == "wrx": + replace_dictionary=(("%[CLIENT_ID]",generate_client_id(self.client_address[0])),("%[WS_URL]","ws://"+config_webrx.server_hostname+":"+str(config_webrx.web_port)+"/ws/"),("%[RX_TITLE]",config_webrx.receiver_name),("%[RX_DESC]","{0} (Loc: {1}, ASL: {2} m)".format(config_webrx.receiver_location, config_webrx.receiver_qra, config_webrx.receiver_asl)),("%[RX_PHOTO_HEIGHT]",str(config_webrx.photo_height)),("%[RX_PHOTO_TITLE]",config_webrx.photo_title),("%[RX_PHOTO_DESC]",config_webrx.photo_desc)) + for rule in replace_dictionary: + while data.find(rule[0])!=-1: + data=data.replace(rule[0],rule[1]) + self.wfile.write(data) + f.close() + return + except IOError: + self.send_error(404, 'Invalid path.') + + + +class ClientNotFoundException(Exception): + pass + +if __name__=="__main__": + main() + +""" +TODO +- image decimate interpolate, implement as draw from-to +- wget Web Audio API +- javascript strings API +- replace readAsText with blob and use that + +SET samplerate=48000¢er=89500000&modulation= + +SET audio + +IDEAS +resample.js +auto-md5 admin passwd (lock icon for admin access) +osmosdr_mus +- both TCP and UCP +- password +GPU +PREDICT +Cloud Demodulator +search freq by search bar on top +the whole screen is fft +animated photo of site on start +sdr.hu/b +play parts already played +multi-demodulator +demod implemented in JS +PSK31/balloon decoder +save last frequency and mode to cookie +space invaders +record audio html5 +rf history (x minutes) +equalizer + +BUGS: ++ small window: white line under photo ++ clicking on canvas-container won't set frequency +------ ++ up arrow down arrow background transparent ++ small window: top bar congested +- small window: panels congested ++ avatar +- toggling photo on start will cause faulty animation +- Spectrum history - button to rewind to pos 0 and restart live fft +- Spectrum history - scrolled when looking at old signal ++ openwebrx-scale-container could go under panels ++ memory leak: eats memory ++ scale markers ++ spectrum pan ++ draw scale text corretly at edges ++ both zoom and pan fail to work on big screen +- remember last freq used +- rolling on scrollbar while panning stops pan operation ++ rtl_mus: ERROR client cache full +- improve latency (http://nutaq.com/en/blog/configure-latency-gnu-radio +""" diff --git a/rtl_mus.py b/rtl_mus.py new file mode 100755 index 0000000000000000000000000000000000000000..2c04572f65116f56f8faf9cc4ea74be12edd06c0 --- /dev/null +++ b/rtl_mus.py @@ -0,0 +1,326 @@ +''' +This file is part of RTL Multi-User Server, + that makes multi-user access to your DVB-T dongle used as an SDR. +Copyright (c) 2013 Andras Retzler <retzlerandras@gmail.com> + +RTL Multi-User Server is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +RTL Multi-User Server is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with RTL Multi-User Server. If not, see <http://www.gnu.org/licenses/>. +''' + +import socket +import sys +import array +import time +import logging +import os +import dl #[openwebrx] + +def ip_match(this,ip_ranges,for_allow): + if not len(ip_ranges): + return 1 #empty list matches all ip addresses + for ip_range in ip_ranges: + #print this[0:len(ip_range)], ip_range # [openwebrx] + if this[0:len(ip_range)]==ip_range: + return 1 + return 0 + +def ip_access_control(ip): + if(not cfg.use_ip_access_control): return 1 + allowed=0 + if(cfg.order_allow_deny): + if ip_match(ip,cfg.allowed_ip_ranges,1): allowed=1 + if ip_match(ip,cfg.denied_ip_ranges,0): allowed=0 + else: + if ip_match(ip,cfg.denied_ip_ranges,0): # [openwebrx] removed print + allowed=0 + if ip_match(ip,cfg.allowed_ip_ranges,1): # [openwebrx] removed print + allowed=1 + return allowed + +def rtl_tcp_connect(close=0): + global rtltcp_client_socket + global server_missing_logged + global rtl_dongle_identifier + if not server_missing_logged: + log.info("rtl_tcp host connection init: host=" + cfg.rtl_tcp_host + " port="+ str(cfg.rtl_tcp_port)) + if close: + try: + rtltcp_client_socket.close() + except socket.error as err: + log.error("rtl_tcp host connection close: "+str(err[0])+" "+err[1]) + rtltcp_client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + try: + rtltcp_client_socket.connect((cfg.rtl_tcp_host, cfg.rtl_tcp_port)) + rtl_dongle_identifier=rtltcp_client_socket.recv(12) + #rtl_tcp server sends us a 12 byte info on the dongle used at server init + #we'll send it to any clients connected to us + rtltcp_client_socket.send(cfg.send_first) # [openwebrx] we can set to direct sampling here + except socket.error as err: + if not server_missing_logged: + log.error("rtl_tcp host connection: "+str(err[0])+" "+err[1]) + server_missing_logged=1 + return 0 + rtltcp_client_socket.setblocking(1) # this does the trick, this is the ONLY blocking IO used + rtltcp_client_socket.settimeout(3) + log.info("rtl_tcp host connection estabilished") + server_missing_logged=0 + return 1 + +def handle_command(command, client_id): + param=array.array("I", command[1:5])[0] + param=socket.ntohl(param) + command_id=ord(command[0]) + client_info=str(client_id)+"@"+client_sockets[client_id][1][0]+":"+str(client_sockets[client_id][1][1]) + if(time.time()-client_start_times[client_id]<cfg.client_cant_set_until): + log.info("deny: "+client_info+" -> client can't set anything until "+str(cfg.client_cant_set_until)+" seconds") + return 0 + if command_id == 1: + if max(map((lambda r: param>=r[0] and param<=r[1]),cfg.freq_allowed_ranges)): + log.debug("allow: "+client_info+" -> set freq "+str(param)) + return 1 + else: + log.debug("deny: "+client_info+" -> set freq - out of range: "+str(param)) + elif command_id == 2: + log.debug("deny: "+client_info+" -> set sample rate: "+str(param)) + return 0 # ordinary clients are not allowed to do this + elif command_id == 3: + log.debug("deny/allow: "+client_info+" -> set gain mode: "+str(param)) + return cfg.allow_gain_set # for ordinary clients auto-gain will be set + elif command_id == 4: + log.debug("deny/allow: "+client_info+" -> set gain: "+str(param)) + return cfg.allow_gain_set # for ordinary clients auto-gain will be set + elif command_id == 5: + log.debug("deny: "+client_info+" -> set freq correction: "+str(param)) + return 0 # ordinary clients are not allowed to do this + elif command_id == 6: + log.debug("deny/allow: set if stage gain") + return cfg.allow_gain_set + elif command_id == 7: + log.debug("deny: set test mode") + return 0 + elif command_id == 8: + log.debug("deny/allow: set agc mode") + return cfg.allow_gain_set + elif command_id == 9: + log.debug("deny: set direct sampling") + return 0 + elif command_id == 10: + log.debug("deny: set offset tuning") + return 0 + elif command_id == 11: + log.debug("deny: set rtl xtal") + return 0 + elif command_id == 12: + log.debug("deny: set tuner xtal") + return 0 + elif command_id == 13: + log.debug("deny/allow: set tuner gain by index") + return cfg.allow_gain_set + else: + log.debug("deny: "+client_info+" sent an ivalid command: "+str(param)) + return 0 + +def close_clients(): + for which in client_indexes_to_close: + try: + log.info("client disconnected: "+str(which)+"@"+client_sockets[which][1][0]) + client_sockets[which][0].close() + except socket.error as err: + log.error("can't close client socket: "+ str(err[0])+" "+err[1]) + except IndexError: + log.error("can't close client socket: weird IndexError") + + del client_sockets[which] + del client_waiting_data[which] + del client_start_times[which] + +#I might implement decimation later, but not this way. - Andris, HA7ILM +''' +def average(input_data,average_items): + if average_items==1 or len(input_data)==0: + return input_data + input_bytes=bytearray(input_data) + output_bytes=bytearray() + j=0 + averaged_i=0 + averaged_q=0 + i_or_q_sample=0 + for i in range(0,len(input_data)): + if i_or_q_sample: + averaged_q+=input_bytes[i] + else: + averaged_i+=input_bytes[i] + if i_or_q_sample==0: + j+=1 + if j>=average_items: + j=0 + output_bytes.append(averaged_i/average_items) + output_bytes.append(averaged_q/average_items) + averaged_i=0 + averaged_q=0 + i_or_q_sample=~i_or_q_sample + return output_bytes +''' + +def main(): + global rtltcp_client_socket + global server_missing_logged + global rtl_dongle_identifier + global log + global client_indexes_to_close + global client_sockets + global client_start_times + global client_waiting_data + global command_send_errors + + #[openwebrx] + try: + for libcpath in ["/lib/i386-linux-gnu/libc.so.6","/lib/libc.so.6"]: + if os.path.exists(libcpath): + libc = dl.open(libcpath) + libc.call("prctl", 15, "rtl_mus-sdr", 0, 0, 0) + break + except: + pass + #/[openwebrx] + + # === Set up logging === + log = logging.getLogger("rtl_mus") + log.setLevel(logging.DEBUG) + formatter = logging.Formatter('%(asctime)s %(levelname)s %(message)s') + stream_handler = logging.StreamHandler() + stream_handler.setLevel(logging.DEBUG) + stream_handler.setFormatter(formatter) + log.addHandler(stream_handler) + file_handler = logging.FileHandler(cfg.log_file_path) + file_handler.setLevel(logging.INFO) + file_handler.setFormatter(formatter) + log.addHandler(file_handler) + log.info("Server is UP") + server_missing_logged=0 + rtl_dongle_identifier='' + rtl_tcp_connect() + my_listening_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + my_listening_socket.bind((cfg.my_ip, cfg.my_listening_port)) + my_listening_socket.listen(5) + my_listening_socket.setblocking(0) + log.info("server listening on port: "+str(cfg.my_listening_port)) + client_sockets=[] + client_waiting_data=[] + client_start_times=[] + commands='' + command_send_errors=0 + + while 1: + #First we try to ACCEPT new connections + try: + temp_client_socket = my_listening_socket.accept() # we get [socket, address] + temp_client_socket[0].setblocking(0) + if(ip_access_control(temp_client_socket[1][0])): + client_sockets.append(temp_client_socket) + client_start_times.append(time.time()) + client_waiting_data.append([]) + temp_client_socket[0].send(rtl_dongle_identifier) + log.info("client accepted: "+str(len(client_sockets)-1)+"@"+temp_client_socket[1][0]+":"+str(temp_client_socket[1][1])+" users now: "+str(len(client_sockets))) + else: + log.info("client denied: "+str(len(client_sockets)-1)+"@"+temp_client_socket[1][0]+":"+str(temp_client_socket[1][1])+" blocked by ip") + temp_client_socket[0].close() + except socket.error as err: + if err[0] != 11: + log.error("client accept(): "+str(err[0])+" "+err[1]) + + #We try to RECEIVE COMMANDS from our clients already connected + for client_id in range(len(client_sockets)): + client=client_sockets[client_id] + try: + for i in range(1, 10): # we only take care of ten commands per loop from a client + new_command = client[0].recv(5) # this is because sizeof(struct command) is 5 in rtl_tcp. + if len(new_command)>=5: + if handle_command(new_command, client_id): + commands+=new_command + except socket.error as err: + if err[0] != 11: + log.error("client recv(): "+str(err[0])+" "+err[1]) + + #We send the commands to the rtl_tcp server + if len(commands)>0: + try: + rtltcp_client_socket.send(commands) + command_send_errors=0 + commands='' + except socket.error as err: + command_send_errors+=1 + log.error("can't send command to RTL_TCP server, this happened",command_send_errors,"times continously.") + except msg: + log.error("rtl_tcp send(): "+msg) # might remove this + + #We get data from the rtl_tcp server + new_data='' + try: + while len(new_data)<16348: + new_data+=rtltcp_client_socket.recv(1024) # should do LPF and decimation here, if required for slower download speeds + except socket.error as err: + if err[0] != 11: + log.error("socket - "+`err`) # Bugfix/Andris/2013-05-10 (Tuple index out of range) + while not rtl_tcp_connect(1): + time.sleep(1) + if(len(new_data)==0): continue #! + # print len(new_data), "bytes of data acquired from RTL_TCP server" + + #We send data to all client sockets + client_indexes_to_close=[] + for client_i in range(0,len(client_sockets)): + client=client_sockets[client_i] + try: + waiting_data_len=0 + if len(client_waiting_data[client_i]): + waiting_data_len=reduce((lambda x,y:x+y),map(len,client_waiting_data[client_i])) + if len(new_data)+waiting_data_len<cfg.buffer_size: + client_waiting_data[client_i].append(new_data) + #http://itamarst.org/writings/pycon05/fast.html "Bad String Concatenation" was a cool tip + else: + log.error("client cache full: "+str(client_i)+"@"+client[1][0]+" now clearing cache") + client_waiting_data[client_i]=[new_data] + # print waiting_data_len, len(client_waiting_data[client_i]) + client_indexes_to_close.append(client_i) # We rather clear cache and hope client to resync soon | [openwebrx] patched + while len(client_waiting_data[client_i]): + client[0].send(client_waiting_data[client_i][0]) # FIXME might be problems here if we do it in the wrong order + del client_waiting_data[client_i][0] + #print "info: client", client_i, "got", len(client_waiting_data[client_i]), "bytes" + except socket.error as err: + if(err[0]==32): # broken pipe + client_indexes_to_close.append(client_i) + if(err[0]!=11): + log.error("client send(): "+str(err[0])+" "+err[1]) + close_clients() + + rtltcp_client_socket.close() + + +if __name__=="__main__": + print "RTL Multi-User Server v0.1a, made at HA5KFU Amateur Radio Club (http://ha5kfu.hu)" + print " code by Andras Retzler, HA7ILM" + print " distributed under GNU GPL v3" + print + # === Load configuration script === + if len(sys.argv)==1: + print "Warning! Configuration script not specified. I will use: \"config_rtl.py\"" + config_script="config_rtl" + else: + config_script=sys.argv[1] + cfg=__import__(config_script) + # print "debug config allow gain set:", cfg.allow_gain_set # [openwebrx] removed print + if cfg.setuid_on_start: + os.setuid(cfg.uid) + main() + diff --git a/rxws.py b/rxws.py new file mode 100644 index 0000000000000000000000000000000000000000..0b42d6cabbb348c35f798a51a01e6cf01dc999f3 --- /dev/null +++ b/rxws.py @@ -0,0 +1,148 @@ +""" +rxws: WebSocket methods implemented for OpenWebRX + +This file is part of OpenWebRX. + + OpenWebRX is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + OpenWebRX is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with OpenWebRX. If not, see <http://www.gnu.org/licenses/>. + +Authors: + Andras Retzler, HA7ILM <retzlerandras@gmail.com> + +""" + +import base64 +import sha +import select + +class WebSocketException(Exception): + pass + +def handshake(myself): + my_client_id=myself.path[4:] + my_headers=myself.headers.items() + my_header_keys=map(lambda x:x[0],my_headers) + h_key_exists=lambda x:my_header_keys.count(x) + h_value=lambda x:my_headers[my_header_keys.index(x)][1] + #print "The Lambdas(tm)" + #print h_key_exists("upgrade") + #print h_value("upgrade") + #print h_key_exists("sec-websocket-key") + if (not h_key_exists("upgrade")) or not (h_value("upgrade")=="websocket") or (not h_key_exists("sec-websocket-key")): + raise WebSocketException + ws_key=h_value("sec-websocket-key") + ws_key_toreturn=base64.b64encode(sha.new(ws_key+"258EAFA5-E914-47DA-95CA-C5AB0DC85B11").digest()) + #A sample list of keys we get: [('origin', 'http://localhost:8073'), ('upgrade', 'websocket'), ('sec-websocket-extensions', 'x-webkit-deflate-frame'), ('sec-websocket-version', '13'), ('host', 'localhost:8073'), ('sec-websocket-key', 't9J1rgy4fc9fg2Hshhnkmg=='), ('connection', 'Upgrade'), ('pragma', 'no-cache'), ('cache-control', 'no-cache')] + myself.connection.send("HTTP/1.1 101 Switching Protocols\r\nUpgrade: websocket\r\nConnection: Upgrade\r\nSec-WebSocket-Accept: "+ws_key_toreturn+"\r\nCQ-CQ-de: HA5KFU\r\n\r\n") + +def get_header(size): + #this does something similar: https://github.com/lemmingzshadow/php-websocket/blob/master/server/lib/WebSocket/Connection.php + ws_first_byte=0b10000010 # FIN=1, OP=2 + if(size>125): + ws_second_byte=126 # The following two bytes will indicate frame size + extended_size=chr((size>>8)&0xff)+chr(size&0xff) #Okay, it uses reverse byte order (little-endian) compared to anything else sent on TCP + else: + ws_second_byte=size + #256 bytes binary message in a single unmasked frame | 0x82 0x7E 0x0100 [256 bytes of binary data] + extended_size="" + return chr(ws_first_byte)+chr(ws_second_byte)+extended_size + +def code_payload(data, masking_key=""): + # both encode or decode + if masking_key=="": + key = (61, 84, 35, 6) + else: + key = [ord(i) for i in masking_key] + encoded="" + for i in range(0,len(data)): + encoded+=chr(ord(data[i])^key[i%4]) + return encoded + +def xxdg(data): + output="" + for i in range(0,len(data)/8): + output+=xxd(data[i:i+8]) + if i%2: output+="\n" + else: output+=" " + return output + + +def xxd(data): + #diagnostic purposes only + output="" + for d in data: + output+=hex(ord(d))[2:].zfill(2)+" " + return output + +def recv(myself, blocking=False, debug=False): + bufsize=70000 + myself.connection.setblocking(blocking) + if debug: print "ws_recv begin" + try: + data=myself.connection.recv(6) + #print "rxws.recv bytes:",xxd(data) + except: + if debug: print "ws_recv error" + return "" + if debug: print "ws_recv recved" + if(len(data)==0): return "" + fin=ord(data[0])&128!=0 + is_text_frame=ord(data[0])&15==1 + length=ord(data[1])&0x7f + data+=myself.connection.recv(length) + #print "rxws.recv length is ",length," (multiple packets together?) len(data) =",len(data) + has_one_byte_length=length<125 + masked=ord(data[1])&0x80!=0 + #print "len=", length, len(data)-2 + #print "fin, is_text_frame, has_one_byte_length, masked = ", (fin, is_text_frame, has_one_byte_length, masked) + #print xxd(data) + if fin and is_text_frame and has_one_byte_length: + if masked: + return code_payload(data[6:], data[2:6]) + else: + return data[2:] + +#Useful links for ideas on WebSockets: +# http://stackoverflow.com/questions/8125507/how-can-i-send-and-receive-websocket-messages-on-the-server-side +# https://developer.mozilla.org/en-US/docs/WebSockets/Writing_WebSocket_server +# http://tools.ietf.org/html/rfc6455#section-5.2 + + +def flush(myself): + lR,lW,lX = select.select([],[myself.connection,],[],60) + + +def send(myself, data, begin_id="", debug=0): + base_frame_size=35000 #could guess by MTU? + debug=0 + #try: + while True: + counter=0 + from_end=len(data)-counter + if from_end+len(begin_id)>base_frame_size: + data_to_send=begin_id+data[counter:counter+base_frame_size-len(begin_id)] + header=get_header(len(data_to_send)) + flush(myself) + myself.connection.send(header+data_to_send) + if debug: print "rxws.send ==================== #1 if branch :: from={0} to={1} dlen={2} hlen={3}".format(counter,counter+base_frame_size-len(begin_id),len(data_to_send),len(header)) + else: + data_to_send=begin_id+data[counter:] + header=get_header(len(data_to_send)) + flush(myself) + myself.connection.send(header+data_to_send) + if debug: print "rxws.send :: #2 else branch :: dlen={0} hlen={1}".format(len(data_to_send),len(header)) + #if debug: print "header:\n"+xxdg(header)+"\n\nws data:\n"+xxdg(data_to_send) + break + counter+=base_frame_size-len(begin_id) + #except: + # pass