mirror of
				https://github.com/AlexandreRouma/SDRPlusPlus.git
				synced 2025-11-04 10:49:11 +01:00 
			
		
		
		
	push before merge
This commit is contained in:
		
							
								
								
									
										2
									
								
								.github/workflows/cmake.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.github/workflows/cmake.yml
									
									
									
									
										vendored
									
									
								
							@@ -18,7 +18,7 @@ jobs:
 | 
			
		||||
    - uses: actions/checkout@v2
 | 
			
		||||
    
 | 
			
		||||
    - name: Install dependencies
 | 
			
		||||
      run: sudo apt install libfftw3-dev libglfw3-dev libglew-dev libvolk2-dev libsoapysdr-dev libairspyhf-dev libiio-dev libad9361-dev portaudio19-dev
 | 
			
		||||
      run: sudo apt install libfftw3-dev libglfw3-dev libglew-dev libvolk2-dev libsoapysdr-dev libairspyhf-dev libiio-dev libad9361-dev portaudio19-dev libhackrf-dev
 | 
			
		||||
 | 
			
		||||
    - name: Create Build Environment
 | 
			
		||||
      # Some projects don't allow in-source building, so create a separate build directory
 | 
			
		||||
 
 | 
			
		||||
@@ -6,6 +6,7 @@ option(OPT_BUILD_SPYSERVER_SOURCE "Build SpyServer Source Module (no dependencie
 | 
			
		||||
option(OPT_BUILD_SOAPY_SOURCE "Build SoapySDR Source Module (Depedencies: soapysdr)" ON)
 | 
			
		||||
option(OPT_BUILD_AIRSPYHF_SOURCE "Build Airspy HF+ Source Module (Depedencies: libairspyhf)" ON)
 | 
			
		||||
option(OPT_BUILD_PLUTOSDR_SOURCE "Build PlutoSDR Source Module (Depedencies: libiio, libad9361)" ON)
 | 
			
		||||
option(OPT_BUILD_HACKRF_SOURCE "Build HackRF Source Module (Depedencies: libhackrf)" ON)
 | 
			
		||||
option(OPT_BUILD_AUDIO_SINK "Build Audio Sink Module (Depedencies: portaudio)" ON)
 | 
			
		||||
 | 
			
		||||
# Core of SDR++
 | 
			
		||||
@@ -36,6 +37,10 @@ if (OPT_BUILD_PLUTOSDR_SOURCE)
 | 
			
		||||
add_subdirectory("plutosdr_source")
 | 
			
		||||
endif (OPT_BUILD_PLUTOSDR_SOURCE)
 | 
			
		||||
 | 
			
		||||
if (OPT_BUILD_HACKRF_SOURCE)
 | 
			
		||||
add_subdirectory("hackrf_source")
 | 
			
		||||
endif (OPT_BUILD_HACKRF_SOURCE)
 | 
			
		||||
 | 
			
		||||
if (OPT_BUILD_AUDIO_SINK)
 | 
			
		||||
add_subdirectory("audio_sink")
 | 
			
		||||
endif (OPT_BUILD_AUDIO_SINK)
 | 
			
		||||
 
 | 
			
		||||
@@ -166,8 +166,6 @@ private:
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        spdlog::warn("{0}", _this->sampleRateList[_this->srId]);
 | 
			
		||||
 | 
			
		||||
        airspyhf_set_samplerate(_this->openDev, _this->sampleRateList[_this->srId]);
 | 
			
		||||
        airspyhf_set_freq(_this->openDev, _this->freq);
 | 
			
		||||
        airspyhf_set_hf_agc(_this->openDev, (_this->agcMode != 0));
 | 
			
		||||
 
 | 
			
		||||
@@ -36,7 +36,6 @@ public:
 | 
			
		||||
        stereoRB.init(_stream->sinkOut);
 | 
			
		||||
 | 
			
		||||
        // Initialize PortAudio
 | 
			
		||||
        Pa_Initialize();
 | 
			
		||||
        devCount = Pa_GetDeviceCount();
 | 
			
		||||
        devId = Pa_GetDefaultOutputDevice();
 | 
			
		||||
        const PaDeviceInfo *deviceInfo;
 | 
			
		||||
@@ -239,11 +238,14 @@ public:
 | 
			
		||||
        this->name = name;
 | 
			
		||||
        provider.create = create_sink;
 | 
			
		||||
        provider.ctx = this;
 | 
			
		||||
 | 
			
		||||
        Pa_Initialize();
 | 
			
		||||
 | 
			
		||||
        sigpath::sinkManager.registerSinkProvider("Audio", provider);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ~AudioSinkModule() {
 | 
			
		||||
 | 
			
		||||
        Pa_Terminate();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void enable() {
 | 
			
		||||
 
 | 
			
		||||
@@ -125,6 +125,7 @@ int sdrpp_main(int argc, char *argv[]) {
 | 
			
		||||
    defConfig["moduleInstances"]["PlutoSDR Source"] = "plutosdr_source";
 | 
			
		||||
    defConfig["moduleInstances"]["RTL-TCP Source"] = "rtl_tcp_source";
 | 
			
		||||
    defConfig["moduleInstances"]["AirspyHF+ Source"] = "airspyhf_source";
 | 
			
		||||
    defConfig["moduleInstances"]["HackRF Source"] = "hackrf_source";
 | 
			
		||||
    defConfig["moduleInstances"]["Audio Sink"] = "audio_sink";
 | 
			
		||||
 | 
			
		||||
    defConfig["modules"] = json::array();
 | 
			
		||||
 
 | 
			
		||||
@@ -16,6 +16,7 @@ public:
 | 
			
		||||
 | 
			
		||||
    class Sink {
 | 
			
		||||
    public:
 | 
			
		||||
        virtual ~Sink() {}
 | 
			
		||||
        virtual void start() = 0;
 | 
			
		||||
        virtual void stop() = 0;
 | 
			
		||||
        virtual void menuHandler() = 0;
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										31
									
								
								hackrf_source/CMakeLists.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								hackrf_source/CMakeLists.txt
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,31 @@
 | 
			
		||||
cmake_minimum_required(VERSION 3.13)
 | 
			
		||||
project(hackrf_source)
 | 
			
		||||
 | 
			
		||||
if (MSVC)
 | 
			
		||||
    set(CMAKE_CXX_FLAGS "-O2 /std:c++17 /EHsc")
 | 
			
		||||
else()
 | 
			
		||||
    set(CMAKE_CXX_FLAGS "-O3 -std=c++17 -fpermissive")
 | 
			
		||||
endif (MSVC)
 | 
			
		||||
 | 
			
		||||
include_directories("src/")
 | 
			
		||||
 | 
			
		||||
file(GLOB SRC "src/*.cpp")
 | 
			
		||||
 | 
			
		||||
add_library(hackrf_source SHARED ${SRC})
 | 
			
		||||
target_link_libraries(hackrf_source PRIVATE sdrpp_core)
 | 
			
		||||
set_target_properties(hackrf_source PROPERTIES PREFIX "")
 | 
			
		||||
 | 
			
		||||
if (MSVC)
 | 
			
		||||
    # Lib path
 | 
			
		||||
    target_link_directories(sdrpp_core PUBLIC "C:/Program Files/PothosSDR/bin/")
 | 
			
		||||
 | 
			
		||||
    target_link_libraries(hackrf_source PUBLIC hackrf)
 | 
			
		||||
else (MSVC)
 | 
			
		||||
    find_package(PkgConfig)
 | 
			
		||||
 | 
			
		||||
    pkg_check_modules(LIBHACKRF REQUIRED libhackrf)
 | 
			
		||||
 | 
			
		||||
    target_include_directories(hackrf_source PUBLIC ${LIBHACKRF_INCLUDE_DIRS})
 | 
			
		||||
    target_link_directories(hackrf_source PUBLIC ${LIBHACKRF_LIBRARY_DIRS})
 | 
			
		||||
    target_link_libraries(hackrf_source PUBLIC ${LIBHACKRF_LIBRARIES})
 | 
			
		||||
endif (MSVC)
 | 
			
		||||
							
								
								
									
										275
									
								
								hackrf_source/src/main.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										275
									
								
								hackrf_source/src/main.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,275 @@
 | 
			
		||||
#include <imgui.h>
 | 
			
		||||
#include <spdlog/spdlog.h>
 | 
			
		||||
#include <module.h>
 | 
			
		||||
#include <gui/gui.h>
 | 
			
		||||
#include <signal_path/signal_path.h>
 | 
			
		||||
#include <core.h>
 | 
			
		||||
#include <gui/style.h>
 | 
			
		||||
#include <config.h>
 | 
			
		||||
#include <libhackrf/hackrf.h>
 | 
			
		||||
 | 
			
		||||
#define CONCAT(a, b) ((std::string(a) + b).c_str())
 | 
			
		||||
 | 
			
		||||
SDRPP_MOD_INFO {
 | 
			
		||||
    /* Name:            */ "hackrf_source",
 | 
			
		||||
    /* Description:     */ "HackRF source module for SDR++",
 | 
			
		||||
    /* Author:          */ "Ryzerth",
 | 
			
		||||
    /* Version:         */ 0, 1, 0,
 | 
			
		||||
    /* Max instances    */ 1
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
//ConfigManager config;
 | 
			
		||||
 | 
			
		||||
const char* AGG_MODES_STR = "Off\0Low\0High\0";
 | 
			
		||||
 | 
			
		||||
const char* sampleRatesTxt = "20MHz\00016MHz\00010MHz\0008MHz\0005MHz\0004MHz\0002MHz\000";
 | 
			
		||||
 | 
			
		||||
const int sampleRates[] = {
 | 
			
		||||
    20000000,
 | 
			
		||||
    16000000,
 | 
			
		||||
    10000000,
 | 
			
		||||
    8000000,
 | 
			
		||||
    5000000,
 | 
			
		||||
    4000000,
 | 
			
		||||
    2000000,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class HackRFSourceModule : public ModuleManager::Instance {
 | 
			
		||||
public:
 | 
			
		||||
    HackRFSourceModule(std::string name) {
 | 
			
		||||
        this->name = name;
 | 
			
		||||
 | 
			
		||||
        hackrf_init();
 | 
			
		||||
 | 
			
		||||
        sampleRate = 2000000;
 | 
			
		||||
 | 
			
		||||
        handler.ctx = this;
 | 
			
		||||
        handler.selectHandler = menuSelected;
 | 
			
		||||
        handler.deselectHandler = menuDeselected;
 | 
			
		||||
        handler.menuHandler = menuHandler;
 | 
			
		||||
        handler.startHandler = start;
 | 
			
		||||
        handler.stopHandler = stop;
 | 
			
		||||
        handler.tuneHandler = tune;
 | 
			
		||||
        handler.stream = &stream;
 | 
			
		||||
 | 
			
		||||
        refresh();
 | 
			
		||||
 | 
			
		||||
        selectFirst();
 | 
			
		||||
 | 
			
		||||
        // config.aquire();
 | 
			
		||||
        // std::string serString = config.conf["device"];
 | 
			
		||||
        // config.release();
 | 
			
		||||
 | 
			
		||||
        sigpath::sourceManager.registerSource("HackRF", &handler);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ~HackRFSourceModule() {
 | 
			
		||||
        hackrf_exit();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    void enable() {
 | 
			
		||||
        enabled = true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void disable() {
 | 
			
		||||
        enabled = false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bool isEnabled() {
 | 
			
		||||
        return enabled;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void refresh() {
 | 
			
		||||
        devList.clear();
 | 
			
		||||
        devListTxt = "";
 | 
			
		||||
 | 
			
		||||
        uint64_t serials[256];
 | 
			
		||||
        hackrf_device_list_t* _devList = hackrf_device_list();
 | 
			
		||||
 | 
			
		||||
        for (int i = 0; i < _devList->devicecount; i++) {
 | 
			
		||||
            devList.push_back(_devList->serial_numbers[i]);
 | 
			
		||||
            devListTxt += _devList->serial_numbers[i];
 | 
			
		||||
            devListTxt += '\0';
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        hackrf_device_list_free(_devList);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void selectFirst() {
 | 
			
		||||
        if (devList.size() != 0) {
 | 
			
		||||
            selectedSerial = devList[0];
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    static void menuSelected(void* ctx) {
 | 
			
		||||
        HackRFSourceModule* _this = (HackRFSourceModule*)ctx;
 | 
			
		||||
        core::setInputSampleRate(_this->sampleRate);
 | 
			
		||||
        spdlog::info("HackRFSourceModule '{0}': Menu Select!", _this->name);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    static void menuDeselected(void* ctx) {
 | 
			
		||||
        HackRFSourceModule* _this = (HackRFSourceModule*)ctx;
 | 
			
		||||
        spdlog::info("HackRFSourceModule '{0}': Menu Deselect!", _this->name);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #pragma optimize( "", off )
 | 
			
		||||
    
 | 
			
		||||
    static void start(void* ctx) {
 | 
			
		||||
        HackRFSourceModule* _this = (HackRFSourceModule*)ctx;
 | 
			
		||||
        if (_this->running) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        if (_this->selectedSerial == "") {
 | 
			
		||||
            spdlog::error("Tried to start HackRF source with empty serial");
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        int err = hackrf_open_by_serial(_this->selectedSerial.c_str(), &_this->openDev);
 | 
			
		||||
        if (err != 0) {
 | 
			
		||||
            spdlog::error("Could not open HackRF {0}", _this->selectedSerial);
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        hackrf_set_sample_rate(_this->openDev, _this->sampleRate);
 | 
			
		||||
        hackrf_set_baseband_filter_bandwidth(_this->openDev, hackrf_compute_baseband_filter_bw(_this->sampleRate));
 | 
			
		||||
        hackrf_set_freq(_this->openDev, _this->freq);
 | 
			
		||||
 | 
			
		||||
        hackrf_set_amp_enable(_this->openDev, _this->amp);
 | 
			
		||||
        hackrf_set_lna_gain(_this->openDev, _this->lna);
 | 
			
		||||
        hackrf_set_vga_gain(_this->openDev, _this->lna);
 | 
			
		||||
 | 
			
		||||
        hackrf_start_rx(_this->openDev, callback, _this);
 | 
			
		||||
 | 
			
		||||
        _this->running = true;
 | 
			
		||||
        spdlog::info("HackRFSourceModule '{0}': Start!", _this->name);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #pragma optimize( "", on )
 | 
			
		||||
    
 | 
			
		||||
    static void stop(void* ctx) {
 | 
			
		||||
        HackRFSourceModule* _this = (HackRFSourceModule*)ctx;
 | 
			
		||||
        if (!_this->running) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        _this->running = false;
 | 
			
		||||
        _this->stream.stopWriter();
 | 
			
		||||
        // TODO: Stream stop
 | 
			
		||||
        hackrf_close(_this->openDev);
 | 
			
		||||
        _this->stream.clearWriteStop();
 | 
			
		||||
        spdlog::info("HackRFSourceModule '{0}': Stop!", _this->name);
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    static void tune(double freq, void* ctx) {
 | 
			
		||||
        HackRFSourceModule* _this = (HackRFSourceModule*)ctx;
 | 
			
		||||
        if (_this->running) {
 | 
			
		||||
            hackrf_set_freq(_this->openDev, freq);
 | 
			
		||||
        }
 | 
			
		||||
        _this->freq = freq;
 | 
			
		||||
        spdlog::info("HackRFSourceModule '{0}': Tune: {1}!", _this->name, freq);
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    static void menuHandler(void* ctx) {
 | 
			
		||||
        HackRFSourceModule* _this = (HackRFSourceModule*)ctx;
 | 
			
		||||
        float menuWidth = ImGui::GetContentRegionAvailWidth();
 | 
			
		||||
 | 
			
		||||
        if (_this->running) { style::beginDisabled(); }
 | 
			
		||||
 | 
			
		||||
        ImGui::SetNextItemWidth(menuWidth);
 | 
			
		||||
        if (ImGui::Combo(CONCAT("##_hackrf_dev_sel_", _this->name), &_this->devId, _this->devListTxt.c_str())) {
 | 
			
		||||
            _this->selectedSerial = _this->devList[_this->devId];
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (ImGui::Combo(CONCAT("##_hackrf_sr_sel_", _this->name), &_this->srId, sampleRatesTxt)) {
 | 
			
		||||
            _this->sampleRate = sampleRates[_this->srId];
 | 
			
		||||
            core::setInputSampleRate(_this->sampleRate);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        ImGui::SameLine();
 | 
			
		||||
        float refreshBtnWdith = menuWidth - ImGui::GetCursorPosX();
 | 
			
		||||
        if (ImGui::Button(CONCAT("Refresh##_hackrf_refr_", _this->name), ImVec2(refreshBtnWdith, 0))) {
 | 
			
		||||
            _this->refresh();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (_this->running) { style::endDisabled(); }
 | 
			
		||||
 | 
			
		||||
        ImGui::Text("Amp Enabled");
 | 
			
		||||
        ImGui::SameLine();
 | 
			
		||||
        if (ImGui::Checkbox(CONCAT("##_hackrf_amp_", _this->name), &_this->amp)) {
 | 
			
		||||
            if (_this->running) {
 | 
			
		||||
                hackrf_set_amp_enable(_this->openDev, _this->amp);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        ImGui::Text("LNA Gain");
 | 
			
		||||
        ImGui::SameLine();
 | 
			
		||||
        if (ImGui::SliderInt(CONCAT("##_hackrf_lna_", _this->name), &_this->lna, 0, 40)) {
 | 
			
		||||
            _this->lna = (_this->lna / 8) * 8;
 | 
			
		||||
            if (_this->running) {
 | 
			
		||||
                hackrf_set_lna_gain(_this->openDev, _this->lna);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        ImGui::Text("LNA Gain");
 | 
			
		||||
        ImGui::SameLine();
 | 
			
		||||
        if (ImGui::SliderInt(CONCAT("##_hackrf_vga_", _this->name), &_this->vga, 0, 62)) {
 | 
			
		||||
            _this->vga = (_this->vga / 2) * 2;
 | 
			
		||||
            if (_this->running) {
 | 
			
		||||
                hackrf_set_vga_gain(_this->openDev, _this->lna);
 | 
			
		||||
            }
 | 
			
		||||
        }   
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    static int callback(hackrf_transfer* transfer) {
 | 
			
		||||
        HackRFSourceModule* _this = (HackRFSourceModule*)transfer->rx_ctx;
 | 
			
		||||
        int count = transfer->valid_length / 2;
 | 
			
		||||
        int8_t* buffer = (int8_t*)transfer->buffer;
 | 
			
		||||
        for (int i = 0; i < count; i++) {
 | 
			
		||||
            _this->stream.writeBuf[i].i = (float)buffer[i * 2] / 128.0f;
 | 
			
		||||
            _this->stream.writeBuf[i].q = (float)buffer[(i * 2) + 1] / 128.0f;
 | 
			
		||||
        }
 | 
			
		||||
        if (!_this->stream.swap(count)) { return -1; }
 | 
			
		||||
        return 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    std::string name;
 | 
			
		||||
    hackrf_device* openDev;
 | 
			
		||||
    bool enabled = true;
 | 
			
		||||
    dsp::stream<dsp::complex_t> stream;
 | 
			
		||||
    int sampleRate;
 | 
			
		||||
    SourceManager::SourceHandler handler;
 | 
			
		||||
    bool running = false;
 | 
			
		||||
    double freq;
 | 
			
		||||
    std::string selectedSerial = "";
 | 
			
		||||
    int devId = 0;
 | 
			
		||||
    int srId = 0;
 | 
			
		||||
    bool amp = false;
 | 
			
		||||
    int lna = 0;
 | 
			
		||||
    int vga = 0;
 | 
			
		||||
 | 
			
		||||
    std::vector<std::string> devList;
 | 
			
		||||
    std::string devListTxt;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
MOD_EXPORT void _INIT_() {
 | 
			
		||||
//    config.setPath(ROOT_DIR "/airspyhf_config.json");
 | 
			
		||||
//    json defConf;
 | 
			
		||||
//    defConf["device"] = "";
 | 
			
		||||
//    defConf["devices"] = json::object();
 | 
			
		||||
//    config.load(defConf);
 | 
			
		||||
//    config.enableAutoSave();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
MOD_EXPORT ModuleManager::Instance* _CREATE_INSTANCE_(std::string name) {
 | 
			
		||||
    return new HackRFSourceModule(name);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
MOD_EXPORT void _DELETE_INSTANCE_(ModuleManager::Instance* instance) {
 | 
			
		||||
    delete (HackRFSourceModule*)instance;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
MOD_EXPORT void _END_() {
 | 
			
		||||
    // config.disableAutoSave();
 | 
			
		||||
    // config.save();
 | 
			
		||||
}
 | 
			
		||||
@@ -35,7 +35,7 @@ Download the latest release from [the Releases page](https://github.com/Alexandr
 | 
			
		||||
 | 
			
		||||
Then, run:
 | 
			
		||||
```sh
 | 
			
		||||
sudo apt install libfftw3-dev libglfw3-dev libglew-dev libvolk2-dev libsoapysdr-dev libairspyhf-dev libiio-dev libad9361-dev portaudio19-dev
 | 
			
		||||
sudo apt install libfftw3-dev libglfw3-dev libglew-dev libvolk2-dev libsoapysdr-dev libairspyhf-dev libiio-dev libad9361-dev portaudio19-dev libhackrf-dev
 | 
			
		||||
sudo dpkg -i sdrpp_debian_amd64.deb
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user