Skip to content

Commit f7ad0b2

Browse files
committed
Merge remote-tracking branch 'upstream/master'
2 parents e5baef9 + eab4264 commit f7ad0b2

File tree

31 files changed

+3325
-214
lines changed

31 files changed

+3325
-214
lines changed

.github/pull_request_template.md

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,4 @@
11
# Important
22

3-
Only minor bug fixes and bandplans are accepted.
4-
5-
Pull requests adding features or any bug fix that requires significant code changes will be automatically rejected.
6-
3+
Only bandplan, colormaps and themes are accepted. Code pull requests are **NOT welcome**.
74
Open an issue requesting a feature or discussing a possible bugfix instead.

.github/workflows/build_all.yml

Lines changed: 12 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ jobs:
3434

3535
- name: Patch Pothos with earlier libusb version
3636
working-directory: ${{runner.workspace}}
37-
run: 7z x libusb.7z -olibusb_old ; rm "C:/Program Files/PothosSDR/bin/libusb-1.0.dll" ; cp "libusb_old/MS64/dll/libusb-1.0.dll" "C:/Program Files/PothosSDR/bin/"
37+
run: 7z x libusb.7z -olibusb_old ; rm "C:/Program Files/PothosSDR/bin/libusb-1.0.dll" ; cp "libusb_old/MS64/dll/libusb-1.0.dll" "C:/Program Files/PothosSDR/bin/" ; rm "C:/Program Files/PothosSDR/lib/libusb-1.0.lib" ; cp "libusb_old/MS64/dll/libusb-1.0.lib" "C:/Program Files/PothosSDR/lib/"
3838

3939
- name: Download SDRPlay API
4040
run: Invoke-WebRequest -Uri "https://drive.google.com/uc?id=12UHPMwkfa67A11QZDmpCT4iwHnyJHWuu&confirm=t" -OutFile ${{runner.workspace}}/SDRPlay.zip
@@ -64,7 +64,7 @@ jobs:
6464
run: git clone https://github.com/thestk/rtaudio ; cd rtaudio ; git checkout 2f2fca4502d506abc50f6d4473b2836d24cfb1e3 ; mkdir build ; cd build ; cmake .. ; cmake --build . --config Release ; cmake --install .
6565

6666
- name: Install libperseus-sdr
67-
run: git clone https://github.com/AlexandreRouma/libperseus-sdr ; cd libperseus-sdr ; mkdir build ; cd build ; cmake .. "-DCMAKE_TOOLCHAIN_FILE=C:/vcpkg/scripts/buildsystems/vcpkg.cmake" ; cmake --build . --config Release ; mkdir "C:/Program Files/PothosSDR/include/perseus-sdr" ; cp Release/perseus-sdr.dll "C:/Program Files/PothosSDR/bin" ; cp Release/perseus-sdr.lib "C:/Program Files/PothosSDR/bin" ; cd .. ; xcopy "src" "C:/Program Files/PothosSDR/include/perseus-sdr"
67+
run: git clone https://github.com/AlexandreRouma/libperseus-sdr ; cd libperseus-sdr ; mkdir build ; cd build ; cmake "-DLIBUSB_LIBRARIES=C:/Program Files/PothosSDR/lib/libusb-1.0.lib" "-DLIBUSB_INCLUDE_DIRS=C:/Program Files/PothosSDR/include/libusb-1.0" .. "-DCMAKE_TOOLCHAIN_FILE=C:/vcpkg/scripts/buildsystems/vcpkg.cmake" ; cmake --build . --config Release ; mkdir "C:/Program Files/PothosSDR/include/perseus-sdr" ; cp Release/perseus-sdr.dll "C:/Program Files/PothosSDR/bin" ; cp Release/perseus-sdr.lib "C:/Program Files/PothosSDR/bin" ; cd .. ; xcopy "src" "C:/Program Files/PothosSDR/include/perseus-sdr"
6868

