1 #include "sstvrx.h"
2 #include "appglobal.h"
3 #include "configparams.h"
4 #include "dispatcher.h"
5 #include "filterparam.h"
6 #include "filters.h"
7 #include "modes/modebase.h"
8
9
10 #ifndef QT_NO_DEBUG
11 #include "arraydumper.h"
12 #include "scope/scopeview.h"
13 #include "scope/scopeoffset.h"
14 #endif
15
16 QString defaultImageFormat;
17 int minCompletion;
18
19 #define DECAY 0.9956
20
21 #ifdef DISABLENARROW
22 #pragma message "NARROW DETECTION DISABLED"
23 #endif
24
25 const QString stateStr[sstvRx::END+1]=
26 {
27 "Hunting",
28 "Processing",
29 "WaitForSync",
30 "SlantAdjust",
31 "Restart",
32 "Sync Lost",
33 "Wait",
34 "End"
35 };
36
37
sstvRx(QObject * parent)38 sstvRx::sstvRx(QObject *parent) : QObject(parent),syncNarrowProc(true),syncWideProc(false)
39 {
40 syncFilterPtr=NULL;
41 videoFilterPtr=NULL;
42 syncProcPtr=NULL;
43 #ifndef QT_NO_DEBUG
44 scopeViewerData=new scopeView("Data Scope");
45 scopeViewerSyncNarrow=new scopeView("Sync Scope Narrow");
46 scopeViewerSyncWide=new scopeView("Sync Scope Wide");
47 #endif
48 }
49
init()50 void sstvRx::init()
51 {
52 setFilters(); // setup sstvRx Filters
53 resetParams(true);
54 #ifndef QT_NO_DEBUG
55 scopeViewerData->setAlternativeScaleMultiplier(SUBSAMPLINGFACTOR/rxClock);
56 scopeViewerData->setCurveName("RX VOL",SCDATA1);
57 scopeViewerData->setCurveName("TEST",SCDATA2);
58 scopeViewerData->setCurveName("STATE",SCDATA3);
59 scopeViewerData->setCurveName("DEMOD",SCDATA4);
60 scopeViewerData->setAxisTitles("Samples","int","State");
61 scopeViewerSyncNarrow->setAlternativeScaleMultiplier(SUBSAMPLINGFACTOR/rxClock);
62 scopeViewerSyncWide->setAlternativeScaleMultiplier(SUBSAMPLINGFACTOR/rxClock);
63 #endif
64 }
resetParams(bool bufferReset)65 void sstvRx::resetParams(bool bufferReset)
66 {
67 SSTVState=HUNTING;
68 bufferCounter=0;
69 if(bufferReset)
70 {
71 addToLog("reset Buffers",LOGRXMAIN);
72 agcVolume=0;
73 bufferVideoDemod.reset();
74 bufferSync1200Vol.reset();
75 bufferSync1900Vol.reset();
76 bufferInputVol.reset();
77 syncNarrowProc.reset();
78 syncWideProc.reset();
79 }
80 else
81 {
82 syncNarrowProc.init();
83 syncWideProc.init();
84 }
85 }
86
~sstvRx()87 sstvRx::~sstvRx()
88 {
89 if(videoFilterPtr!=NULL) delete videoFilterPtr;
90 if(syncFilterPtr !=NULL) delete syncFilterPtr;
91 }
92
setFilters()93 void sstvRx::setFilters()
94 {
95 if(videoFilterPtr==NULL)
96 videoFilterPtr=new videoFilter(RXSTRIPE);
97 else
98 videoFilterPtr->init();
99 if(syncFilterPtr==NULL) syncFilterPtr=new syncFilter(RXSTRIPE);
100 else syncFilterPtr->init();
101 }
102
103
104 //void sstvRx::run(DSPFLOAT *dataPtr, unsigned int *inputVolumePtr)
run(DSPFLOAT * dataPtr,DSPFLOAT * volumePtr)105 void sstvRx::run(DSPFLOAT *dataPtr,DSPFLOAT *volumePtr)
106 {
107 bufferCounter++;
108 if(videoFilterPtr==NULL)
109 {
110 errorOut() << "videoFltr NULL";
111 return;
112 }
113 // run AGC
114 // for(i=0;i<RXSTRIPE;i++)
115 // {
116 // DSPFLOAT tmp=sqrt(dataPtr[i]*dataPtr[i]);
117 // if(tmp>agcVolume) agcVolume=tmp;
118 // else agcVolume*=DECAY;
119 // if(agcVolume<1000) agcVolume=1000; //maximum gain is 8
120 // dataPtr[i]=dataPtr[i]*8000/agcVolume;
121 // }
122 videoFilterPtr->process(dataPtr);
123 bufferVideoDemod.putNoCheck(videoFilterPtr->demodPtr,RXSTRIPE);
124 syncFilterPtr->process(dataPtr);
125 bufferSync1200Vol.putNoCheck(syncFilterPtr->detect1200Ptr,RXSTRIPE);
126 #ifndef DISABLENARROW
127 bufferSync1900Vol.putNoCheck(syncFilterPtr->detect1900Ptr,RXSTRIPE);
128 #endif
129 bufferInputVol.putNoCheck(volumePtr ,RXSTRIPE);
130
131 while (bufferVideoDemod.count())
132 {
133 syncWideProc.freqPtr=bufferVideoDemod.readPointer();
134 syncNarrowProc.freqPtr=bufferVideoDemod.readPointer();
135 syncWideProc.syncVolumePtr =bufferSync1200Vol.readPointer();
136 syncNarrowProc.syncVolumePtr =bufferSync1900Vol.readPointer();
137 syncWideProc.inputVolumePtr=bufferInputVol.readPointer();
138 syncNarrowProc.inputVolumePtr=bufferInputVol.readPointer();
139
140 displaySyncEvent* ce;
141 if(syncProcPtr)
142 {
143 ce = new displaySyncEvent(syncProcPtr->syncQuality);
144 }
145 else
146 {
147 ce = new displaySyncEvent(0);
148 }
149 QApplication::postEvent(dispatcherPtr, ce);
150 #ifndef QT_NO_DEBUG
151 scopeViewerData->addData(SCDATA1,bufferInputVol.readPointer(),syncWideProc.sampleCounter,RXSTRIPE);
152 scopeViewerData->addData(SCDATA2,dataPtr,syncWideProc.sampleCounter,RXSTRIPE);
153 scopeViewerData->addData(SCDATA4,bufferVideoDemod.readPointer(),syncWideProc.sampleCounter,RXSTRIPE);
154 #endif
155
156 process();
157 }
158 // addToLog(QString("After process readIndex:=%1 sampleCounter:=%2").arg(bufferVideoDemod.getReadIndex()).arg(syncProcPtr->sampleCounter),LOGRXFUNC);
159 }
160
advanceBuffers()161 void sstvRx::advanceBuffers()
162 {
163 syncWideProc.sampleCounter+=RXSTRIPE;
164 syncNarrowProc.sampleCounter+=RXSTRIPE;
165 bufferVideoDemod.skip(RXSTRIPE);
166 bufferSync1200Vol.skip(RXSTRIPE);
167 bufferSync1900Vol.skip(RXSTRIPE);
168 bufferInputVol.skip(RXSTRIPE);
169 }
170
rewindBuffers(uint rlen)171 void sstvRx::rewindBuffers(uint rlen)
172 {
173 syncWideProc.sampleCounter-=rlen;
174 syncNarrowProc.sampleCounter-=rlen;
175 bufferVideoDemod.rewind(rlen);
176 bufferSync1200Vol.rewind(rlen);
177 bufferSync1900Vol.rewind(rlen);
178 bufferInputVol.rewind(rlen);
179 }
180
181
182
183
process()184 void sstvRx::process()
185 {
186 // unsigned int ri;
187 quint32 block;
188 quint32 syncPosition;
189 quint32 sampleCounterLatch;
190 quint32 diff;
191
192 syncProcessor::esyncProcessState syncStateWide;
193 syncProcessor::esyncProcessState syncStateNarrow;
194 syncProcessor::esyncProcessState syncState;
195 rxSSTVStatusEvent *stce;
196
197 syncWideProc.process();
198
199 syncStateWide=syncWideProc.getSyncState(syncPosition);
200
201 if(syncStateWide!=syncProcessor::MODEDETECT)
202 {
203 usingWide=true;
204 syncNarrowProc.setSyncDetectionEnabled(false);
205 syncProcPtr=&syncWideProc;
206 }
207 else
208 {
209 syncNarrowProc.process();
210 syncStateNarrow=syncNarrowProc.getSyncState(syncPosition);
211 if(syncStateNarrow!=syncProcessor::MODEDETECT)
212 {
213 usingWide=false;
214 syncWideProc.setSyncDetectionEnabled(false);
215 syncProcPtr=&syncNarrowProc;
216 }
217 else
218 {
219 syncProcPtr=NULL;
220 syncState=syncProcessor::MODEDETECT;
221 if(SSTVState!=HUNTING)
222 {
223 switchState(HUNTING);
224 }
225 }
226 }
227 if(syncProcPtr)
228 {
229 syncState=syncProcPtr->getSyncState(syncPosition);
230 }
231
232
233 if((syncProcPtr!=0) && (syncProcPtr->getSyncState(syncPosition)==syncProcessor::SYNCLOST))
234 {
235 switchState(SYNCLOST);
236 }
237 switch (SSTVState)
238 {
239 case HUNTING:
240 if(syncProcPtr==NULL)
241 {
242 stce= new rxSSTVStatusEvent(QString("No sync"));
243 QApplication::postEvent( dispatcherPtr, stce ); // Qt will delete it when done
244 advanceBuffers();
245 break; // no sync
246 }
247 stce= new rxSSTVStatusEvent(QString("Receiving ")+getSSTVModeNameLong(syncProcPtr->getMode()));
248 lastUsedModeStr=getSSTVModeNameShort(syncProcPtr->getMode());
249 QApplication::postEvent( dispatcherPtr, stce ); // Qt will delete it when done
250 // fallthrough for first processing
251 switchState(SLANTADJUST); // for logging
252 // clear the call received via fskID
253 emit (resetCall());
254
255 case SLANTADJUST:
256 sampleCounterLatch=syncProcPtr->sampleCounter; //remember where we've got
257 // ri=bufferVideoDemod.getReadIndex();
258 // addToLog(QString("rxFunctions: sampleCounterLatch= %1,readIndex=%2").arg(sampleCounterLatch).arg(ri),LOGRXFUNC);
259 block=(syncPosition)/RXSTRIPE;
260 bufferVideoDemod.rewind(syncProcPtr->sampleCounter-block*RXSTRIPE);
261 // ri=bufferVideoDemod.getReadIndex();
262 // addToLog(QString("sc_rewind: block=%1,new readIndex= %2").arg(block).arg(ri),LOGRXFUNC);
263 syncProcPtr->sampleCounter=block*RXSTRIPE;
264 syncProcPtr->currentModePtr->setRxSampleCounter(syncProcPtr->sampleCounter);
265 syncProcPtr->currentModePtr->redrawFast(true);
266 if(syncProcPtr->currentModePtr->process(bufferVideoDemod.readPointer(),syncPosition-syncProcPtr->sampleCounter,true,syncProcPtr->sampleCounter)!=modeBase::MBRUNNING)
267 {
268 switchState(END);
269 break;
270 }
271
272
273 // scopeViewerData->addData(SCDATA2,bufferVideoDemod.readPointer(),syncProcPtr->sampleCounter,RXSTRIPE);
274 // addToLog(QString("slant scope add demodIdx=%1; syncProcPtr->sampleCounter=%2").arg(bufferVideoDemod.getReadIndex()).arg(syncProcPtr->sampleCounter),LOGRXFUNC);
275 #ifndef QT_NO_DEBUG
276 scopeViewerData->addData(SCDATA3,syncProcPtr->currentModePtr->debugStatePtr,syncProcPtr->sampleCounter,RXSTRIPE);
277 #endif
278 addToLog(QString("rxFunctions: currentMode pos:=%1, syncProcPtr->sampleCounter %2").arg(syncPosition-syncProcPtr->sampleCounter).arg(syncProcPtr->sampleCounter),LOGRXFUNC);
279 // addToLog(QString("after Current mode set: %1,syncProcPtr->sampleCounter: %2").arg(rxHoldingBuffer.getReadIndex()).arg(syncProcPtr->sampleCounter),LOGRXFUNC);
280 while(syncProcPtr->sampleCounter<sampleCounterLatch)
281 {
282 bufferVideoDemod.skip(RXSTRIPE);
283 syncProcPtr->sampleCounter+=RXSTRIPE;
284 // addToLog(QString("loop readIndex: %1,syncProcPtr->sampleCounter: %2").arg(rxHoldingBuffer.getReadIndex()).arg(syncProcPtr->sampleCounter),LOGRXFUNC);
285 syncProcPtr->currentModePtr->process(bufferVideoDemod.readPointer(),0,false,syncProcPtr->sampleCounter);
286 // scopeViewerData->addData(SCDATA2,bufferVideoDemod.readPointer(),syncProcPtr->sampleCounter,RXSTRIPE);
287 #ifndef QT_NO_DEBUG
288 scopeViewerData->addData(SCDATA3,syncProcPtr->currentModePtr->debugStatePtr,syncProcPtr->sampleCounter,RXSTRIPE);
289 #endif
290 }
291 addToLog(QString("end loop readIndex: %1,syncProcPtr->sampleCounter: %2").arg(bufferVideoDemod.getReadIndex()).arg(syncProcPtr->sampleCounter),LOGRXFUNC);
292 syncProcPtr->currentModePtr->redrawFast(false);
293 syncProcPtr->recalculateMatchArray();
294 switchState(PROCESSING);
295 advanceBuffers();
296 break;
297 case PROCESSING:
298 if(syncState!=syncProcessor::INSYNC)
299 {
300 switchState(END);
301 }
302 else if(syncProcPtr->retraceFlag)
303 {
304 addToLog(QString("retrace detected"),LOGRXFUNC);
305 saveImage();
306 rewindBuffers(8*RXSTRIPE);
307 syncProcPtr->resetRetraceFlag();
308 resetParams(false);
309 break;
310 }
311 else
312 {
313 if(syncProcPtr->currentModePtr->process(bufferVideoDemod.readPointer(),0,false,syncProcPtr->sampleCounter)!=modeBase::MBRUNNING)
314 {
315 switchState(END);
316 }
317 }
318
319 if(syncProcPtr->hasNewClock())
320 {
321 syncProcPtr->currentModePtr->init(syncProcPtr->getNewClock());
322 switchState(SLANTADJUST);
323 }
324 #ifndef QT_NO_DEBUG
325 scopeViewerData->addData(SCDATA3,syncProcPtr->currentModePtr->debugStatePtr,syncProcPtr->sampleCounter,RXSTRIPE);
326 #endif
327 advanceBuffers();
328 if(syncProcPtr->tempOutOfSync)
329 {
330 bufferIdx=bufferVideoDemod.getReadIndex();
331 switchState(WAITFORSYNC);
332 }
333 break;
334 case WAITFORSYNC:
335 {
336
337 if(!(syncState==syncProcessor::INSYNC))
338 {
339 switchState(END);
340 }
341 else if(syncProcPtr->retraceFlag)
342 {
343 addToLog(QString("retrace detected"),LOGRXFUNC);
344 saveImage();
345 // addToLog(QString("before rewind readIndex:=%1 sampleCounter:=%2").arg(bufferVideoDemod.getReadIndex()).arg(syncProcPtr->sampleCounter),LOGRXFUNC);
346 rewindBuffers(8*RXSTRIPE);
347 syncProcPtr->resetRetraceFlag();
348 resetParams(false);
349 // addToLog(QString("after resetParms readIndex:=%1 sampleCounter:=%2").arg(bufferVideoDemod.getReadIndex()).arg(syncProcPtr->sampleCounter),LOGRXFUNC);
350 break;
351 }
352
353 else
354 {
355 currentIdx=bufferVideoDemod.getReadIndex();
356 if(!syncProcPtr->tempOutOfSync)
357 {
358 bufferVideoDemod.setReadIndex(bufferIdx);
359 while(bufferVideoDemod.getReadIndex()!=currentIdx)
360 {
361 if(syncProcPtr->currentModePtr->process(bufferVideoDemod.readPointer(),0,false,syncProcPtr->sampleCounter)==modeBase::MBENDOFIMAGE)
362 {
363 switchState(END);
364 }
365 bufferVideoDemod.skip(RXSTRIPE);
366 }
367 if(syncProcPtr->currentModePtr->process(bufferVideoDemod.readPointer(),0,false,syncProcPtr->sampleCounter)==modeBase::MBENDOFIMAGE)
368 {
369 switchState(END);
370 }
371 else
372 {
373 switchState(PROCESSING);
374 }
375 }
376 }
377 advanceBuffers();
378 break;
379 }
380 case RESTART:
381 resetParams(true);
382 break;
383 case SYNCLOST:
384 saveImage();
385 diff=(syncProcPtr->sampleCounter-syncProcPtr->lastValidSyncCounter)/RXSTRIPE;
386 addToLog(QString("rewind after synclost %1").arg(diff),LOGRXFUNC);
387 rewindBuffers(diff*RXSTRIPE);
388 syncProcPtr->resetRetraceFlag();
389 resetParams(true);
390 break;
391 case WAIT:
392 break;
393 case END:
394 saveImage();
395 resetParams(false); // will set state to HUNTING
396 advanceBuffers();
397 break;
398 }
399 }
400
saveImage()401 void sstvRx::saveImage()
402 {
403 bool done=false;
404 addToLog("saveImage",LOGRXFUNC);
405 endImageSSTVRXEvent *endce;
406 if(syncProcPtr->currentModePtr->receivedLines()<(syncProcPtr->currentModePtr->imageLines()*minCompletion)/100)
407 {
408 endce = new endImageSSTVRXEvent(NOTVALID); //indicate there is no valid image
409 }
410 else
411 {
412 // endce = new endImageSSTVRXEvent(getSSTVModeNameShort(syncProcPtr->getMode()));
413 endce = new endImageSSTVRXEvent(syncProcPtr->getMode());
414 }
415 endce->waitFor(&done);
416 QApplication::postEvent(dispatcherPtr, endce);
417 while(!done) { usleep(100);}
418 }
419
420
switchState(eSSTVState newState)421 void sstvRx::switchState(eSSTVState newState)
422 {
423 addToLog(QString("%1 to %2").arg(stateStr[SSTVState]).arg(stateStr[newState]),LOGRXFUNC);
424 SSTVState=newState;
425 }
426
eraseImage()427 void sstvRx::eraseImage()
428 {
429 switchState(RESTART);
430 while(SSTVState!=HUNTING)
431 {
432 QApplication::processEvents();
433 }
434 }
435
436
437 #ifndef QT_NO_DEBUG
setOffset(unsigned int offset,bool ask)438 unsigned int sstvRx::setOffset(unsigned int offset,bool ask)
439 {
440 unsigned int xOffset=0;
441 if(ask)
442 {
443 scopeOffset so;
444 so.setOffset(offset);
445 if(so.exec()==QDialog::Accepted)
446 {
447 xOffset=so.getOffset()*1000;
448 }
449 }
450 else
451 {
452 xOffset=offset*1000;
453 }
454 syncNarrowProc.clear();
455 syncWideProc.clear();
456 scopeViewerData->clear();
457 syncNarrowProc.setOffset(xOffset);
458 syncWideProc.setOffset(xOffset);
459 scopeViewerData->setOffset(xOffset);
460
461
462 return xOffset/1000;
463 }
464 #endif
465
466