kopia lustrzana https://github.com/f4exb/sdrangel
HackRF input plugin: use actual sample rate and bandwidth values not indexes
rodzic
a02b78d24d
commit
1a8d96d874
|
@ -2,11 +2,13 @@ project(hackrfdevice)
|
|||
|
||||
set(hackrfdevice_SOURCES
|
||||
devicehackrf.cpp
|
||||
devicehackrfvalues.cpp
|
||||
)
|
||||
|
||||
set(hackrfdevice_HEADERS
|
||||
devicehackrf.h
|
||||
devicehackrfparam.h
|
||||
devicehackrfvalues.h
|
||||
)
|
||||
|
||||
if (BUILD_DEBIAN)
|
||||
|
|
|
@ -21,9 +21,9 @@ hackrf_device *DeviceHackRF::open_hackrf(int sequence)
|
|||
{
|
||||
hackrf_error rc;
|
||||
|
||||
// TODO: this may not work if several HackRF Devices are running concurrently. It should be handled globally in the application
|
||||
rc = (hackrf_error) hackrf_init();
|
||||
|
||||
// TODO: this may not work if several HackRF Devices are running concurrently. It should be handled globally in the application
|
||||
if (rc != HACKRF_SUCCESS)
|
||||
{
|
||||
fprintf(stderr, "DeviceHackRF::open_hackrf: failed to initiate HackRF library %s\n", hackrf_error_name(rc));
|
||||
|
|
|
@ -0,0 +1,73 @@
|
|||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2017 Edouard Griffiths, F4EXB //
|
||||
// //
|
||||
// This program 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 as version 3 of the License, or //
|
||||
// //
|
||||
// This program 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 V3 for more details. //
|
||||
// //
|
||||
// You should have received a copy of the GNU General Public License //
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "devicehackrfvalues.h"
|
||||
|
||||
unsigned int HackRFSampleRates::m_rates_k[] = {2400, 3200, 4800, 5600, 6400, 8000, 9600, 12800, 19200};
|
||||
|
||||
unsigned int HackRFSampleRates::getRate(unsigned int rate_index)
|
||||
{
|
||||
if (rate_index < m_nb_rates)
|
||||
{
|
||||
return m_rates_k[rate_index];
|
||||
}
|
||||
else
|
||||
{
|
||||
return m_rates_k[0];
|
||||
}
|
||||
}
|
||||
|
||||
unsigned int HackRFSampleRates::getRateIndex(unsigned int rate)
|
||||
{
|
||||
for (unsigned int i=0; i < m_nb_rates; i++)
|
||||
{
|
||||
if (rate == m_rates_k[i])
|
||||
{
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
unsigned int HackRFBandwidths::m_bw_k[] = {1750, 2500, 3500, 5000, 5500, 6000, 7000, 8000, 9000, 10000, 12000, 14000, 15000, 20000, 24000, 28000};
|
||||
|
||||
unsigned int HackRFBandwidths::getBandwidth(unsigned int bandwidth_index)
|
||||
{
|
||||
if (bandwidth_index < m_nb_bw)
|
||||
{
|
||||
return m_bw_k[bandwidth_index];
|
||||
}
|
||||
else
|
||||
{
|
||||
return m_bw_k[0];
|
||||
}
|
||||
}
|
||||
|
||||
unsigned int HackRFBandwidths::getBandwidthIndex(unsigned int bandwidth)
|
||||
{
|
||||
for (unsigned int i=0; i < m_nb_bw; i++)
|
||||
{
|
||||
if (bandwidth == m_bw_k[i])
|
||||
{
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2017 Edouard Griffiths, F4EXB //
|
||||
// //
|
||||
// This program 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 as version 3 of the License, or //
|
||||
// //
|
||||
// This program 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 V3 for more details. //
|
||||
// //
|
||||
// You should have received a copy of the GNU General Public License //
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef DEVICES_HACKRF_DEVICEHACKRFVALUES_H_
|
||||
#define DEVICES_HACKRF_DEVICEHACKRFVALUES_H_
|
||||
|
||||
class HackRFSampleRates {
|
||||
public:
|
||||
static unsigned int getRate(unsigned int rate_index);
|
||||
static unsigned int getRateIndex(unsigned int rate);
|
||||
static const unsigned int m_nb_rates = 9;
|
||||
static unsigned int m_rates_k[m_nb_rates];
|
||||
};
|
||||
|
||||
class HackRFBandwidths {
|
||||
public:
|
||||
static unsigned int getBandwidth(unsigned int bandwidth_index);
|
||||
static unsigned int getBandwidthIndex(unsigned int bandwidth);
|
||||
static const unsigned int m_nb_bw = 16;
|
||||
static unsigned int m_bw_k[m_nb_bw];
|
||||
};
|
||||
|
||||
#endif /* DEVICES_HACKRF_DEVICEHACKRFVALUES_H_ */
|
|
@ -25,7 +25,7 @@
|
|||
#include "dsp/dspengine.h"
|
||||
#include "device/devicesourceapi.h"
|
||||
#include "device/devicesinkapi.h"
|
||||
|
||||
#include "hackrf/devicehackrfvalues.h"
|
||||
|
||||
#include "hackrfinputgui.h"
|
||||
#include "hackrfinputthread.h"
|
||||
|
@ -208,8 +208,7 @@ const QString& HackRFInput::getDeviceDescription() const
|
|||
|
||||
int HackRFInput::getSampleRate() const
|
||||
{
|
||||
int rate = HackRFSampleRates::m_rates_k[m_settings.m_devSampleRateIndex] * 1000;
|
||||
return (rate / (1<<m_settings.m_log2Decim));
|
||||
return (m_settings.m_devSampleRate / (1<<m_settings.m_log2Decim));
|
||||
}
|
||||
|
||||
quint64 HackRFInput::getCenterFrequency() const
|
||||
|
@ -277,31 +276,23 @@ bool HackRFInput::applySettings(const HackRFInputSettings& settings, bool force)
|
|||
m_deviceAPI->configureCorrections(m_settings.m_dcBlock, m_settings.m_iqCorrection);
|
||||
}
|
||||
|
||||
if ((m_settings.m_devSampleRateIndex != settings.m_devSampleRateIndex) || force)
|
||||
if ((m_settings.m_devSampleRate != settings.m_devSampleRate) || force)
|
||||
{
|
||||
forwardChange = true;
|
||||
|
||||
if (settings.m_devSampleRateIndex < HackRFSampleRates::m_nb_rates)
|
||||
{
|
||||
m_settings.m_devSampleRateIndex = settings.m_devSampleRateIndex;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_settings.m_devSampleRateIndex = HackRFSampleRates::m_nb_rates - 1;
|
||||
}
|
||||
m_settings.m_devSampleRate = settings.m_devSampleRate;
|
||||
|
||||
if (m_dev != 0)
|
||||
{
|
||||
rc = (hackrf_error) hackrf_set_sample_rate_manual(m_dev, HackRFSampleRates::m_rates_k[m_settings.m_devSampleRateIndex]*1000, 1);
|
||||
rc = (hackrf_error) hackrf_set_sample_rate_manual(m_dev, m_settings.m_devSampleRate, 1);
|
||||
|
||||
if (rc != HACKRF_SUCCESS)
|
||||
{
|
||||
qCritical("HackRFInput::applySettings: could not set sample rate index %u (%d kS/s): %s", m_settings.m_devSampleRateIndex, HackRFSampleRates::m_rates_k[m_settings.m_devSampleRateIndex], hackrf_error_name(rc));
|
||||
qCritical("HackRFInput::applySettings: could not set sample rate TO %d kS/s: %s", m_settings.m_devSampleRate, hackrf_error_name(rc));
|
||||
}
|
||||
else
|
||||
{
|
||||
qDebug("HackRFInput::applySettings: sample rate set to index: %u (%d kS/s)", m_settings.m_devSampleRateIndex, HackRFSampleRates::m_rates_k[m_settings.m_devSampleRateIndex]);
|
||||
m_hackRFThread->setSamplerate(HackRFSampleRates::m_rates_k[m_settings.m_devSampleRateIndex]);
|
||||
qDebug("HackRFInput::applySettings: sample rate set to %d kS/s", m_settings.m_devSampleRate);
|
||||
m_hackRFThread->setSamplerate(m_settings.m_devSampleRate);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -320,7 +311,7 @@ bool HackRFInput::applySettings(const HackRFInputSettings& settings, bool force)
|
|||
|
||||
qint64 deviceCenterFrequency = m_settings.m_centerFrequency;
|
||||
qint64 f_img = deviceCenterFrequency;
|
||||
quint32 devSampleRate = HackRFSampleRates::m_rates_k[m_settings.m_devSampleRateIndex] * 1000;
|
||||
quint32 devSampleRate =m_settings.m_devSampleRate;
|
||||
|
||||
if (force || (m_settings.m_centerFrequency != settings.m_centerFrequency) ||
|
||||
(m_settings.m_LOppmTenths != settings.m_LOppmTenths) ||
|
||||
|
@ -411,21 +402,11 @@ bool HackRFInput::applySettings(const HackRFInputSettings& settings, bool force)
|
|||
}
|
||||
}
|
||||
|
||||
if ((m_settings.m_bandwidthIndex != settings.m_bandwidthIndex) || force)
|
||||
if ((m_settings.m_bandwidth != settings.m_bandwidth) || force)
|
||||
{
|
||||
|
||||
if (settings.m_bandwidthIndex < HackRFBandwidths::m_nb_bw)
|
||||
{
|
||||
m_settings.m_bandwidthIndex = settings.m_bandwidthIndex;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_settings.m_bandwidthIndex = HackRFBandwidths::m_nb_bw - 1;
|
||||
}
|
||||
|
||||
if (m_dev != 0)
|
||||
{
|
||||
uint32_t bw_index = hackrf_compute_baseband_filter_bw_round_down_lt(HackRFBandwidths::m_bw_k[m_settings.m_bandwidthIndex]*1000);
|
||||
uint32_t bw_index = hackrf_compute_baseband_filter_bw_round_down_lt(m_settings.m_bandwidth);
|
||||
rc = (hackrf_error) hackrf_set_baseband_filter_bandwidth(m_dev, bw_index);
|
||||
|
||||
if (rc != HACKRF_SUCCESS)
|
||||
|
@ -434,7 +415,7 @@ bool HackRFInput::applySettings(const HackRFInputSettings& settings, bool force)
|
|||
}
|
||||
else
|
||||
{
|
||||
qDebug() << "HackRFInput:applySettings: Baseband BW filter set to " << HackRFBandwidths::m_bw_k[m_settings.m_bandwidthIndex] << " kHz";
|
||||
qDebug() << "HackRFInput:applySettings: Baseband BW filter set to " << m_settings.m_bandwidth << " Hz";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -484,6 +465,11 @@ bool HackRFInput::applySettings(const HackRFInputSettings& settings, bool force)
|
|||
m_deviceAPI->getDeviceInputMessageQueue()->push(notif);
|
||||
}
|
||||
|
||||
qDebug() << "HackRFInput::applySettings: center freq: " << m_settings.m_centerFrequency << " Hz"
|
||||
<< " device center freq: " << deviceCenterFrequency << " Hz"
|
||||
<< " device sample rate: " << m_settings.m_devSampleRate << "Hz"
|
||||
<< " Actual sample rate: " << m_settings.m_devSampleRate/(1<<m_settings.m_log2Decim) << "Hz";
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
#include "dsp/dspcommands.h"
|
||||
#include <device/devicesourceapi.h>
|
||||
#include <dsp/filerecord.h>
|
||||
#include "hackrf/devicehackrfvalues.h"
|
||||
|
||||
#include "ui_hackrfinputgui.h"
|
||||
|
||||
|
@ -112,11 +113,14 @@ QByteArray HackRFInputGui::serialize() const
|
|||
|
||||
bool HackRFInputGui::deserialize(const QByteArray& data)
|
||||
{
|
||||
if(m_settings.deserialize(data)) {
|
||||
if(m_settings.deserialize(data))
|
||||
{
|
||||
displaySettings();
|
||||
sendSettings();
|
||||
return true;
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
resetToDefaults();
|
||||
return false;
|
||||
}
|
||||
|
@ -124,7 +128,15 @@ bool HackRFInputGui::deserialize(const QByteArray& data)
|
|||
|
||||
bool HackRFInputGui::handleMessage(const Message& message)
|
||||
{
|
||||
return false;
|
||||
if (HackRFInput::MsgReportHackRF::match(message))
|
||||
{
|
||||
displaySettings();
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void HackRFInputGui::handleDSPMessages()
|
||||
|
@ -166,7 +178,8 @@ void HackRFInputGui::displaySettings()
|
|||
ui->dcOffset->setChecked(m_settings.m_dcBlock);
|
||||
ui->iqImbalance->setChecked(m_settings.m_iqCorrection);
|
||||
|
||||
ui->sampleRate->setCurrentIndex(m_settings.m_devSampleRateIndex);
|
||||
unsigned int sampleRateIndex = HackRFSampleRates::getRateIndex(m_settings.m_devSampleRate/1000);
|
||||
ui->sampleRate->setCurrentIndex(sampleRateIndex);
|
||||
|
||||
ui->biasT->setChecked(m_settings.m_biasT);
|
||||
|
||||
|
@ -178,7 +191,8 @@ void HackRFInputGui::displaySettings()
|
|||
ui->lnaGainText->setText(tr("%1dB").arg(m_settings.m_lnaGain));
|
||||
ui->lna->setValue(m_settings.m_lnaGain);
|
||||
|
||||
ui->bbFilter->setCurrentIndex(m_settings.m_bandwidthIndex);
|
||||
unsigned int bandwidthIndex = HackRFBandwidths::getBandwidthIndex(m_settings.m_bandwidth/1000);
|
||||
ui->bbFilter->setCurrentIndex(bandwidthIndex);
|
||||
|
||||
ui->vgaText->setText(tr("%1dB").arg(m_settings.m_vgaGain));
|
||||
ui->vga->setValue(m_settings.m_vgaGain);
|
||||
|
@ -186,7 +200,7 @@ void HackRFInputGui::displaySettings()
|
|||
|
||||
void HackRFInputGui::displaySampleRates()
|
||||
{
|
||||
int savedIndex = m_settings.m_devSampleRateIndex;
|
||||
int savedIndex = HackRFSampleRates::getRateIndex(m_settings.m_devSampleRate/1000);
|
||||
ui->sampleRate->blockSignals(true);
|
||||
ui->sampleRate->clear();
|
||||
|
||||
|
@ -209,7 +223,7 @@ void HackRFInputGui::displaySampleRates()
|
|||
|
||||
void HackRFInputGui::displayBandwidths()
|
||||
{
|
||||
int savedIndex = m_settings.m_bandwidthIndex;
|
||||
int savedIndex = HackRFBandwidths::getBandwidthIndex(m_settings.m_bandwidth/1000);
|
||||
ui->bbFilter->blockSignals(true);
|
||||
ui->bbFilter->clear();
|
||||
|
||||
|
@ -263,13 +277,15 @@ void HackRFInputGui::on_iqImbalance_toggled(bool checked)
|
|||
|
||||
void HackRFInputGui::on_sampleRate_currentIndexChanged(int index)
|
||||
{
|
||||
m_settings.m_devSampleRateIndex = index;
|
||||
int newrate = HackRFSampleRates::getRate(index);
|
||||
m_settings.m_devSampleRate = newrate * 1000;
|
||||
sendSettings();
|
||||
}
|
||||
|
||||
void HackRFInputGui::on_bbFilter_currentIndexChanged(int index)
|
||||
{
|
||||
m_settings.m_bandwidthIndex = index;
|
||||
int newBandwidth = HackRFBandwidths::getBandwidth(index);
|
||||
m_settings.m_bandwidth = newBandwidth * 1000;
|
||||
sendSettings();
|
||||
}
|
||||
|
||||
|
@ -361,7 +377,7 @@ void HackRFInputGui::on_record_toggled(bool checked)
|
|||
void HackRFInputGui::updateHardware()
|
||||
{
|
||||
qDebug() << "HackRFGui::updateHardware";
|
||||
HackRFInput::MsgConfigureHackRF* message = HackRFInput::MsgConfigureHackRF::create( m_settings);
|
||||
HackRFInput::MsgConfigureHackRF* message = HackRFInput::MsgConfigureHackRF::create(m_settings);
|
||||
m_sampleSource->getInputMessageQueue()->push(message);
|
||||
m_updateTimer.stop();
|
||||
}
|
||||
|
@ -394,57 +410,3 @@ void HackRFInputGui::updateStatus()
|
|||
m_lastEngineState = state;
|
||||
}
|
||||
}
|
||||
|
||||
unsigned int HackRFSampleRates::m_rates_k[] = {2400, 3200, 4800, 5600, 6400, 8000, 9600, 12800, 19200};
|
||||
|
||||
unsigned int HackRFSampleRates::getRate(unsigned int rate_index)
|
||||
{
|
||||
if (rate_index < m_nb_rates)
|
||||
{
|
||||
return m_rates_k[rate_index];
|
||||
}
|
||||
else
|
||||
{
|
||||
return m_rates_k[0];
|
||||
}
|
||||
}
|
||||
|
||||
unsigned int HackRFSampleRates::getRateIndex(unsigned int rate)
|
||||
{
|
||||
for (unsigned int i=0; i < m_nb_rates; i++)
|
||||
{
|
||||
if (rate/1000 == m_rates_k[i])
|
||||
{
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
unsigned int HackRFBandwidths::m_bw_k[] = {1750, 2500, 3500, 5000, 5500, 6000, 7000, 8000, 9000, 10000, 12000, 14000, 15000, 20000, 24000, 28000};
|
||||
|
||||
unsigned int HackRFBandwidths::getBandwidth(unsigned int bandwidth_index)
|
||||
{
|
||||
if (bandwidth_index < m_nb_bw)
|
||||
{
|
||||
return m_bw_k[bandwidth_index];
|
||||
}
|
||||
else
|
||||
{
|
||||
return m_bw_k[0];
|
||||
}
|
||||
}
|
||||
|
||||
unsigned int HackRFBandwidths::getBandwidthIndex(unsigned int bandwidth)
|
||||
{
|
||||
for (unsigned int i=0; i < m_nb_bw; i++)
|
||||
{
|
||||
if (bandwidth == m_bw_k[i])
|
||||
{
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -96,20 +96,4 @@ private slots:
|
|||
void updateStatus();
|
||||
};
|
||||
|
||||
class HackRFSampleRates {
|
||||
public:
|
||||
static unsigned int getRate(unsigned int rate_index);
|
||||
static unsigned int getRateIndex(unsigned int rate);
|
||||
static const unsigned int m_nb_rates = 9;
|
||||
static unsigned int m_rates_k[m_nb_rates];
|
||||
};
|
||||
|
||||
class HackRFBandwidths {
|
||||
public:
|
||||
static unsigned int getBandwidth(unsigned int bandwidth_index);
|
||||
static unsigned int getBandwidthIndex(unsigned int bandwidth);
|
||||
static const unsigned int m_nb_bw = 16;
|
||||
static unsigned int m_bw_k[m_nb_bw];
|
||||
};
|
||||
|
||||
#endif // INCLUDE_HACKRFINPUTGUI_H
|
||||
|
|
|
@ -29,13 +29,13 @@ void HackRFInputSettings::resetToDefaults()
|
|||
{
|
||||
m_centerFrequency = 435000 * 1000;
|
||||
m_LOppmTenths = 0;
|
||||
m_devSampleRateIndex = 0;
|
||||
m_devSampleRate = 2400000;
|
||||
m_biasT = false;
|
||||
m_log2Decim = 0;
|
||||
m_fcPos = FC_POS_CENTER;
|
||||
m_lnaExt = false;
|
||||
m_lnaGain = 16;
|
||||
m_bandwidthIndex = 0;
|
||||
m_bandwidth = 1750000;
|
||||
m_vgaGain = 16;
|
||||
m_dcBlock = false;
|
||||
m_iqCorrection = false;
|
||||
|
@ -46,13 +46,13 @@ QByteArray HackRFInputSettings::serialize() const
|
|||
SimpleSerializer s(1);
|
||||
|
||||
s.writeS32(1, m_LOppmTenths);
|
||||
s.writeU32(2, m_devSampleRateIndex);
|
||||
s.writeU32(2, m_devSampleRate);
|
||||
s.writeBool(3, m_biasT);
|
||||
s.writeU32(4, m_log2Decim);
|
||||
s.writeS32(5, m_fcPos);
|
||||
s.writeBool(6, m_lnaExt);
|
||||
s.writeU32(7, m_lnaGain);
|
||||
s.writeU32(8, m_bandwidthIndex);
|
||||
s.writeU32(8, m_bandwidth);
|
||||
s.writeU32(9, m_vgaGain);
|
||||
s.writeBool(10, m_dcBlock);
|
||||
s.writeBool(11, m_iqCorrection);
|
||||
|
@ -75,14 +75,14 @@ bool HackRFInputSettings::deserialize(const QByteArray& data)
|
|||
int intval;
|
||||
|
||||
d.readS32(1, &m_LOppmTenths, 0);
|
||||
d.readU32(2, &m_devSampleRateIndex, 0);
|
||||
d.readU32(2, &m_devSampleRate, 2400000);
|
||||
d.readBool(3, &m_biasT, false);
|
||||
d.readU32(4, &m_log2Decim, 0);
|
||||
d.readS32(5, &intval, 0);
|
||||
m_fcPos = (fcPos_t) intval;
|
||||
d.readBool(6, &m_lnaExt, false);
|
||||
d.readU32(7, &m_lnaGain, 16);
|
||||
d.readU32(8, &m_bandwidthIndex, 0);
|
||||
d.readU32(8, &m_bandwidth, 1750000);
|
||||
d.readU32(9, &m_vgaGain, 16);
|
||||
d.readBool(10, &m_dcBlock, false);
|
||||
d.readBool(11, &m_iqCorrection, false);
|
||||
|
|
|
@ -28,8 +28,8 @@ struct HackRFInputSettings {
|
|||
|
||||
quint64 m_centerFrequency;
|
||||
qint32 m_LOppmTenths;
|
||||
quint32 m_devSampleRateIndex;
|
||||
quint32 m_bandwidthIndex;
|
||||
quint32 m_devSampleRate;
|
||||
quint32 m_bandwidth;
|
||||
quint32 m_lnaGain;
|
||||
quint32 m_vgaGain;
|
||||
quint32 m_log2Decim;
|
||||
|
|
Ładowanie…
Reference in New Issue