6969
- name: Prepare CMake
7070
working-directory: ${{runner.workspace}}/build
@@ -85,7 +85,7 @@ jobs:
8585
path: ${{runner.workspace}}/sdrpp_windows_x64.zip
8686

8787
build_macos:
88-
runs-on: macos-11
88+
runs-on: macos-latest
8989

9090
steps:
9191
- uses: actions/checkout@v3
@@ -100,29 +100,29 @@ jobs:
100100
run: brew install pkg-config libusb fftw glfw airspy airspyhf portaudio hackrf libbladerf codec2 zstd autoconf automake libtool && pip3 install mako
101101

102102
- name: Install volk
103-
run: git clone --recursive https://github.com/gnuradio/volk && cd volk && mkdir build && cd build && cmake -DCMAKE_BUILD_TYPE=Release .. && make -j3 && sudo make install && cd ../../
103+
run: git clone --recursive https://github.com/gnuradio/volk && cd volk && mkdir build && cd build && cmake -DCMAKE_OSX_DEPLOYMENT_TARGET=10.15 -DCMAKE_BUILD_TYPE=Release .. && make -j3 && sudo make install && cd ../../
104104

105105
- name: Install SDRplay API
106106
run: wget https://www.sdrplay.com/software/SDRplay_RSP_API-MacOSX-3.07.3.pkg && sudo installer -pkg SDRplay_RSP_API-MacOSX-3.07.3.pkg -target /
107107

108108
- name: Install libiio
109-
run: wget https://github.com/analogdevicesinc/libiio/archive/refs/tags/v0.25.zip && 7z x v0.25.zip && cd libiio-0.25 && mkdir build && cd build && cmake -DCMAKE_BUILD_TYPE=Release .. && make -j3 && sudo make install && cd ../../
109+
run: wget https://github.com/analogdevicesinc/libiio/archive/refs/tags/v0.25.zip && 7z x v0.25.zip && cd libiio-0.25 && mkdir build && cd build && cmake -DCMAKE_OSX_DEPLOYMENT_TARGET=10.15 -DCMAKE_BUILD_TYPE=Release .. && make -j3 && sudo make install && cd ../../
110110

111111
- name: Install libad9361
112-
run: git clone https://github.com/analogdevicesinc/libad9361-iio && cd libad9361-iio && mkdir build && cd build && cmake -DCMAKE_BUILD_TYPE=Release .. && make -j3 && sudo make install && cd ../../
112+
run: git clone https://github.com/analogdevicesinc/libad9361-iio && cd libad9361-iio && mkdir build && cd build && cmake -DCMAKE_OSX_DEPLOYMENT_TARGET=10.15 -DCMAKE_BUILD_TYPE=Release .. && make -j3 && sudo make install && cd ../../
113113

114114
- name: Install LimeSuite
115-
run: git clone https://github.com/myriadrf/LimeSuite && cd LimeSuite && mkdir builddir && cd builddir && cmake -DCMAKE_BUILD_TYPE=Release .. && make -j3 && sudo make install && cd ../../
115+
run: git clone https://github.com/myriadrf/LimeSuite && cd LimeSuite && mkdir builddir && cd builddir && cmake -DCMAKE_OSX_DEPLOYMENT_TARGET=10.15 -DCMAKE_BUILD_TYPE=Release .. && make -j3 && sudo make install && cd ../../
116116

117117
- name: Install libperseus
118118
run: git clone https://github.com/Microtelecom/libperseus-sdr && cd libperseus-sdr && autoreconf -i && ./configure --prefix=/usr/local --prefix=/usr/local && make && make install && cd ..
119119

120120
- name: Install modified librtlsdr
121-
run: git clone https://github.com/Mr-Precise/rtl-sdr && cd rtl-sdr && mkdir build && cd build && cmake -DCMAKE_BUILD_TYPE=Release .. && make -j3 LIBRARY_PATH=$(pkg-config --libs-only-L libusb-1.0 | sed 's/\-L//') && sudo make install && cd ../../
121+
run: git clone https://github.com/Mr-Precise/rtl-sdr && cd rtl-sdr && mkdir build && cd build && cmake -DCMAKE_OSX_DEPLOYMENT_TARGET=10.15 -DCMAKE_BUILD_TYPE=Release .. && make -j3 LIBRARY_PATH=$(pkg-config --libs-only-L libusb-1.0 | sed 's/\-L//') && sudo make install && cd ../../
122122

