diff --git a/package.json b/package.json index b6d78f0..7b550ea 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "sdrangelcli", - "version": "2.4.0", + "version": "2.4.1", "repository": { "type": "git", "url": "git://github.com/f4exb/sdrangelcli.git" diff --git a/src/app/app.component.ts b/src/app/app.component.ts index cf1d468..722a876 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -16,7 +16,7 @@ import { RemoveFeaturesetDialogComponent } from './main/remove-featureset-dialog export class AppComponent { title = 'SDRangelCli'; - version = '2.4.0'; + version = '2.4.1'; sdrangelURL = 'http://127.0.0.1:8091/sdrangel'; // the default URL constructor(private sdrangelUrlService: SdrangelUrlService, diff --git a/src/app/channel-details/remote-source/remote-source.component.html b/src/app/channel-details/remote-source/remote-source.component.html index 4fefff3..997d4d7 100644 --- a/src/app/channel-details/remote-source/remote-source.component.html +++ b/src/app/channel-details/remote-source/remote-source.component.html @@ -21,11 +21,8 @@ - - + + +
- Freq {{report.centerFreq}} kHz - - SR {{report.sampleRate}} S/s + + {{ getReportDateTime() }} FEC {{report.nbOriginalBlocks+report.nbFECBlocks}}/{{report.nbFECBlocks}} @@ -41,18 +38,63 @@
- Pull SR {{getStreamSampleRate().toFixed(0)}} S/s +   + Pull SR {{ getStreamSampleRate().toFixed(0) }} S/s - Corr {{deltaCorrectableCount}}/{{report.correctableErrorsCount}} + Corr {{ deltaCorrectableCount }} / {{ report.correctableErrorsCount }} - Uncorr {{deltaUncorrectableCount}}/{{report.uncorrectableErrorsCount}} + Uncorr {{ deltaUncorrectableCount }} / {{ report.uncorrectableErrorsCount }}
+ + + + + + + + + + +
+ Fc + + {{channelCenterFrequencyKhz}} kHz + + + Δf {{calculateFrequencyOffset()}} kHz +
+ ÷ + + + {{log2Interp.viewValue}} + + + + {{getChannelBaseband()}} kHz + + + Filter hash + + + {{getFilterChainString()}} + +
diff --git a/src/app/channel-details/remote-source/remote-source.component.ts b/src/app/channel-details/remote-source/remote-source.component.ts index dc2328a..b4cc430 100644 --- a/src/app/channel-details/remote-source/remote-source.component.ts +++ b/src/app/channel-details/remote-source/remote-source.component.ts @@ -9,6 +9,10 @@ import { Subscription, interval } from 'rxjs'; import { Utils } from '../../common-components/utils'; import { ChannelSettings } from '../channel-details'; +interface Log2 { + value: number; + viewValue: number; +} @Component({ selector: 'app-remote-source', templateUrl: './remote-source.component.html', @@ -38,6 +42,17 @@ export class RemoteSourceComponent implements OnInit { deltaUncorrectableCount: number; deltaSampleCount: number; useReverseAPI: boolean; + log2Interps: Log2[] = [ + {value: 0, viewValue: 1}, + {value: 1, viewValue: 2}, + {value: 2, viewValue: 4}, + {value: 3, viewValue: 8}, + {value: 4, viewValue: 16}, + {value: 5, viewValue: 32}, + {value: 6, viewValue: 64}, + ]; + channelDeltaFrequency: number; + channelCenterFrequencyKhz: number; constructor(private route: ActivatedRoute, private channeldetailsService: ChannelDetailsService, @@ -100,6 +115,8 @@ export class RemoteSourceComponent implements OnInit { this.statusMessage = 'OK'; this.statusError = false; this.settings = channelSettings.RemoteSourceSettings; + this.channelDeltaFrequency = this.calculateFrequencyOffset(); + this.channelCenterFrequencyKhz = (this.deviceCenterFrequency + this.channelDeltaFrequency) / 1000; this.rgbTitle = Utils.intToRGB(this.settings.rgbColor); this.rgbTitleStr = Utils.getRGBStr(this.rgbTitle); this.useReverseAPI = this.settings.useReverseAPI !== 0; @@ -136,7 +153,7 @@ export class RemoteSourceComponent implements OnInit { _ => { this.channeldetailsService.getReport(this.sdrangelURL, this.deviceIndex, this.channelIndex).subscribe( channelReport => { - if (channelReport.channelType === 'DaemonSource') { + if (channelReport.channelType === 'RemoteSource') { this.report = channelReport.RemoteSourceReport; const timestampUs = this.report.tvSec * 1000000 + this.report.tvUSec; if (this.lastTimestampUs === 0) { @@ -178,6 +195,11 @@ export class RemoteSourceComponent implements OnInit { this.enableReporting(this.monitor); } + getReportDateTime(): string { + const dateObj = new Date((this.report.tvSec * 1000000 + this.report.tvUSec) / 1000); + return dateObj.toISOString(); + } + onTitleColorChanged(colorStr: string) { this.rgbTitleStr = colorStr; this.setTitleColor(); @@ -200,6 +222,18 @@ export class RemoteSourceComponent implements OnInit { this.setDeviceSettings(newSettings); } + setInterp() { + const newSettings: RemoteSourceSettings = {}; + newSettings.log2Interp = this.settings.log2Interp; + this.setDeviceSettings(newSettings); + } + + setFilterChainHash() { + const newSettings: RemoteSourceSettings = {}; + newSettings.filterChainHash = this.settings.filterChainHash; + this.setDeviceSettings(newSettings); + } + setDataAddress() { const newSettings: RemoteSourceSettings = {}; newSettings.dataAddress = this.settings.dataAddress; @@ -256,13 +290,13 @@ export class RemoteSourceComponent implements OnInit { getStreamStatusColor(): string { if (this.deltaSampleCount === 0) { - return 'blue'; + return 'rgb(0, 0, 200, 1.0)'; } else if (this.deltaUncorrectableCount !== 0) { - return 'red'; + return 'rgb(200, 0, 0, 1.0)'; } else if (this.deltaCorrectableCount !== 0) { - return 'grey'; + return 'rgb(160, 160, 160, 1.0)'; } else { - return 'green'; + return 'rgb(0, 200, 0, 1.0)'; } } @@ -277,4 +311,21 @@ export class RemoteSourceComponent implements OnInit { return 'Streaming OK'; } } + + getChannelBaseband(): number { + return (this.deviceBasebandRate / 1000) / (1 << this.settings.log2Interp); + } + + getMaxFilterChainHash(): number { + return 3 ** this.settings.log2Interp; + } + + getFilterChainString(): string { + return Utils.convertHBFilterChainToString(this.settings.log2Interp, this.settings.filterChainHash); + } + + calculateFrequencyOffset(): number { + return this.deviceBasebandRate * Utils.getHBFilterChainShiftFactor(this.settings.log2Interp, this.settings.filterChainHash); + } + } diff --git a/src/app/channel-details/remote-source/remote-source.ts b/src/app/channel-details/remote-source/remote-source.ts index a46afbe..c3301fc 100644 --- a/src/app/channel-details/remote-source/remote-source.ts +++ b/src/app/channel-details/remote-source/remote-source.ts @@ -1,30 +1,36 @@ export interface RemoteSourceSettings { dataAddress: string; dataPort: number; - rgbColor: number; - title: string; - useReverseAPI?: number; // bool + filterChainHash: number; + log2Interp: number; reverseAPIAddress?: string; reverseAPIPort?: number; reverseAPIDeviceIndex?: number; reverseAPIChannelIndex?: number; + rgbColor: number; + title: string; + useReverseAPI?: number; // bool } export const REMOTE_SOURCE_SETTINGS_DEFAULT = { dataAddress: '127.0.0.1', dataPort: 9090, - rgbColor: -7601148, - title: 'Remote channel source', - useReverseAPI: 0, + filterChainHash: 0, + log2Interp: 0, reverseAPIAddress: '127.0.0.1', reverseAPIPort: 8888, reverseAPIDeviceIndex: 0, - reverseAPIChannelIndex: 0 + reverseAPIChannelIndex: 0, + rgbColor: -7601148, + title: 'Remote channel source', + useReverseAPI: 0 }; export interface RemoteSourceReport { centerFreq: number; correctableErrorsCount: number; + deviceCenterFreq: number; + deviceSampleRate: number; nbFECBlocks: number; nbOriginalBlocks: number; queueLength: number; @@ -39,11 +45,13 @@ export interface RemoteSourceReport { export const REMOTE_SOURCE_REPORT_DEFAULT = { centerFreq: 434900, correctableErrorsCount: 0, + deviceCenterFreq: 435000000, + deviceSampleRate: 48000, nbFECBlocks: 8, nbOriginalBlocks: 128, queueLength: 18, queueSize: 32, - sampleRate: 75000, + sampleRate: 48000, samplesCount: 101481494, tvSec: 1535913707, tvUSec: 667575, diff --git a/src/app/device-details/device-details.module.ts b/src/app/device-details/device-details.module.ts index ca5d25c..6b2de0c 100644 --- a/src/app/device-details/device-details.module.ts +++ b/src/app/device-details/device-details.module.ts @@ -34,6 +34,7 @@ import { RemoteInputComponent } from './remote-input/remote-input.component'; import { KiwisdrComponent } from './kiwisdr/kiwisdr.component'; import { RemoteOutputComponent } from './remote-output/remote-output.component'; import { Sdrplayv3Component } from './sdrplayv3/sdrplayv3.component'; +import { MatProgressBarModule } from '@angular/material/progress-bar'; @NgModule({ imports: [ @@ -44,6 +45,7 @@ import { Sdrplayv3Component } from './sdrplayv3/sdrplayv3.component'; MatOptionModule, MatCheckboxModule, MatTooltipModule, + MatProgressBarModule, CommonComponentsModule, DeviceDetailsRoutingModule ], diff --git a/src/app/device-details/remote-output/remote-output.component.css b/src/app/device-details/remote-output/remote-output.component.css index 7959cde..239832e 100644 --- a/src/app/device-details/remote-output/remote-output.component.css +++ b/src/app/device-details/remote-output/remote-output.component.css @@ -109,4 +109,22 @@ td.yellow { ::ng-deep .mat-checkbox-label { line-height: 16px!important; -} \ No newline at end of file +} + +.fill-bar { + height: 10px; + width: 40ch; + margin:0 auto; +} + +::ng-deep .mat-progress-bar-fill::after { + background-color: rgb(33, 124, 30); +} + +::ng-deep .mat-progress-bar-buffer { + background: rgb(205, 205, 205); +} + +::ng-deep .mat-progress-bar { + border-radius: 5px; +} diff --git a/src/app/device-details/remote-output/remote-output.component.html b/src/app/device-details/remote-output/remote-output.component.html index 4d2af07..79a14ec 100644 --- a/src/app/device-details/remote-output/remote-output.component.html +++ b/src/app/device-details/remote-output/remote-output.component.html @@ -9,47 +9,51 @@ (click)="toggleMonitor()">   - Remote output {{getSampleRate()/1000}} kS/s + Remote output {{report.sampleRate/1000}} kS/s - - +
+ + + + + + + + + + - + + + + +
- - + {{ getReportDateTime() }} +
+ + + {{report.queueLength}}/{{report.queueSize}} ({{((report.queueLength*100)/report.queueSize).toFixed(0)}}%) +
+ +   + Pull SR {{ getStreamSampleRate().toFixed(0) }} S/s + + Corr {{ deltaCorrectableCount }} / {{ report.correctableErrorsCount }} + + Uncorr {{ deltaUncorrectableCount }} / {{ report.uncorrectableErrorsCount }}
- SR - - S/s + Freq {{ report.centerFrequency.toLocaleString() }} Hz - UDP delay - - % + SR {{ report.sampleRate.toLocaleString() }} S/s
+ + + + +
@@ -88,6 +92,19 @@ min=1024 max=65535>
+ FEC + +   + {{ 128+settings.nbFECBlocks }} / {{ settings.nbFECBlocks }} +
@@ -127,25 +144,8 @@
- - - - - - - - - -
- Samples - {{report ? report.sampleCount : 0}} - - R/W bal - {{report ? report.bufferRWBalance : 0}} - % -
{{ statusMessage }} - \ No newline at end of file + diff --git a/src/app/device-details/remote-output/remote-output.component.ts b/src/app/device-details/remote-output/remote-output.component.ts index be32202..4098a55 100644 --- a/src/app/device-details/remote-output/remote-output.component.ts +++ b/src/app/device-details/remote-output/remote-output.component.ts @@ -21,16 +21,30 @@ export class RemoteOutputComponent implements OnInit { sdrangelURL: string; report: RemoteOutputReport = REMOTE_OUTPUT_REPORT_DEFAULT; settings: RemoteOutputSettings = REMOTE_OUTPUT_SETTINGS_DEFAULT; - centerFreqKhz: number; - txDelayPercent: number; useReverseAPI: boolean; deviceReportSubscription: Subscription; monitor: boolean; + lastTimestampUs: number; + deltaTimestampUs: number; + lastSampleCount: number; + deltaSampleCount: number; + lastCorrectableCount: number; + deltaCorrectableCount: number; + lastUncorrectableCount: number; + deltaUncorrectableCount: number; constructor(private route: ActivatedRoute, private devicedetailsService: DeviceDetailsService, private sdrangelUrlService: SdrangelUrlService, private deviceStoreService: DeviceStoreService) { + this.lastSampleCount = 0; + this.deltaSampleCount = 0; + this.lastTimestampUs = 0; + this.deltaTimestampUs = 0; + this.lastCorrectableCount = 0; + this.deltaCorrectableCount = 0; + this.lastUncorrectableCount = 0; + this.deltaUncorrectableCount = 0; } ngOnInit() { @@ -48,10 +62,7 @@ export class RemoteOutputComponent implements OnInit { this.statusMessage = 'OK'; this.statusError = false; this.settings = deviceSettings.remoteOutputSettings; - this.centerFreqKhz = this.settings.centerFrequency / 1000; - this.txDelayPercent = Math.round(this.settings.txDelay * 100); this.useReverseAPI = this.settings.useReverseAPI !== 0; - this.feedDeviceStore(); } else { this.statusMessage = 'Not a Remote output device'; this.statusError = true; @@ -60,14 +71,6 @@ export class RemoteOutputComponent implements OnInit { ); } - private feedDeviceStore() { - const deviceStorage = { - centerFrequency: this.settings.centerFrequency, - basebandRate: this.settings.sampleRate - }; - this.deviceStoreService.change(this.deviceIndex, deviceStorage); - } - enableReporting(enable: boolean) { if (enable) { this.deviceReportSubscription = interval(1000).subscribe( @@ -76,6 +79,23 @@ export class RemoteOutputComponent implements OnInit { devicelReport => { if ((devicelReport.deviceHwType === 'RemoteOutput') && (devicelReport.direction === 1)) { this.report = devicelReport.remoteOutputReport; + this.feedDeviceStore(); + const timestampUs = this.report.tvSec * 1000000 + this.report.tvUSec; + if (this.lastTimestampUs === 0) { + this.lastTimestampUs = timestampUs; + } + this.deltaTimestampUs = timestampUs - this.lastTimestampUs; + this.lastTimestampUs = timestampUs; + if (this.report.sampleCount < this.lastSampleCount) { + this.deltaSampleCount = (0xFFFFFFFF - this.lastSampleCount) + this.report.sampleCount + 1; + } else { + this.deltaSampleCount = this.report.sampleCount - this.lastSampleCount; + } + this.lastSampleCount = this.report.sampleCount; + this.deltaCorrectableCount = this.report.correctableErrorsCount - this.lastCorrectableCount; + this.deltaUncorrectableCount = this.report.uncorrectableErrorsCount - this.lastUncorrectableCount; + this.lastCorrectableCount = this.report.correctableErrorsCount; + this.lastUncorrectableCount = this.report.uncorrectableErrorsCount; } } ); @@ -92,6 +112,14 @@ export class RemoteOutputComponent implements OnInit { this.enableReporting(this.monitor); } + private feedDeviceStore() { + const deviceStorage = { + centerFrequency: this.report.centerFrequency, + basebandRate: this.report.sampleRate + }; + this.deviceStoreService.change(this.deviceIndex, deviceStorage); + } + private setDeviceSettings(remoteOutputSettings: RemoteOutputSettings) { const settings: DeviceSettings = {}; settings.deviceHwType = 'RemoteOutput'; @@ -111,33 +139,6 @@ export class RemoteOutputComponent implements OnInit { ); } - getSampleRate(): number { - return this.settings.sampleRate; - } - - setSampleRate() { - const newSettings: RemoteOutputSettings = {}; - newSettings.sampleRate = this.settings.sampleRate; - this.setDeviceSettings(newSettings); - } - - onFrequencyUpdate(frequency: number) { - this.centerFreqKhz = frequency; - this.setCenterFrequency(); - } - - setCenterFrequency() { - const newSettings: RemoteOutputSettings = {}; - newSettings.centerFrequency = this.centerFreqKhz * 1000; - this.setDeviceSettings(newSettings); - } - - setTxDelay() { - const newSettings: RemoteOutputSettings = {}; - newSettings.txDelay = this.txDelayPercent / 100; - this.setDeviceSettings(newSettings); - } - setRemoteAPIAddress() { const newSettings: RemoteOutputSettings = {}; newSettings.apiAddress = this.settings.apiAddress; @@ -162,6 +163,12 @@ export class RemoteOutputComponent implements OnInit { this.setDeviceSettings(newSettings); } + setNbFECBlocks() { + const newSettings: RemoteOutputSettings = {}; + newSettings.nbFECBlocks = this.settings.nbFECBlocks; + this.setDeviceSettings(newSettings); + } + setUseReverseAPI() { const newSettings: RemoteOutputSettings = {}; newSettings.useReverseAPI = this.useReverseAPI ? 1 : 0; @@ -185,4 +192,45 @@ export class RemoteOutputComponent implements OnInit { newSettings.reverseAPIDeviceIndex = this.settings.reverseAPIDeviceIndex; this.setDeviceSettings(newSettings); } + + getReportDateTime(): string { + const dateObj = new Date((this.report.tvSec * 1000000 + this.report.tvUSec) / 1000); + return dateObj.toISOString(); + } + + getQueuePercentage(): number { + return (this.report.queueLength * 100) / this.report.queueSize; + } + + getStreamSampleRate(): number { + if (this.deltaTimestampUs === 0) { + return 0; + } else { + return (this.deltaSampleCount * 1e6) / this.deltaTimestampUs; + } + } + + getStreamStatusColor(): string { + if (this.deltaSampleCount === 0) { + return 'rgb(0, 0, 200, 1.0)'; + } else if (this.deltaUncorrectableCount !== 0) { + return 'rgb(200, 0, 0, 1.0)'; + } else if (this.deltaCorrectableCount !== 0) { + return 'rgb(160, 160, 160, 1.0)'; + } else { + return 'rgb(0, 200, 0, 1.0)'; + } + } + + getStreamStatusText(): string { + if (this.deltaSampleCount === 0) { + return 'Not streaming'; + } else if (this.deltaUncorrectableCount !== 0) { + return 'Data lost'; + } else if (this.deltaCorrectableCount !== 0) { + return 'Data corrected'; + } else { + return 'Streaming OK'; + } + } } diff --git a/src/app/device-details/remote-output/remote-output.ts b/src/app/device-details/remote-output/remote-output.ts index 8c3dfc2..f3f7531 100644 --- a/src/app/device-details/remote-output/remote-output.ts +++ b/src/app/device-details/remote-output/remote-output.ts @@ -1,43 +1,51 @@ export interface RemoteOutputSettings { - centerFrequency?: number; - sampleRate?: number; - txDelay?: number; // minimum delay in ms between two consecutive packets sending - nbFECBlocks?: number; apiAddress?: string; apiPort?: number; + channelIndex?: number; // remote SDRangel instance channel index dataAddress?: string; dataPort?: number; deviceIndex?: number; // remote SDRangel instance deviceset index - channelIndex?: number; // remote SDRangel instance channel index - useReverseAPI?: number; // bool + nbFECBlocks?: number; reverseAPIAddress?: string; - reverseAPIPort?: number; reverseAPIDeviceIndex?: number; + reverseAPIPort?: number; + useReverseAPI?: number; // bool } export const REMOTE_OUTPUT_SETTINGS_DEFAULT = { - centerFrequency: 435000000, - sampleRate: 48000, - txDelay: 0.35, - nbFECBlocks: 0, apiAddress: '127.0.0.1', apiPort: 9091, + channelIndex: 0, dataAddress: '127.0.0.1', dataPort: 9090, deviceIndex: 0, - channelIndex: 0, - useReverseAPI: 0, + nbFECBlocks: 0, reverseAPIAddress: '127.0.0.1', + reverseAPIDeviceIndex: 0, reverseAPIPort: 8888, - reverseAPIDeviceIndex: 0 + useReverseAPI: 0 }; export interface RemoteOutputReport { - bufferRWBalance: number; // ratio off the mid buffer (positive read leads) + centerFrequency: number; // cener frequency of remote (in stream) sampleCount: number; // count of samples that have been sent + sampleRate: number; // sample rate in remote (in stream) + queueLength: number; + queueSize: number; + correctableErrorsCount: number; + uncorrectableErrorsCount: number; + tvSec: number; + tvUSec: number; } export const REMOTE_OUTPUT_REPORT_DEFAULT = { - bufferRWBalance: 0, - sampleCount: 0 + centerFrequency: 435000000, + sampleCount: 0, + sampleRate: 48000, + queueLength: 2, + queueSize: 20, + correctableErrorsCount: 0, + uncorrectableErrorsCount: 0, + tvSec: 1535913707, + tvUSec: 667575 };