123123
- name: Prepare CMake
124124
working-directory: ${{runner.workspace}}/build
125-
run: cmake $GITHUB_WORKSPACE -DOPT_BUILD_PLUTOSDR_SOURCE=ON -DOPT_BUILD_SOAPY_SOURCE=OFF -DOPT_BUILD_BLADERF_SOURCE=ON -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_AUDIO_SINK=OFF -DOPT_BUILD_PORTAUDIO_SINK=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_BUILD_M17_DECODER=ON -DOPT_BUILD_PERSEUS_SOURCE=ON -DOPT_BUILD_AUDIO_SOURCE=OFF -DUSE_BUNDLE_DEFAULTS=ON -DCMAKE_BUILD_TYPE=Release
125+
run: cmake -DCMAKE_OSX_DEPLOYMENT_TARGET=10.15 $GITHUB_WORKSPACE -DOPT_BUILD_PLUTOSDR_SOURCE=ON -DOPT_BUILD_SOAPY_SOURCE=OFF -DOPT_BUILD_BLADERF_SOURCE=ON -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_AUDIO_SINK=OFF -DOPT_BUILD_PORTAUDIO_SINK=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_BUILD_M17_DECODER=ON -DOPT_BUILD_PERSEUS_SOURCE=ON -DOPT_BUILD_AUDIO_SOURCE=OFF -DUSE_BUNDLE_DEFAULTS=ON -DCMAKE_BUILD_TYPE=Release
126126

127127
- name: Build
128128
working-directory: ${{runner.workspace}}/build
@@ -136,68 +136,14 @@ jobs:
136136
uses: actions/upload-artifact@v3
137137
with:
138138
name: sdrpp_macos_intel
139-
path: ${{runner.workspace}}/sdrpp_macos_intel.zip
139+
path: ${{runner.workspace}}/sdrpp_macos_intel.zip
140140

141-
build_macos13:
142-
runs-on: macos-13
143-
144-
steps:
145-
- uses: actions/checkout@v3
146-
147-
- name: Create Build Environment
148-
run: cmake -E make_directory ${{runner.workspace}}/build
149-
150-
- name: Update brew repositories
151-
run: brew update
152-
153-
- name: Install dependencies
154-
run: brew install pkg-config libusb fftw glfw airspy airspyhf portaudio hackrf libbladerf codec2 zstd autoconf automake libtool && pip3 install mako
155-
156-
- name: Install volk
157-
run: git clone --recursive https://github.com/gnuradio/volk && cd volk && mkdir build && cd build && cmake -DCMAKE_BUILD_TYPE=Release .. && make -j3 && sudo make install && cd ../../
158-
159-
- name: Install SDRplay API
160-
run: wget https://www.sdrplay.com/software/SDRplay_RSP_API-MacOSX-3.07.3.pkg && sudo installer -pkg SDRplay_RSP_API-MacOSX-3.07.3.pkg -target /
161-
162-
- name: Install libiio
163-
run: wget https://github.com/analogdevicesinc/libiio/archive/refs/tags/v0.25.zip && 7z x v0.25.zip && cd libiio-0.25 && mkdir build && cd build && cmake -DCMAKE_BUILD_TYPE=Release .. && make -j3 && sudo make install && cd ../../
164-
165-
- name: Install libad9361
166-
run: git clone https://github.com/analogdevicesinc/libad9361-iio && cd libad9361-iio && mkdir build && cd build && cmake -DCMAKE_BUILD_TYPE=Release .. && make -j3 && sudo make install && cd ../../
167-
168-
- name: Install LimeSuite
169-
run: git clone https://github.com/myriadrf/LimeSuite && cd LimeSuite && mkdir builddir && cd builddir && cmake -DCMAKE_BUILD_TYPE=Release .. && make -j3 && sudo make install && cd ../../
170-
171-
- name: Install libperseus
172-
run: git clone https://github.com/Microtelecom/libperseus-sdr && cd libperseus-sdr && autoreconf -i && ./configure --prefix=/usr/local && make && make install && cd ..
173-
174-
- name: Install modified librtlsdr
175-
run: git clone https://github.com/Mr-Precise/rtl-sdr && cd rtl-sdr && mkdir build && cd build && cmake -DCMAKE_BUILD_TYPE=Release .. && make -j3 LIBRARY_PATH=$(pkg-config --libs-only-L libusb-1.0 | sed 's/\-L//') && sudo make install && cd ../../
176-
177-
- name: Prepare CMake
178-
working-directory: ${{runner.workspace}}/build
179-
run: cmake $GITHUB_WORKSPACE -DOPT_BUILD_PLUTOSDR_SOURCE=ON -DOPT_BUILD_SOAPY_SOURCE=OFF -DOPT_BUILD_BLADERF_SOURCE=ON -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_AUDIO_SINK=OFF -DOPT_BUILD_PORTAUDIO_SINK=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_BUILD_M17_DECODER=ON -DOPT_BUILD_PERSEUS_SOURCE=ON -DOPT_BUILD_AUDIO_SOURCE=OFF -DUSE_BUNDLE_DEFAULTS=ON -DCMAKE_BUILD_TYPE=Release
180-
181-
- name: Build
182-
working-directory: ${{runner.workspace}}/build
183-
run: make VERBOSE=1 -j3
184-
185-
- name: Create Archive
186-
working-directory: ${{runner.workspace}}
187-
run: cd $GITHUB_WORKSPACE && sh make_macos_bundle.sh ${{runner.workspace}}/build ./SDR++.app && zip -r ${{runner.workspace}}/sdrpp_macos13_intel.zip SDR++.app
188-
189-
- name: Save Archive
190-
uses: actions/upload-artifact@v3
191-
with:
192-
name: sdrpp_macos13_intel
193-
path: ${{runner.workspace}}/sdrpp_macos13_intel.zip
194-
195141
build_debian_buster:
196142
runs-on: ubuntu-latest
197143

198144
steps:
199145
- uses: actions/checkout@v3
200-
146+
201147
- name: Create Docker Image
202148
run: cd $GITHUB_WORKSPACE/docker_builds/debian_buster && docker build . --tag sdrpp_build
203149

@@ -352,7 +298,7 @@ jobs:
352298
path: ${{runner.workspace}}/sdrpp.apk
353299

354300
create_full_archive:
355-
needs: ['build_windows', 'build_macos', 'build_macos13', 'build_debian_buster', 'build_debian_bullseye', 'build_debian_bookworm', 'build_debian_sid', 'build_ubuntu_focal', 'build_ubuntu_jammy', 'build_android']
301+
needs: ['build_windows', 'build_macos', 'build_debian_buster', 'build_debian_bullseye', 'build_debian_bookworm', 'build_debian_sid', 'build_ubuntu_focal', 'build_ubuntu_jammy', 'build_android']
356302
runs-on: ubuntu-latest
357303

358304
steps:
@@ -364,7 +310,6 @@ jobs:
364310
mkdir sdrpp_all &&
365311
mv sdrpp_windows_x64/sdrpp_windows_x64.zip sdrpp_all/ &&
366312
mv sdrpp_macos_intel/sdrpp_macos_intel.zip sdrpp_all/ &&
367-
mv sdrpp_macos13_intel/sdrpp_macos13_intel.zip sdrpp_all/ &&
368313
mv sdrpp_debian_buster_amd64/sdrpp_debian_amd64.deb sdrpp_all/sdrpp_debian_buster_amd64.deb &&
369314
mv sdrpp_debian_bullseye_amd64/sdrpp_debian_amd64.deb sdrpp_all/sdrpp_debian_bullseye_amd64.deb &&
370315
mv sdrpp_debian_bookworm_amd64/sdrpp_debian_amd64.deb sdrpp_all/sdrpp_debian_bookworm_amd64.deb &&

contributing.md

Lines changed: 5 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -1,65 +1,6 @@
11
# Pull Requests
22

3-
**I DO NOT ACCEPT PULL-REQUEST FOR FEATURES OR BUGFIXES REQUIRING SIGNIFICANT CODE/STRUCTURE CHANGES.**
4-
**SUCH PULL REQUESTS WILL BE CLOSED AUTOMATICALLY. OPEN AN ISSUE DETAILING FEATURE REQUESTS OR POTENTIAL BUGFIX INSTEAD.**
5-
6-
# Code Style
7-
8-
## Naming Convention
9-
10-
- Files: `snake_case.h` `snake_case.cpp`
11-
- Namespaces: `CamelCase`
12-
- Classes: `CamelCase`
13-
- Structs: `CamelCase_t`
14-
- Members: `camelCase`
15-
- Enum: `SNAKE_CASE`
16-
- Macros: `SNAKE_CASE`
17-
18-
## Brace Style
19-
20-
```c++
21-
int myFunction() {
22-
if (shortIf) { shortFunctionName(); }
23-
24-
if (longIf) {
25-
longFunction();
26-
otherStuff();
27-
myLongFunction();
28-
}
29-
}
30-
```
31-
32-
Note: If it makes the code cleaner, remember to use the `?` keyword instead of a `if else` statement.
33-
34-
## Pointers
35-
36-
Please use `type* name` for pointers.
37-
38-
## Structure
39-
40-
Headers and their associated C++ files shall be in the same directory. All headers must use `#pragma once` instead of other include guards. Only include files in a header that are being used in that header. Include the rest in the associated C++ file.
41-
42-
# Modules
43-
44-
## Module Naming Convention
45-
46-
All modules names must be `snake_case`. If the module is a source, it must end with `_source`. If it is a sink, it must end with `_sink`.
47-
48-
For example, lets take the module named `cool_source`:
49-
50-
- Directory: `cool_source`
51-
- Class: `CoolSourceModule`
52-
- Binary: `cool_source.<os dynlib extension>`
53-
54-
## Integration into main repository
55-
56-
If the module meets the code quality requirements, it may be added to the official repository. A module that doesn't require any external dependencies that the core doesn't already use may be enabled for build by default. Otherwise, they must be disabled for build by default with a `OPT_BUILD_MODULE_NAME` variable set to `OFF`.
57-
58-
# JSON Formatting
59-
60-
The ability to add new radio band allocation identifiers and color maps relies on JSON files. Proper formatting of these JSON files is important for reference and readability. The following guides will show you how to properly format the JSON files for their respective uses.
61-
62-
**IMPORTANT: JSON File cannot contain comments, there are only in this example for clarity**
3+
Code pull requests are **NOT welcome**. Please open an issue discussing potential bugfixes or feature requests instead.
634

645
## Band Frequency Allocation
656

@@ -119,8 +60,8 @@ Please follow this guide to properly format the JSON files for custom color maps
11960
}
12061
```
12162

122-
# Best Practices
63+
# JSON Formatting
64+
65+
The ability to add new radio band allocation identifiers and color maps relies on JSON files. Proper formatting of these JSON files is important for reference and readability. The following guides will show you how to properly format the JSON files for their respective uses.
12366

124-
* All additions and/or bug fixes to the core must not add additional dependencies.
125-
* Use VSCode for development, VS seems to cause issues.
126-
* DO NOT use libboost for any code meant for this repository
67+
**IMPORTANT: JSON File cannot contain comments, there are only in this example for clarity**

core/CMakeLists.txt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,6 @@ elseif (ANDROID)
108108
)
109109

110110
target_link_libraries(sdrpp_core PUBLIC
111-
/sdr-kit/${ANDROID_ABI}/lib/libcpu_features.a
112111
/sdr-kit/${ANDROID_ABI}/lib/libvolk.so
113112
/sdr-kit/${ANDROID_ABI}/lib/libfftw3f.so
114113
/sdr-kit/${ANDROID_ABI}/lib/libzstd.so

core/src/command_args.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ int CommandArgsParser::parse(int argc, char* argv[]) {
8888
try {
8989
carg.ival = std::stoi(arg);
9090
}
91-
catch (std::exception e) {
91+
catch (const std::exception& e) {
9292
printf("Invalid argument, failed to parse integer\n");
9393
showHelp();
9494
return -1;
@@ -98,7 +98,7 @@ int CommandArgsParser::parse(int argc, char* argv[]) {
9898
try {
9999
carg.fval = std::stod(arg);
100100
}
101-
catch (std::exception e) {
101+
catch (const std::exception& e) {
102102
printf("Invalid argument, failed to parse float\n");
103103
showHelp();
104104
return -1;

core/src/config.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,8 @@ void ConfigManager::load(json def, bool lock) {
3636
file >> conf;
3737
file.close();
3838
}
39-
catch (std::exception e) {
40-
flog::error("Config file '{0}' is corrupted, resetting it", path);
39+
catch (const std::exception& e) {
40+
flog::error("Config file '{}' is corrupted, resetting it: {}", path, e.what());
4141
conf = def;
4242
save(false);
4343
}

core/src/dsp/chain.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ namespace dsp {
9393
void disableBlock(Processor<T, T>* block, Func onOutputChange) {
9494
// Check that the block is part of the chain
9595
if (!blockExists(block)) {
96-
throw std::runtime_error("[chain] Tried to enable a block that isn't part of the chain");
96+
throw std::runtime_error("[chain] Tried to disable a block that isn't part of the chain");
9797
}
9898

9999
// If already disabled, don't do anything
@@ -163,10 +163,12 @@ namespace dsp {
163163

164164
private:
165165
Processor<T, T>* blockBefore(Processor<T, T>* block) {
166+
// TODO: This is wrong and must be fixed when I get more time
166167
for (auto& ln : links) {
167168
if (ln == block) { return NULL; }
168169
if (states[ln]) { return ln; }
169170
}
171+
return NULL;
170172
}
171173

172174
Processor<T, T>* blockAfter(Processor<T, T>* block) {

core/src/dsp/demod/ssb.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ namespace dsp::demod {
110110
else if (_mode == Mode::LSB) {
111111
return -_bandwidth / 2.0;
112112
}
113-
else if (_mode == Mode::DSB) {
113+
else {
114114
return 0.0;
115115
}
116116
}

core/src/gui/main_window.cpp

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -574,10 +574,22 @@ void MainWindow::draw() {
574574
// Handle scrollwheel
575575
int wheel = ImGui::GetIO().MouseWheel;
576576
if (wheel != 0 && (gui::waterfall.mouseInFFT || gui::waterfall.mouseInWaterfall)) {
577+
// Select factor depending on modifier keys
578+
double interval;
579+
if (ImGui::IsKeyDown(ImGuiKey_LeftShift)) {
580+
interval = vfo->snapInterval * 10.0;
581+
}
582+
else if (ImGui::IsKeyDown(ImGuiKey_LeftAlt)) {
583+
interval = vfo->snapInterval * 0.1;
584+
}
585+
else {
586+
interval = vfo->snapInterval;
587+
}
588+
577589
double nfreq;
578590
if (vfo != NULL) {
579-
nfreq = gui::waterfall.getCenterFrequency() + vfo->generalOffset + (vfo->snapInterval * wheel);
580-
nfreq = roundl(nfreq / vfo->snapInterval) * vfo->snapInterval;
591+
nfreq = gui::waterfall.getCenterFrequency() + vfo->generalOffset + (interval * wheel);
592+
nfreq = roundl(nfreq / interval) * interval;
581593
}
582594
else {
583595
nfreq = gui::waterfall.getCenterFrequency() - (gui::waterfall.getViewBandwidth() * wheel / 20.0);

0 commit comments

Comments
 (0)