/********************************************************/ /* nabtsdsp.c */ /* */ /* Perform DSP required for NABTS decoding */ /* */ /* Copyright Microsoft, Inc. 1997. All rights reserved. */ /********************************************************/ #include #include #include #include #include #ifdef NTOOL # include # include "winsys.h" #else //NTOOL # include # include # include #endif //!NTOOL #include "host.h" #include "nabtsapi.h" #include "nabtsprv.h" #include "nabtsdsp.h" #pragma warning(disable:4244 4305) inline int fltCmp(Double a, Double b) { return (a == b); } #define NDSP_STATE_MAGIC 0x648a0a59 /***************/ /*** Globals ***/ /***************/ /* Have the global GCR signals been initialized? 0 if no, 1 if yes. See NDSPInitGlobals */ int g_bNDSPGlobalsInitted= 0; /* How many fields to check during retrain operation */ int g_nNabtsRetrainDuration= 16; /* in fields */ /* How many fields between automatic retrains. (Note that immediate retrain can be caused also by NDSPStartRetrain) */ int g_nNabtsRetrainDelay= 600; /* in fields */ /* How many "far ghost" equalization taps should we add? Note that this only occurs when we are using the GCR for equalization/ghost cancellation. The NABTS sync is not long enough to be able to detect and cancel far ghosts. */ int g_nNabtsAdditionalTapsGCR= 2; /* # of additional taps to add while equalizing */ /* The following is extraneous and should be removed at earliest convenience: */ Double g_dGCRDetectionThreshold= 100000.0; /* extraneous */ /* Minimum and maximum limits to the sample # at which the first NABTS sync sample occurs. These are used by NDSPDecodeFindSync. Be careful changing these numbers, as you want to make sure that there are enough samples left at the beginning and end of the lines to do adequate filtering. As of the time of writing these comments, the range is 20 to 85. This means that VBI signal should be adjusted such that the GCR begins roughly at (20+85)/2, or around sample 52. To verify adjustment of the VBI signal, enable the "Sync found at %d with conv ..." debugging line in NDSPDecodeLine and plot a histogram of the found sync positions. Maladjustment of the VBI signal probably means incorrect values in the VBIINFOHEADER structure passed with the signal. */ /* replace these w/ time-based values... */ //int g_nNabtsDecodeBeginMin= 20; /* BeginMax should be less than DecodeSamples - (36*8*5) - 10 */ //int g_nNabtsDecodeBeginMax= 85; int g_nNabtsDecodeBeginStart; int g_nNabtsDecodeBeginEnd; /* What's the longest post-ghost which we can correct without requiring samples we don't have? */ int g_nNabtsMaxPostGhost= 170; /* in samples */ /* What's the longest pre-ghost which we can correct without requiring samples we don't have? */ int g_nNabtsMaxPreGhost= 40; /* in samples */ /* This is the default filter used for signals if both GCR-based and NABTS sync-based equalization/ghost cancellation algorithms fail */ FIRFilter g_filterDefault5= { FALSE, /* bVariableTaps */ 5, /* nTaps */ 5, /* dTapSpacing */ 0, /* nMinTap */ 0, /* nMaxTap */ { 0.0549969, -0.115282, 0.946446, -0.260854, 0.0476174}, /* pdTaps */ //{} /* pnTapLocs */ }; FIRFilter g_filterDefault47 = { FALSE, /* bVariableTaps */ 5, /* nTaps */ KS_47NABTS_SCALER, /* dTapSpacing */ //FIXME (maybe) 0, /* nMinTap */ 0, /* nMaxTap */ { 0.0549969, -0.115282, 0.946446, -0.260854, 0.0476174}, /* pdTaps */ //{} /* pnTapLocs */ }; FIRFilter g_filterDefault4= { FALSE, /* bVariableTaps */ 5, /* nTaps */ 4, /* dTapSpacing */ 0, /* nMinTap */ 0, /* nMaxTap */ { 0.0549969, -0.115282, 0.946446, -0.260854, 0.0476174}, /* pdTaps */ //{} /* pnTapLocs */ }; FIRFilter* g_filterDefault = &g_filterDefault5; /* If DEBUG_VERBOSE, do we show information about NABTS sync? */ int g_bDebugSyncBytes= FALSE; /* This is a set of samples representing a typical NABTS sync taken at NABTS bit rate * 5. It is used for NABTS sync equalization, which is a fallback from the GCR equalization/ghost cancellation */ Double g_pdSync5[NABSYNC_SIZE]={ 72.7816, 98.7424, 128.392, 152.351, 161.749, 153.177, 127.707, 94.7642, 67.3989, 57.3863, 68.85, 97.1427, 131.288, 156.581, 164.365, 151.438, 123.222, 90.791, 66.191, 57.8995, 70.1041, 98.0289, 129.88, 154.471, 161.947, 149.796, 123.056, 90.4673, 64.7314, 55.3073, 67.6522, 96.0781, 129.079, 154.704, 162.274, 148.879, 120.957, 88.971, 64.2987, 57.3871, 69.9341, 97.2937, 130.365, 154.517, 161.46, 149.066, 122.341, 89.6801, 64.8201, 56.3087, 67.9328, 95.4871, 127.708, 152.344, 160.203, 147.785, 120.972, 89.2391, 64.8204, 55.4777, 67.4764, 95.4465, 128.277, 153.469, 161.047, 149.581, 123.23, 90.3241, 65.0892, 57.5301, 69.3996, 97.4484, 130.064, 154.063, 161.211, 147.833, 119.759, 89.3406, 67.3504, 60.1918, 70.7763, 93.4328, 120.709, 145.109, 160.585, 166.873, 166.863, 163.397, 159.525, 158.707, 160.963, 165.351, 167.585, 165.082, 154.448, 135.721, 112.454, 88.9855, 68.8211, 55.3262, 47.8155, 45.8446, 47.352, 51.1301, 59.5515, 74.6295, 96.5327, 121.793, 144.703, 160.891, 167.617, 164.946, 159.698, 156.271, 157.871 }; Double g_pdSync47[NABSYNC_SIZE] = { 72.7816, 96.9455, 125.488, 150.642, 163.827, 160.895, 139.686, 106.374, 72.2535, 51.1334, 51.7029, 74.3792, 112.893, 150.448, 175.333, 177.089, 154.67, 66.4422, 56.79, 65.8731, 91.2727, 123.173, 151.979, 166.734, 161.95, 139.844, 105.285, 70.7285, 46.7033, 46.1175, 69.0775, 106.56, 147.593, 175.669, 122.895, 91.9615, 65.5553, 54.3455, 61.6435, 85.2679, 120.119, 151.151, 168.22, 165.263, 144.115, 107.76, 71.526, 46.4459, 42.8836, 64.2428, 152.106, 161.332, 151.847, 127.703, 95.8986, 67.9346, 50.751, 54.7629, 78.5341, 113.773, 148.647, 169.038, 169.544, 150.153, 112.5, 72.1901, 45.6607, 95.472, 127.155, 152.763, 164.454, 156.34, 130.82, 98.6696, 70.8212, 54.4185, 57.0453, 75.2489, 102.964, 132.916, 155.25, 166.882, 170.224, 159.55, 158.502, 160.298, 164.877, 168.268, 168.627, 161.825, 146.297, 124.544, 100.595, 77.4086, 60.5608, 49.3086, 44.6113, 44.0319, 43.2192, 44.4737, 95.0021, 119.016, 141.76, 159.261, 168.426, 166.854, 161.152, 155.495, }; Double g_pdSync4[NABSYNC_SIZE] = { -39.1488, -12.0978, 33.5648, 46.2761, 39.4813, 9.29926, -35.5121, -54.1576, -43.0804, -8.89138, 34.4705, 51.1527, 37.7423, 3.70292, -37.0844, -54.5635, -41.8263, -8.01077, 31.8198, 49.2458, 36.1003, 3.28592, -37.4172, -57.2909, -44.2782, -9.47973, 31.2838, 49.8912, 35.1833, 1.00765, -38.2074, -56.0426, -41.9963, -8.0181, 31.6699, 49.2505, 35.3703, 2.16505, -37.3994, -56.7359, -43.9976, -9.86633, 28.8379, 47.9894, 34.0893, 0.793831, -37.1945, -57.5387, -44.454, -9.71416, 29.6742, 48.954, 35.8853, 2.77469, -36.6699, -56.0604, -42.5308, -7.411, 30.524, 49.4549, 34.1373, -0.682732, -35.4269, -53.5466, -41.1541, -12.8922, 20.253, 45.9943, 53.1773, 53.4395, 49.3341, 44.4674, 49.0326, 53.631, 53.0627, 46.721, 22.0253, -6.8225, -32.9031, -56.6665, -64.1149, -65.8565, -66.4073, -53.3933, -39.0662, -11.9141, 25.1485, 40.7185, 55.6866, 56.0335, 31.9488, 64.5845, -112.813 }; int g_nNabtsSyncSize = NABSYNC_SIZE; Double g_pdSync[MAX_NABTS_SAMPLES_PER_LINE]; /* used for resampling */ /* This is a set of samples representing a typical GCR waveform taken at NABTS bit rate * 2.5. It is used for GCR-based equalization and ghost cancellation, which is the primary and most desirable strategy */ Double g_pdGCRSignal1_5[GCR_SIZE]={ 0.00000000000000E+0000, 1.02260999999970E-0003, 1.05892999999924E-0003, 1.09718000000036E-0003, 1.13701999999982E-0003, 1.17906999999917E-0003, 1.22286999999943E-0003, 1.26910000000002E-0003, 1.31748000000087E-0003, 1.36846999999918E-0003, 1.42190000000042E-0003, 1.47821000000015E-0003, 1.53740000000013E-0003, 1.59988999999960E-0003, 1.66547000000072E-0003, 1.73485999999912E-0003, 1.80787000000038E-0003, 1.88510000000086E-0003, 1.96657000000044E-0003, 2.05283000000023E-0003, 2.14390000000009E-0003, 2.24045999999944E-0003, 2.34250000000102E-0003, 2.45085999999972E-0003, 2.56557000000157E-0003, 2.68752999999933E-0003, 2.81685999999937E-0003, 2.95447999999965E-0003, 3.10071999999906E-0003, 3.25656000000052E-0003, 3.42231000000126E-0003, 3.59925000000061E-0003, 3.78781999999944E-0003, 3.98931000000147E-0003, 4.20439999999900E-0003, 4.43461999999784E-0003, 4.68077000000022E-0003, 4.94463999999795E-0003, 5.22724000000352E-0003, 5.53064000000347E-0003, 5.85610000000258E-0003, 6.20609999999999E-0003, 6.58219999999687E-0003, 6.98715000000050E-0003, 7.42307000000153E-0003, 7.89319000000432E-0003, 8.40024000000028E-0003, 8.94780000000139E-0003, 9.53912999999318E-0003, 1.01788000000056E-0002, 1.08706000000041E-0002, 1.16199999999935E-0002, 1.24318000000017E-0002, 1.33122999999955E-0002, 1.42673999999943E-0002, 1.53047000000015E-0002, 1.64313999999877E-0002, 1.76562000000047E-0002, 1.89881000000014E-0002, 2.04373999999916E-0002, 2.20148999999878E-0002, 2.37329999999929E-0002, 2.56043999999918E-0002, 2.76438999999868E-0002, 2.98664999999971E-0002, 3.22896999999784E-0002, 3.49309999999718E-0002, 3.78107999999884E-0002, 4.09500000000094E-0002, 4.43718999999874E-0002, 4.81004000000098E-0002, 5.21621999999979E-0002, 5.65846999999735E-0002, 6.13976000000207E-0002, 6.66313000000400E-0002, 7.23186999999825E-0002, 7.84923999999592E-0002, 8.51879000000508E-0002, 9.24387000000024E-0002, 1.00280999999995E-0001, 1.08748999999989E-0001, 1.17876000000024E-0001, 1.27694000000020E-0001, 1.38230999999905E-0001, 1.49511000000075E-0001, 1.61552999999913E-0001, 1.74367999999959E-0001, 1.87959000000092E-0001, 2.02316999999994E-0001, 2.17419999999947E-0001, 2.33228000000054E-0001, 2.49684999999999E-0001, 2.66712000000098E-0001, 2.84204999999929E-0001, 3.02032999999938E-0001, 3.20032000000083E-0001, 3.38005000000067E-0001, 3.55717999999797E-0001, 3.72897000000194E-0001, 3.89230000000225E-0001, 4.04360999999881E-0001, 4.17892999999822E-0001, 4.29389999999785E-0001, 4.38381999999820E-0001, 4.44367000000057E-0001, 4.46823000000222E-0001, 4.45215000000189E-0001, 4.39011999999821E-0001, 4.27705000000060E-0001, 4.10821999999825E-0001, 3.87955000000147E-0001, 3.58785999999782E-0001, 3.23113999999805E-0001, 2.80886000000010E-0001, 2.32228000000077E-0001, 1.77474000000075E-0001, 1.17197000000033E-0001, 5.22252000000094E-0002, -1.63339000000065E-0002, -8.70900999999549E-0002, -1.58376000000089E-0001, -2.28262000000086E-0001, -2.94597000000067E-0001, -3.55059999999867E-0001, -4.07236000000012E-0001, -4.48710000000119E-0001, -4.77181000000201E-0001, -4.90596000000096E-0001, -4.87282999999934E-0001, -4.66113999999834E-0001, -4.26633000000038E-0001, -3.69197999999869E-0001, -2.95083999999861E-0001, -2.06552999999985E-0001, -1.06861999999978E-0001, -2.26569000000065E-0004, 1.08304999999973E-0001, 2.13048999999955E-0001, 3.07967000000190E-0001, 3.87016000000131E-0001, 4.44562000000133E-0001, 4.75829999999860E-0001, 4.77361000000201E-0001, 4.47440999999799E-0001, 3.86449999999968E-0001, 2.97098000000005E-0001, 1.84488000000101E-0001, 5.59898000000203E-0002, -7.91186000000152E-0002, -2.10235999999895E-0001, -3.26232999999775E-0001, -4.16419000000133E-0001, -4.71591999999873E-0001, -4.85105000000203E-0001, -4.53794000000016E-0001, -3.78679999999804E-0001, -2.65297999999802E-0001, -1.23576999999955E-0001, 3.28038999999762E-0002, 1.87584000000015E-0001, 3.23534999999993E-0001, 4.24414000000070E-0001, 4.77027999999791E-0001, 4.73159000000123E-0001, 4.11086000000068E-0001, 2.96408000000156E-0001, 1.41969000000017E-0001, -3.32369999999855E-0002, -2.06171999999924E-0001, -3.52687000000060E-0001, -4.50949999999921E-0001, -4.84910000000127E-0001, -4.47235999999975E-0001, -3.41190000000097E-0001, -1.80984000000080E-0001, 9.63199000000259E-0003, 2.00563999999986E-0001, 3.59968000000208E-0001, 4.59641999999803E-0001, 4.80308000000150E-0001, 4.15771999999833E-0001, 2.75076999999783E-0001, 8.19416000000501E-0002, -1.28693999999996E-0001, -3.16683999999896E-0001, -4.44307000000208E-0001, -4.84112000000096E-0001, -4.25353000000086E-0001, -2.77427999999873E-0001, -6.92708000000266E-0002, 1.55655000000024E-0001, 3.48071999999775E-0001, 4.63710999999876E-0001, 4.73907999999938E-0001, 3.73398000000179E-0001, 1.83113999999932E-0001, -5.32258000000070E-0002, -2.78533000000152E-0001, -4.35938999999962E-0001, -4.83456999999817E-0001, -4.05898999999863E-0001, -2.20585999999912E-0001, 2.53643000000068E-0002, 2.66195000000153E-0001, 4.34799999999996E-0001, 4.81733999999960E-0001, 3.90551999999843E-0001, 1.84567999999899E-0001, -7.78073999999833E-0002, -3.18760000000111E-0001, -4.63948000000073E-0001, -4.65948999999910E-0001, -3.20834999999988E-0001, -7.18414000000394E-0002, 2.02201999999943E-0001, 4.11149000000023E-0001, 4.83345000000099E-0001, 3.91160000000127E-0001, 1.62923000000092E-0001, -1.24134000000026E-0001, -3.68950000000041E-0001, -4.82100000000173E-0001, -4.19299000000137E-0001, -2.00237999999899E-0001, 9.56523999999490E-0002, 3.56715999999778E-0001, 4.80940999999802E-0001, 4.16690999999901E-0001, 1.85821000000033E-0001, -1.21634999999969E-0001, -3.81190000000061E-0001, -4.84109999999873E-0001, -3.84040999999797E-0001, -1.19752999999946E-0001, 1.97697999999946E-0001, 4.30369000000155E-0001, 4.73501999999826E-0001, 3.04200000000037E-0001, -4.45780999999812E-0003, -3.12843000000157E-0001, -4.77105999999822E-0001, -4.17065000000093E-0001, -1.57408999999916E-0001, 1.79722999999967E-0001, 4.30503999999928E-0001, 4.69046000000162E-0001, 2.72319000000152E-0001, -6.37966000000461E-0002, -3.68903000000046E-0001, -4.83776999999918E-0001, -3.44759999999951E-0001, -2.16183999999942E-0002, 3.14663999999993E-0001, 4.81103000000076E-0001, 3.83209000000079E-0001, 7.12525999999798E-0002, -2.82396000000062E-0001, -4.76846999999907E-0001, -3.97513999999774E-0001, -8.63047999999935E-0002, 2.77078999999958E-0001, 4.77041000000099E-0001, 3.90680999999859E-0001, 6.61678999999822E-0002, -3.00528000000213E-0001, -4.82100000000173E-0001, -3.61706000000140E-0001, -1.12785000000031E-0002, 3.47796000000017E-0001, 4.82912999999826E-0001, 3.02329000000100E-0001, -7.90537999999970E-0002, -4.09227999999985E-0001, -4.64552999999796E-0001, -2.03408999999965E-0001, 1.98789999999917E-0001, 4.64230000000043E-0001, 4.04536999999891E-0001, 5.75837999999749E-0002, -3.32002000000102E-0001, -4.82986999999866E-0001, -2.81882000000223E-0001, 1.27741000000015E-0001, 4.43717999999990E-0001, 4.27357000000029E-0001, 8.67075999999543E-0002, -3.21608000000197E-0001, -4.82884000000013E-0001, -2.68137000000024E-0001, 1.58376000000089E-0001, 4.60480000000189E-0001, 3.94394999999804E-0001, 8.97358999999653E-0003, -3.85126000000128E-0001, -4.63401999999860E-0001, -1.56926999999996E-0001, 2.82047000000148E-0001, 4.83843999999863E-0001, 2.73432999999841E-0001, -1.73019000000068E-0001, -4.70732000000226E-0001, -3.57265000000098E-0001, 7.19904999999699E-0002, 4.38375000000178E-0001, 4.12060999999994E-0001, 1.23822000000047E-0002, -3.99836000000050E-0001, -4.45055000000139E-0001, -7.71581999999853E-0002, 3.63976000000093E-0001, 4.62701999999808E-0001, 1.21280999999954E-0001, -3.37500999999975E-0001, -4.71152000000075E-0001, -1.45841000000019E-0001, 3.23628000000099E-0001, 4.73699000000124E-0001, 1.51135999999951E-0001, -3.24419000000034E-0001, -4.72220000000107E-0001, -1.37774000000036E-0001, 3.39390999999978E-0001, 4.65411000000131E-0001, 1.04878999999983E-0001, -3.67131999999856E-0001, -4.50452000000041E-0001, -5.20331000000169E-0002, 4.03378999999859E-0001, 4.21691999999894E-0001, -2.14152000000070E-0002, -4.41976000000068E-0001, -3.72757999999976E-0001, 1.13511000000017E-0001, 4.72905000000083E-0001, 2.96350999999959E-0001, -2.19368000000031E-0001, -4.83674999999948E-0001, -1.87777999999980E-0001, 3.27948999999990E-0001, 4.59028000000217E-0001, 4.67350000000124E-0002, -4.22157999999854E-0001, -3.85088999999880E-0001, 1.17635999999948E-0001, 4.78071999999884E-0001, 2.53111999999874E-0001, -2.84720999999990E-0001, -4.69815999999810E-0001, -6.75424999999450E-0002, 4.20501000000058E-0001, 3.76236999999946E-0001, -1.48369000000002E-0001, -4.83322999999928E-0001, -1.93813000000091E-0001, 3.48465000000033E-0001, 4.35011999999915E-0001, -5.20594999999844E-0002, -4.70860999999786E-0001, -2.61331000000155E-0001, 2.99838999999793E-0001, 4.56122000000050E-0001, -8.47457000000418E-0003, -4.62175000000116E-0001, -2.79306999999790E-0001, 2.91294999999991E-0001, 4.55973999999969E-0001, -1.96689999999933E-0002, -4.68189000000166E-0001, -2.51569000000018E-0001, 3.25217999999950E-0001, 4.34597999999824E-0001, -8.52701999999681E-0002, -4.81628999999884E-0001, -1.72663999999941E-0001, 3.91235999999935E-0001, 3.75564999999824E-0001, -2.00489999999945E-0001, -4.76438000000144E-0001, -3.35570999999959E-0002, 4.60794000000078E-0001, 2.51975000000130E-0001, -3.44184000000041E-0001, -4.09337999999934E-0001, 1.61409999999933E-0001, 4.80509999999867E-0001, 4.47993000000224E-0002, -4.62160999999924E-0001, -2.35175999999910E-0001, 3.67765999999847E-0001, 3.80204000000049E-0001, -2.21663999999919E-0001, -4.63874000000033E-0001, 5.13983000000167E-0002, 4.82525999999780E-0001, 1.17171999999982E-0001, -4.42914000000201E-0001, -2.64184000000114E-0001, 3.57844999999998E-0001, 3.76622000000225E-0001, -2.43244999999888E-0001, -4.48910999999953E-0001, 1.14519999999970E-0001, 4.81073999999808E-0001, 1.47939000000008E-0002, -4.77703000000020E-0001, -1.34743000000071E-0001, 4.45565999999872E-0001, 2.38581000000067E-0001, -3.92727999999806E-0001, -3.23120999999901E-0001, 3.26771999999892E-0001, 3.87517000000116E-0001, -2.54672999999912E-0001, -4.33148000000074E-0001, 1.81812000000036E-0001, 4.62328000000070E-0001, -1.12458999999944E-0001, -4.78289000000132E-0001, 4.92869999999925E-0002, 4.84148000000005E-0001, 5.86423000000025E-0003, -4.83146999999917E-0001, -5.22940999999832E-0002, 4.77914999999939E-0001, 8.96437999999762E-0002, -4.70905000000130E-0001, -1.18200000000002E-0001, 4.63843999999881E-0001, 1.38171000000057E-0001, -4.58263999999872E-0001, -1.50045999999975E-0001, 4.55015999999887E-0001, 1.53954999999996E-0001, -4.54807999999957E-0001, -1.50088000000096E-0001, 4.57700999999815E-0001, 1.38134999999920E-0001, -4.63569999999891E-0001, -1.17764999999963E-0001, 4.71551999999974E-0001, 8.81894999999986E-0002, -4.80403000000024E-0001, -4.87550000000283E-0002, 4.87923000000137E-0001, -1.28841999999985E-0003, -4.91308000000117E-0001, 6.19993000000250E-0002, 4.86694000000170E-0001, -1.32732999999917E-0001, -4.69751999999971E-0001, 2.11195000000089E-0001, 4.35657999999876E-0001, -2.93380999999954E-0001, -3.80208000000039E-0001, 3.72847999999976E-0001, 3.00403000000188E-0001, -4.41198999999870E-0001, -1.96052999999893E-0001, 4.88281999999799E-0001, 7.05491999999595E-0002, -5.03888000000188E-0001, 6.79192000000057E-0002, 4.79287999999997E-0001, -2.06691999999975E-0001, -4.09948999999870E-0001, 3.29295000000002E-0001, 2.97392999999829E-0001, -4.18125000000146E-0001, -1.51035000000093E-0001, 4.57456999999977E-0001, -1.21829999999932E-0002, -4.37680000000000E-0001, 1.69366000000082E-0001, 3.58297000000221E-0001, -2.95907000000170E-0001, -2.30043000000023E-0001, 3.70323999999982E-0001, 7.37844000000223E-0002, -3.79878000000190E-0001, 8.26819999999771E-0002, 3.24047000000064E-0001, -2.10759999999937E-0001, -2.15877000000091E-0001, 2.87343999999848E-0001, 7.92186999999558E-0002, -3.00759000000198E-0001, 5.63948000000210E-0002, 2.53298000000086E-0001, -1.63127999999915E-0001, -1.60753000000113E-0001, 2.21193999999969E-0001, 4.78082000000200E-0002, -2.23539999999957E-0001, 5.81391000000053E-0002, 1.76492000000053E-0001, -1.34119999999939E-0001, -9.74677000000383E-0002, 1.66602999999895E-0001, 9.46428999999682E-0003, -1.54275999999982E-0001, 6.49469999999610E-0002, 1.06955999999968E-0001, -1.09595000000013E-0001, -4.21787000000222E-0002, 1.18017000000009E-0001, -2.05329000000063E-0002, -9.43591999999853E-0002, 6.49757999999565E-0002, 5.07000999999718E-0002, -8.26296000000184E-0002, -2.79875000000018E-0003, 7.37881000000016E-0002, -3.52417999999943E-0002, -4.63371999999822E-0002, 5.46148000000244E-0002, 1.19423999999952E-0002, -5.37128000000280E-0002, 1.78473999999937E-0002, 3.70907000000216E-0002, -3.52100999999720E-0002, -1.32136000000003E-0002, 3.75063999999838E-0002, -8.99504000000206E-0003, -2.73909000000003E-0002, 2.27873000000045E-0002, 1.08742000000035E-0002, -2.56093000000135E-0002, 5.03203000000241E-0003, 1.90342999999871E-0002, -1.51086999999990E-0002, -7.63514999999870E-0003, 1.72402000000034E-0002, -3.46168000000091E-0003, -1.26917999999989E-0002, 1.03507000000036E-0002, 4.70225000000113E-0003, -1.16266999999937E-0002, 2.92620999999826E-0003, 8.12958999999580E-0003, -7.45025999999882E-0003, -2.37827999999851E-0003, 7.79329000000217E-0003, -2.83968999999828E-0003, -4.85918999999768E-0003, 5.45333000000170E-0003, 6.50574999999876E-0004, -5.05228000000102E-0003, 2.66608000000090E-0003, 2.55376999999868E-0003, -3.93425000000036E-0003, 3.76394999999974E-0004, 3.09312999999989E-0003, -2.39051000000146E-0003, -1.06363999999992E-0003, 2.75651000000110E-0003, -9.89122000000009E-0004, -1.69909999999973E-0003, 2.01841000000158E-0003, 2.12075999999894E-0005, -1.75568000000048E-0003, 1.21378999999955E-0003, 6.16308999999760E-0004, -1.49133999999940E-0003, 5.30142999999761E-0004, 8.80323999999710E-0004, -1.11185999999996E-0003, 0.00000000000000E+0000 }; Double g_pdGCRSignal1_47[GCR_SIZE] = { 0.0, //TODO - Fix this if we re-enable GCR usage }; Double g_pdGCRSignal1_4[GCR_SIZE] = { -0.00347677, -0.00322337, -0.00325605, -0.00322112, -0.00321719, -0.00320921, -0.00318706, -0.0031813, -0.00316357, -0.0031458, -0.0031343, -0.00311177, -0.00309477, -0.00307598, -0.00305072, -0.00303091, -0.00300454, -0.00297671, -0.00295068, -0.00291709, -0.00288493, -0.00284988, -0.00280893, -0.00276923, -0.00272296, -0.00267316, -0.00262177, -0.00256212, -0.00250011, -0.00243231, -0.00235619, -0.00227647, -0.00218688, -0.00208914, -0.00198407, -0.00186586, -0.00173831, -0.00159776, -0.00144136, -0.00127163, -0.00108236, -0.000873557, -0.000644226, -0.000387946, -0.000105821, 0.00020736, 0.000556413, 0.000941966, 0.00137233, 0.00185082, 0.00238212, 0.00297575, 0.00363537, 0.00437036, 0.00519034, 0.00610175, 0.00711803, 0.00824878, 0.00950516, 0.0109028, 0.0124523, 0.0141696, 0.0160703, 0.0181666, 0.0204768, 0.0230134, 0.0257895, 0.0288187, 0.0321075, 0.0356624, 0.0394846, 0.0435661, 0.047895, 0.0524468, 0.0571852, 0.0620638, 0.0670147, 0.0719563, 0.0767863, 0.0813785, 0.0855895, 0.0892493, 0.0921681, 0.0941404, 0.0949423, 0.0943457, 0.0921242, 0.0880623, 0.0819759, 0.0737222, 0.0632224, 0.050484, 0.0356143, 0.018848, 0.000557361, -0.0187374, -0.0383613, -0.0575002, -0.07522, -0.0905034, -0.102312, -0.109649, -0.111652, -0.107688, -0.0974488, -0.0810454, -0.0590807, -0.0326872, -0.00352681, 0.026276, 0.0542588, 0.0778138, 0.0944444, 0.102052, 0.0992456, 0.0856211, 0.0619632, 0.0303378, -0.0059827, -0.0427809, -0.0753343, -0.0990286, -0.110051, -0.106085, -0.0868866, -0.0545886, -0.013663, 0.0295587, 0.0677864, 0.0939929, 0.102784, 0.0916517, 0.0618112, 0.0183872, -0.030241, -0.0739181, -0.102805, -0.109703, -0.0920499, -0.0530257, -0.00135532, 0.0503947, 0.0887573, 0.102941, 0.088103, 0.0472938, -0.00868865, -0.0638268, -0.101342, -0.108907, -0.0830246, -0.0309566, 0.0308085, 0.081466, 0.102895, 0.0864348, 0.0368566, -0.0283508, -0.0847231, -0.10992, -0.0928818, -0.0392512, 0.0297409, 0.0852003, 0.102632, 0.0731864, 0.00880831, -0.0618224, -0.105668, -0.100953, -0.0486301, 0.0265581, 0.0870846, 0.101275, 0.060433, -0.0149953, -0.0847433, -0.109925, -0.0751765, 0.00101709, 0.0750949, 0.102915, 0.0665191, -0.0132255, -0.0874393, -0.108785, -0.062238, 0.0231743, 0.091318, 0.0955987, 0.0316556, -0.0572763, -0.108566, -0.0844189, -0.000913411, 0.0811435, 0.0998372, 0.0395636, -0.0544801, -0.108895, -0.0794151, 0.0117194, 0.0908163, 0.0917383, 0.0122175, -0.0811607, -0.107599, -0.0426033, 0.0575538, 0.103033, 0.0512332, -0.051541, -0.109666, -0.0670796, 0.0369271, 0.101949, 0.0631155, -0.0424742, -0.109048, -0.0677645, 0.0403094, 0.102837, 0.0527074, -0.057985, -0.109862, -0.0449822, 0.0662633, 0.0994283, 0.0156217, -0.0904555, -0.0957237, 0.00746482, 0.0979505, 0.0667789, -0.0498718, -0.109913, -0.0380423, 0.0773721, 0.090618, -0.0166247, -0.107162, -0.0624203, 0.0602526, 0.0982112, -0.000749544, -0.103676, -0.0691827, 0.0565363, 0.09844, -0.00374564, -0.105781, -0.0605572, 0.0678068, 0.0916978, -0.0254848, -0.109974, -0.0338235, 0.0886854, 0.0696882, -0.0628569, -0.102695, 0.0140484, 0.103037, 0.0210051, -0.100828, -0.0637215, 0.072721, 0.0826481, -0.0517958, -0.104731, 0.014821, 0.103036, 0.00681728, -0.107478, -0.0388149, 0.0925919, 0.0522746, -0.0885787, -0.0749478, 0.0695205, 0.0793948, -0.0647939, -0.0942172, 0.047579, 0.0923407, -0.0464317, -0.102366, 0.033887, 0.0969906, -0.0380317, -0.104437, 0.031087, 0.0969579, -0.040917, -0.102302, 0.0395639, 0.0922497, -0.0546754, -0.0940965, 0.0579833, 0.0792467, -0.0766651, -0.074754, 0.0818012, 0.0520243, -0.0997399, -0.0384754, 0.100644, 0.00639079, -0.110028, 0.0153267, 0.0976144, -0.0523014, -0.0892546, 0.0731407, 0.0561963, -0.101035, -0.0273019, 0.10298, -0.0216741, -0.102356, 0.0564077, 0.0691551, -0.0959579, -0.033156, 0.103057, -0.0261942, -0.0984214, 0.0684994, 0.0529683, -0.106231, -0.00223094, 0.0983575, -0.0646024, -0.0682985, 0.097313, -0.00218509, -0.104729, 0.0614701, 0.0539863, -0.1072, 0.0111803, 0.0893852, -0.084751, -0.0365265, 0.102008, -0.0503339, -0.0726031, 0.0973383, -0.0146527, -0.0952283, 0.0831772, 0.0159482, -0.106912, 0.0664624, 0.0389454, -0.111695, 0.0521044, 0.0540878, -0.113279, 0.043042, 0.0618967, -0.114071, 0.0406873, 0.0626912, -0.114795, 0.0452189, 0.0562661, -0.114465, 0.0555428, 0.0422166, -0.110703, 0.069055, 0.0207763, -0.100524, 0.0816043, -0.00616025, -0.0816649, 0.0881263, -0.0344248, -0.0541472, 0.0841699, -0.0580354, -0.0212963, 0.0678992, -0.0709826, 0.0106367, 0.0415202, -0.0697234, 0.0345279, 0.0110249, -0.0550094, 0.0452444, -0.0159388, -0.0318536, 0.0417614, -0.0330188, -0.00759705, 0.0274425, -0.0374128, 0.0108287, 0.0085779, -0.0305831, 0.0192502, -0.00799957, -0.0174139, 0.0175002, -0.0174261, -0.00409323, 0.00907835, -0.0184854, 0.0045466, -0.000846416, -0.0134708, 0.00677922, -0.00796784, -0.00638725, 0.00390153, -0.0103627, -0.0008869, -0.0010817, -0.00865121, 0.00113132, -0.00511521, -0.00515852, 1.14208e-05, -0.00659004, -0.00231654, -0.00244116, -0.00577361, -0.00133297, -0.00441284, -0.00400062, -0.00202788, -0.00497207, -0.00267527, -0.0033336, -0.00432329, -0.00246436, -0.00415329, -0.00339386, -0.00304039, -0.00414497, -0.00292314, -0.00369477, -0.00363559, -0.00308028, -0.00390425, -0.00320941, -0.00350326, -0.00366609, -0.00319138, -0.0037218, -0.00347683 }; Double g_pdGCRSignal1[MAX_NABTS_SAMPLES_PER_LINE]; /* used for resampling */ int g_nNabtsGcrSize = GCR_SIZE; /* This array is filled with the inverted GCR waveform. (in FillGCRSignals()) */ Double g_pdGCRSignal2[MAX_NABTS_SAMPLES_PER_LINE]; /* The EqualizeMatch structures for positive GCR, negative GCR, and NABTS sync. Each structure contains the appropriate waveform for matching, its size, its sample rate (actually, individual sample period in units of the 5*NABTS bit rate), and the minimum and maximum samples among which the signal may be matched in the VBI samples. */ EqualizeMatch eqmatchGCR1 = { GCR_SIZE, GCR_SAMPLE_RATE, GCR_START_DETECT, GCR_END_DETECT, g_pdGCRSignal1 }; EqualizeMatch eqmatchGCR2 = { GCR_SIZE, GCR_SAMPLE_RATE, GCR_START_DETECT, GCR_END_DETECT, g_pdGCRSignal2 }; EqualizeMatch eqmatchNabtsSync = { NABSYNC_SIZE, /* changes */ NABSYNC_SAMPLE_RATE, /* doesn't change */ NABSYNC_START_DETECT, /* changes */ NABSYNC_END_DETECT, /* changes */ g_pdSync5 /* changes */ }; int pnGCRPositiveIndices0[]= { 192, 194, 196, 198, 200, 202, 204, 206, 208, 210, 212, 214, 216, 218, 220, 222, 278, 280, 282, 284, 286, 288, 322, 324, 326, 328 }; /* Some sample locations at which a "positive" GCR signal should be quite negative */ int pnGCRNegativeIndices0[]= { 246, 248, 250, 252, 254, 256, 258, 260, 262, 302, 304, 306, 308, 310, 338, 340, 342, 344, 346 }; int pnGCRPositiveIndices[26]; int pnGCRNegativeIndices[19]; /*******************/ /*** End Globals ***/ /*******************/ /*************************************/ /*** Begin inline helper functions ***/ /*************************************/ /* Inline helper functions for computing min, max, absolute value */ inline Double dmin(Double x, Double y) { return xy?x:y; } inline Double dmax3(Double x, Double y, Double z) { return dmax(x,dmax(y,z)); } inline int imax(int x, int y) { return x>y?x:y; } inline int imax3(int x, int y, int z) { return imax(x,imax(y,z)); } inline int iabs(int x) { return x>0?x:-x; } /* idivceil returns a/b, rouding towards positive infinity */ /* idivfloor returns a/b, rounding towards negative infinity */ /* idivceil and idivfloor are somewhat disgusting because C does not specify rounding behavior with division using negative numbers (!). */ inline int idivceil(int a, int b) { int sgn= 0; if (a<0) { a= (-a); sgn ^= 1; } if (b<0) { b= (-b); sgn ^= 1; } if (!sgn) { /* Answer will be positive so round away from zero */ return (a+b-1)/b; } else { /* Answer will be negative so round towards zero */ return -(a/b); } } inline int idivfloor(int a, int b) { int sgn= 0; if (a<0) { a= (-a); sgn ^= 1; } if (b<0) { b= (-b); sgn ^= 1; } if (!sgn) { /* Answer will be positive so round towards zero */ return a/b; } else { /* Answer will be negative so round away from zero */ return -((a+b-1)/b); } } /***********************************/ /*** End inline helper functions ***/ /***********************************/ /* Our own ASSERT macro */ #ifdef DEBUG // Raises X to the Yth power static long power(long x, long y) { long rval; long i; rval = x; for (i = 2; i <= y; ++i) rval *= x; return (rval); } static char *flPrintf(Double dNum, int nPrec) { static char rbufs[4][50]; static short rbuf = 0; long lFix, lNum, lFrac; char *rval = rbufs[rbuf++]; char *ep; lFix = float2long(dNum * power(10, nPrec)); lNum = lFix / power(10, nPrec); lFrac = lFix % power(10, nPrec); if (lFrac < 0) lFrac = -lFrac; sprintf(rval, "%ld.%0*ld", lNum, nPrec, lFrac); for (ep = rval; *ep; ++ep) ; for ( ; ep > rval && ep[-1] != '.'; --ep) { if (*ep == '0') *ep = '\0'; } return (rval); } #endif //DEBUG /* Fill the negative GCR signal, and normalize the various EqualizeMatch signals */ void NDSPInitGlobals() { if (g_bNDSPGlobalsInitted) return; memcpy(g_pdGCRSignal1, g_pdGCRSignal1_5, GCR_SIZE*sizeof(Double)); FillGCRSignals(); memcpy(g_pdSync, g_pdSync5, NABSYNC_SIZE*sizeof(Double)); NormalizeSyncSignal(); memcpy(pnGCRPositiveIndices, pnGCRPositiveIndices0, 26*sizeof(int)); memcpy(pnGCRNegativeIndices, pnGCRNegativeIndices0, 19*sizeof(int)); g_bNDSPGlobalsInitted= 1; } int NDSPStateSetSampleRate(NDSPState* pState, unsigned long newRate) { double newMultiple; unsigned long oldRate; debug_printf(("NDSPStateSetSampleRate(0x%p, %lu) entered\n", pState, newRate)); newMultiple = (double)newRate / KS_VBIDATARATE_NABTS; // (28636360 / 5727272.0) = 5.0 switch (newRate) { case KS_VBISAMPLINGRATE_4X_NABTS: // 4X NABTS g_filterDefault = &g_filterDefault4; break; case KS_VBISAMPLINGRATE_47X_NABTS: // 4.7X NABTS g_filterDefault = &g_filterDefault47; break; case KS_VBISAMPLINGRATE_5X_NABTS: // 5X NABTS g_filterDefault = &g_filterDefault5; break; /* ===== add cases here as necessary ===== */ default: debug_printf(("Unsupported sample rate: %luhz\n", newRate)); return NDSP_ERROR_UNSUPPORTED_SAMPLING_RATE; } NDSPStateSetFilter(pState, g_filterDefault, sizeof(*g_filterDefault)); pState->dSampleRate = newMultiple; oldRate = float2long(pState->dSampleFreq); pState->dSampleFreq = newRate; switch (newRate) { case KS_VBISAMPLINGRATE_4X_NABTS: ClearVariableFIRFilter(&pState->filterGCREqualizeTemplate); AddToVariableFIRFilter(&pState->filterGCREqualizeTemplate, 0, 11, 9, 2); break; case KS_VBISAMPLINGRATE_47X_NABTS: ClearVariableFIRFilter(&pState->filterGCREqualizeTemplate); AddToVariableFIRFilter(&pState->filterGCREqualizeTemplate, 0, 11, 9, 2); //FIXME break; case KS_VBISAMPLINGRATE_5X_NABTS: break; /* ===== add cases here as necessary ===== */ default: // Unknown sample rate EASSERT(0); break; } /* Last but not least, recompute for the new rate */ NDSPComputeNewSampleRate(newRate, oldRate); return 0; } /* Create a new "DSP state". A separate DSP state should be maintained for each separate simultaneous source to the DSP. NDSPStartRetrain() is implicitly called upon creation of a new state. Public function: also see nabtsapi.h. If memory is passed, use it for storing the state rather than mallocing a new state */ NDSPState *NDSPStateNew(void *memory) { NDSPState *pState; /* Be sure that the globals are initialized */ NDSPInitGlobals(); pState= memory ? ((NDSPState *)memory) : alloc_mem(sizeof(NDSPState)); if (!pState) return NULL; memset(pState, 0, sizeof(NDSPState)); /* Magic number to help identify an NDSPState object */ pState->uMagic= NDSP_STATE_MAGIC; pState->nRetrainState= 0; pState->nUsingGCR= 0; pState->bUsingScratch1= FALSE; pState->bUsingScratch2= FALSE; pState->bUsingScratch3= FALSE; pState->bUsingScratch4= FALSE; pState->bUsingScratch5= FALSE; pState->bFreeStateMem = (memory == NULL); pState->SamplesPerLine= 1600; // Default from Bt829 driver /* Reset the filter to be the "fallback" filter */ NDSPResetFilter(pState); /* Set the equalization "template" filters. (These filters have the correct tap locations, but the tap values are ignored) */ ClearVariableFIRFilter(&pState->filterGCREqualizeTemplate); AddToVariableFIRFilter(&pState->filterGCREqualizeTemplate, 0, 11, 9, 3); ClearVariableFIRFilter(&pState->filterNabsyncEqualizeTemplate); AddToVariableFIRFilter(&pState->filterNabsyncEqualizeTemplate, 0, 2, 2, 5); NDSPStateSetSampleRate(pState, KS_VBISAMPLINGRATE_5X_NABTS); return pState; } /* Destroys the DSP state. Automatically disconnects from any FEC state. Returns 0 on success. Returns NDSP_ERROR_ILLEGAL_NDSP_STATE if illegal state. Public function: also see nabtsapi.h */ int NDSPStateDestroy(NDSPState *pState) { if (!pState || pState->uMagic != NDSP_STATE_MAGIC) return NDSP_ERROR_ILLEGAL_NDSP_STATE; if (pState->bFreeStateMem) free_mem(pState); return 0; } /* Connect the given NFECState and NDSPState For cases where the NDSP and NFEC modules are connected, giving pointers to the connected state may result in increased robustness and efficiency. Note that only one of NDSPStateConnectToFEC or NFECStateConnectToDSP need be called to connect the two states. (Calling both is OK). Returns 0 on success. Returns NDSP_ERROR_ILLEGAL_NDSP_STATE if illegal DSP state. Public function: also see nabtsapi.h This function currently does nothing, but the API is in place for future algorithms that could potentially make use of combined DSP and FEC knowledge */ int NDSPStateConnectToFEC(NDSPState *pDSPState, NFECState *pFECState) { if (!pDSPState || pDSPState->uMagic != NDSP_STATE_MAGIC) return NDSP_ERROR_ILLEGAL_NDSP_STATE; return 0; } /* Tells the DSP to initiate a "fast retrain". This is useful if you suspect that conditions have changed sufficiently to be worth spending significant CPU to quickly train on a signal. This should be called when the source of video changes. Returns 0 on success. Returns NDSP_ERROR_ILLEGAL_NDSP_STATE if illegal DSP state. Public function: also see nabtsapi.h */ int NDSPStartRetrain(NDSPState *pState) { if (!pState || pState->uMagic != NDSP_STATE_MAGIC) return NDSP_ERROR_ILLEGAL_NDSP_STATE; /* Reset nRetrainState to zero. This initiates a retrain. See NDSPProcessGCRLine for more information */ pState->nRetrainState= 0; return 0; } /* Resets the DSP filter back to the "fallback" fixed filter. This normally doesn't need to be called, but might be useful for performance analysis. */ int NDSPResetFilter(NDSPState *pState) { if (!pState || pState->uMagic != NDSP_STATE_MAGIC) return NDSP_ERROR_ILLEGAL_NDSP_STATE; NDSPStateSetFilter(pState, g_filterDefault, sizeof(*g_filterDefault)); pState->nRetrainState= 0; return 0; } /* Assign an array of Double from an array of unsigned char */ void Copy_d_8(Double *pfOutput, unsigned char *pbInput, int nLen) { int i; for (i= 0; i< nLen; i++) pfOutput[i]= pbInput[i]; } /* The general strategy for convolution is to "slide" one array across another and multiple the overlapping portions like so: Convolve A and B by sliding B across A. -- j \ dest[i]= / a[j*a_step+i]*b[j*b_step] -- For Convolve_d_d_filt_var, we are convolving and array of Double with a variable-tap filter. A variable-tap filter is simply a sparse representation for an array */ int debug_convolve= 0; void Convolve_d_d_filt_var(NDSPState *pState, Double *dest, int dest_begin, int dest_end, Double *a, int a_begin, int a_end, FIRFilter *pFilt, BOOL bSubtractMean) { int maxTap = pFilt->nMaxTap; /* positive value */ int minTap = pFilt->nMinTap; /* negative value */ int b_len = maxTap-minTap+1; Double *b; int i; /* Set b to a scratch buffer from the NDSPState. Mark the scratch buffer as used. Offset b by "minTap" (which is negative), so that array references as low as "minTap" can be handled. */ SASSERT(!pState->bUsingScratch7); SASSERT(sizeof(pState->pdScratch7)/sizeof(Double) >= b_len); b = pState->pdScratch7 - minTap; pState->bUsingScratch7 = __LINE__; /* The strategy here is to generate a non-sparse representation for the filter, and call the standard convolution routine with it. */ for (i = minTap; i <= maxTap; i++) { b[i] = 0; } for (i = 0; i < pFilt->nTaps; i++) { b[pFilt->pnTapLocs[i]] = pFilt->pdTaps[i]; } Convolve_d_d_d(dest, dest_begin, dest_end, a, a_begin, a_end, 1, b, minTap, maxTap, 1, bSubtractMean); /* Mark Scratch7 as free */ pState->bUsingScratch7 = 0; } /* Like Convolve_d_d_filt_var above, but is implemented to work only for fixed-tap filters. This improves performance. */ void Convolve_d_d_filt(Double *dest, int dest_begin, int dest_end, Double *a, int a_begin, int a_end, FIRFilter *pFilt, BOOL bSubtractMean) { Double *b= pFilt->pdTaps+(pFilt->nTaps-1)/2; Double b_begin= -(pFilt->nTaps-1)/2; Double b_end= (pFilt->nTaps-1)/2; Double a_step= pFilt->dTapSpacing; Double b_step= 1.0; SASSERT(pFilt->nTaps > 0 && (pFilt->nTaps % 2) == 1); /* Simply call the standard convolution routine, taking into account the fixed spacing of the filter */ Convolve_d_d_d(dest, dest_begin, dest_end, a, a_begin, a_end, a_step, b, b_begin, b_end, b_step, bSubtractMean); } /* Convolve_d_d_d convolves two input arrays and places the result in a single output array. The general strategy for convolution is to "slide" one array across another and multiple the overlapping portions like so: Convolve A and B by sliding B across A. -- j \ dest[i]= / a[j*a_step+i]*b[j*b_step] -- Convolve_d_d_d complicates matters somewhat by allowing: 1) Arbitrary begin and end indices for the destination array. 2) Arbitrary begin and end indices for A and B (Any elements required by the convolution that fall outside these ranges are taken to be zero) 3) Arbitrary spacing between the array elements of A and B, allowing for a fixed-spacing sparse array to be convolved efficiently. 4) The ability to subtract the mean from one of the arguments prior to convolution. Note that the arbitrary begin and end indices can be negative if appropriate. */ void Convolve_d_d_d(Double *dest, int dest_begin, int dest_end, Double *a, Double a_begin, Double a_end, Double a_step, Double *b, Double b_begin, Double b_end, Double b_step, BOOL bSubtractMean) { Double dSum; int i; Double j; ASSERT( a_step > 0.0 && b_step > 0.0 ); // This should never happen! if ( a_step > 0 && b_step > 0 ) // To fix a PREFIX tool divide-by-zero whine { for (i= dest_begin; i <= dest_end; i++) { Double j_begin, j_end; Double a_index, b_index; Double a_interpolated, b_interpolated; /* a_begin <= j*a_step+i <= a_end */ /* b_begin <= j*b_step <= b_end */ /* or */ /* ceil((a_begin-i)/a_step) <= j <= floor((a_end-i)/a_step) */ /* ceil(b_begin/b_step) <= j <= floor(b_end/b_step) */ j_begin= max( (a_begin-i) / a_step, (b_begin) / b_step ); SASSERT((a_begin <= j_begin*a_step+i && b_begin <= j_begin*b_step)); SASSERT(!(a_begin <= (j_begin-1)*a_step+i && b_begin <= (j_begin-1)*b_step)); j_end = min( (a_end-i) / a_step, (b_end) / b_step ); SASSERT((j_end*a_step+i <= a_end && j_end*b_step <= b_end)); if (debug_convolve) { debug_printf(("(%d,%s-%s) ", i, flPrintf(j_begin,6), flPrintf(j_end,6))); } dSum= 0.0; if (j_end >= j_begin) { Double dMean; a_index= j_begin*a_step+i; b_index= j_begin*b_step; dMean= 0.0; if (bSubtractMean) { for(j= j_begin; j <= j_end; j++) { SASSERT(b_begin <= b_index && b_index <= b_end); b_interpolated = _InterpDoubleArr(b, b_index); dMean += b_interpolated; b_index += b_step; } dMean /= (j_end - j_begin + 1); } a_index= j_begin*a_step+i; b_index= j_begin*b_step; for(j= j_begin; j <= j_end; j++) { SASSERT(a_begin <= a_index && a_index <= a_end); SASSERT(b_begin <= b_index && b_index <= b_end); a_interpolated = _InterpDoubleArr(a,a_index); b_interpolated = _InterpDoubleArr(b,b_index); dSum += a_interpolated*(b_interpolated-dMean); a_index += a_step; b_index += b_step; } } dest[i]= dSum; } } } /* Determine the minimum and maximum tap locations for a variable-tap filter */ void RecalcVariableFIRFilterBounds(FIRFilter *pFilter) { int i; int nMinTap= +NDSP_MAX_FIR_TAIL; int nMaxTap= -NDSP_MAX_FIR_TAIL; SASSERT(pFilter->bVariableTaps); for (i= 0; i< pFilter->nTaps; i++) { nMinTap= imin(nMinTap, pFilter->pnTapLocs[i]); nMaxTap= imax(nMaxTap, pFilter->pnTapLocs[i]); } if (pFilter->nTaps) { pFilter->nMinTap= nMinTap; pFilter->nMaxTap= nMaxTap; } else { pFilter->nMinTap= 0; pFilter->nMaxTap= 0; } } /* Adds one or more taps to a variable FIR filter. One tap is added at nCenterPos. Additional taps are added before or after nCenterPos depending on the number of left taps and right taps requested. To be more precise, taps are added at all: nCenterPos + i * nTapSpacing where i is an integer in the range from (-nLeftTaps) to nRightTaps, inclusive. Will call RecalcVariableFIRFilterBounds to recalculate filter bounds in case the tap was added outside previous bounds */ void AddToVariableFIRFilter(FIRFilter *pFilter, int nCenterPos, int nLeftTaps, int nRightTaps, int nTapSpacing) { int i; int nCurrentTap= pFilter->nTaps; pFilter->bVariableTaps= TRUE; /* debug_printf(("\nADD %lx [+%d] l=%d r=%d spacing=%d\n", (long)pFilter, nCenterPos, nLeftTaps, nRightTaps, nTapSpacing)); */ for (i= -nLeftTaps; i <= nRightTaps; i++) { int nCurrentTapLoc= nCenterPos + i*nTapSpacing; SASSERT(nCurrentTap < NDSP_MAX_FIR_COEFFS); SASSERT(iabs(nCurrentTapLoc) <= NDSP_MAX_FIR_TAIL); pFilter->pnTapLocs[nCurrentTap]= nCurrentTapLoc; pFilter->pdTaps[nCurrentTap]= 0.0; nCurrentTap++; } pFilter->nTaps= nCurrentTap; RecalcVariableFIRFilterBounds(pFilter); } /* Removes all taps from a variable FIR filter */ void ClearVariableFIRFilter(FIRFilter *pFilter) { /*debug_printf(("\nCLEAR %lx\n", (long)pFilter));*/ pFilter->bVariableTaps= TRUE; pFilter->nTaps= 0; RecalcVariableFIRFilterBounds(pFilter); } /* Copies a FIRFilter */ void CopyFIRFilter(FIRFilter *pfilterDest, FIRFilter *pfilterSrc) { memcpy(pfilterDest, pfilterSrc, sizeof(FIRFilter)); } /* Attempt to perform equalization and long ghost cancellation from GCR in the state "pState". Assumes that pState->psmPosGCRs[0] has been filled during by NDSPProcessGCRLine during retrain states (-g_nNabtsRetrainDuration) to 0. (see NDSPProcessGCRLine for more information). Return 0 if success. Return error status if failure. */ int DoEqualizeFromGCRs(NDSPState *pState) { /* If the signal doesn't look enough like a GCR signal, fail and say we didn't detect the GCR */ if (NDSPDetectGCRConfidence(pState->psmPosGCRs[0].pbSamples+ pState->psmPosGCRs[0].nOffset) < 50) { return NDSP_ERROR_NO_GCR; } return DoGCREqualFromSignal( pState, &pState->psmPosGCRs[0], &eqmatchGCR1, &pState->filterGCREqualizeTemplate, g_nNabtsAdditionalTapsGCR); } /* Attempt to perform equalization (WITHOUT long ghost cancellation) from GCR in the state "pState". Assumes that pState->psmSyncs[0] has been filled during by NDSPDecodeLine during retrain states (-g_nNabtsRetrainDuration) to 0. (see NDSPProcessGCRLine for more information). Return 0 if success. Return error status if failure. */ int DoEqualizeFromNabsync(NDSPState *pState) { return DoGCREqualFromSignal( pState, &pState->psmSyncs[0], &eqmatchNabtsSync, &pState->filterNabsyncEqualizeTemplate, 0 /* don't add additional taps for equalizing from nabsync */); } /* Attempt to perform equalization (WITHOUT long ghost cancellation) from given signal and given NDSPSigMatch. After equalization, attempt to add nAddTaps taps to handle long ghosts. This is only appropriate if the sigmatch signal is long enough. Return 0 if success. Return error status if failure. */ int DoGCREqualFromSignal(NDSPState *pState, NDSPSigMatch *sigmatch, EqualizeMatch *eqm, FIRFilter *pfilterTemplate, int nAddTaps) { int i; Double dConv; Double *GCRsignal= eqm->pdSignal; int nMatchSize= eqm->nSignalSize; int nMatchSampleRate= eqm->nSignalSampleRate; Double *pdSamples; int nMaxindex= 0; int nStatus= 0; if (!pState || pState->uMagic != NDSP_STATE_MAGIC) return NDSP_ERROR_ILLEGAL_NDSP_STATE; #ifdef DEBUG debug_printf(("GCRs: ")); for (i= 0; i< NDSP_SIGS_TO_ACQUIRE; i++) { debug_printf(("%s@%d -%s@%d ", flPrintf(pState->psmPosGCRs[i].dMaxval,4), pState->psmPosGCRs[i].nOffset, flPrintf(pState->psmNegGCRs[i].dMaxval,4), pState->psmNegGCRs[i].nOffset)); } debug_printf(("\n")); # if 0 { FILE *out= fopen("foo+", "w"); NDSPAvgGCRs(buf, pState->psmPosGCRs); for (i= 0; i< pState->SamplesPerLine; i++) fprintf(out, "%g\n", buf[i]); fclose(out); } { FILE *out= fopen("foo-", "w"); NDSPAvgGCRs(buf, pState->psmNegGCRs); for (i= 0; i< pState->SamplesPerLine; i++) fprintf(out, "%g\n", buf[i]); fclose(out); } # endif //0 #endif //DEBUG SASSERT(!pState->bUsingScratch1); SASSERT(sizeof(pState->pdScratch1)/sizeof(Double) >= pState->SamplesPerLine); pdSamples= pState->pdScratch1; pState->bUsingScratch1= __LINE__; SASSERT(!pState->bUsingScratch2); SASSERT(sizeof(pState->pdScratch2)/sizeof(Double) >= pState->SamplesPerLine); if (NDSPAvgSigs(pState, pdSamples, sigmatch, &dConv)) { pState->bUsingScratch1= FALSE; return NDSP_ERROR_NO_GCR; } else { debug_printf(("[Conv=%s] ", flPrintf(dConv,4))); } nMaxindex= 20; { int nFirstTap; int nLastTap; Double *pdInput; BOOL bRet; int nTaps; FIRFilter *pfilterGcr= &pState->filterScratch3; Double dSyncVal; CopyFIRFilter(pfilterGcr, pfilterTemplate); SASSERT(pfilterGcr->nTaps > 0); nTaps= pfilterGcr->nTaps; nFirstTap= pfilterGcr->nMinTap; nLastTap= pfilterGcr->nMaxTap; SASSERT(!pState->bUsingScratch2); pdInput= pState->pdScratch2 - nFirstTap; SASSERT(sizeof(pState->pdScratch2)/sizeof(Double) >= nMatchSize*nMatchSampleRate-nFirstTap+nLastTap); pState->bUsingScratch2= TRUE; dSyncVal= Mean_d(pdSamples, 5); for (i= nFirstTap; i< nMatchSize*nMatchSampleRate+nLastTap; i++) { if (i+nMaxindex >= 0 && i+nMaxindex < pState->SamplesPerLine) { pdInput[i]= pdSamples[i+nMaxindex]; } else { /*debug_printf(("zf %d %s ", i, flPrintf(dSyncVal,4)));*/ pdInput[i]= dSyncVal; } } SubtractMean_d(pdInput+nFirstTap, pdInput+nFirstTap, nMatchSize*nMatchSampleRate-nFirstTap+nLastTap); /* Perform equalization step */ bRet= EqualizeVar(pState, pdInput, nFirstTap, nMatchSize*nMatchSampleRate+nLastTap, GCRsignal, nMatchSize, nMatchSampleRate, pfilterGcr); { int minTap = pfilterGcr->nMinTap; int maxTap = pfilterGcr->nMaxTap; /* Attempt to add additional taps (if requested) to handle long ghosts */ while (nAddTaps > 0) { nAddTaps--; if (bRet) { Double *pdFilt; Double *pdConv; int bestTap = -9999; SASSERT(!pState->bUsingScratch8); SASSERT(sizeof(pState->pdScratch8)/sizeof(Double) >= g_nNabtsMaxPreGhost + g_nNabtsMaxPostGhost + 1); pdConv = pState->pdScratch8; pState->bUsingScratch8 = __LINE__; SASSERT(!pState->bUsingScratch6); SASSERT(sizeof(pState->pdScratch6)/sizeof(Double) >= pState->SamplesPerLine); pdFilt = pState->pdScratch6; pState->bUsingScratch6 = __LINE__; /* Filter the input line given the best filter so far */ Convolve_d_d_filt_var(pState, pdFilt, 0, pState->SamplesPerLine-1, pdInput, 0, pState->SamplesPerLine-1, pfilterGcr, TRUE); /* Now convolve the filtered output with the target signal to see if we can see any echos */ Convolve_d_d_d(pdConv + g_nNabtsMaxPreGhost, -g_nNabtsMaxPreGhost, g_nNabtsMaxPostGhost, pdFilt, 0, pState->SamplesPerLine-1, GCR_SAMPLE_RATE, GCRsignal, 0, g_nNabtsGcrSize, 1, TRUE); { Double bestTapVal = -1; /* Look for the strongest echo */ for (i = 0; i < g_nNabtsMaxPreGhost + g_nNabtsMaxPostGhost + 1; i++) { if (fabs(pdConv[i]) > bestTapVal && (i-g_nNabtsMaxPreGhost < minTap || i-g_nNabtsMaxPreGhost > maxTap)) { int j; int tap_ok = 1; for (j = 0; j < pfilterGcr->nTaps; j++) { if (i-g_nNabtsMaxPreGhost == -pfilterGcr->pnTapLocs[j]) { tap_ok = 0; break; } } if (tap_ok) { bestTapVal = fabs(pdConv[i]); bestTap = i-g_nNabtsMaxPreGhost; } } } } debug_printf(("Adding %d to current %d-tap filter\n", -bestTap, pfilterGcr->nTaps)); /* Add a tap to the filter to handle the strongest echo location */ AddToVariableFIRFilter(pfilterGcr, -bestTap, 0, 0, 0); /* Now re-run the equalization given the new tap locations as the prototype */ bRet= EqualizeVar(pState, pdInput, pfilterGcr->nMinTap, nMatchSize*nMatchSampleRate+pfilterGcr->nMaxTap, GCRsignal, nMatchSize, nMatchSampleRate, pfilterGcr); pState->bUsingScratch8 = 0; pState->bUsingScratch6 = 0; } } } if (!bRet) { /* Equalization failed */ nStatus= NDSP_ERROR_NO_GCR; NDSPStateSetFilter(pState, g_filterDefault, sizeof(*g_filterDefault)); pState->nUsingGCR= FALSE; } else { nStatus= 0; NDSPStateSetFilter(pState, pfilterGcr, sizeof(*pfilterGcr)); pState->nUsingGCR= TRUE; } pdInput= NULL; pState->bUsingScratch2= FALSE; } pdSamples= NULL; pState->bUsingScratch1= FALSE; return nStatus; } /* Do we want to use the actual GCR signal for equalization? If FALSE, fall back to only use NABTS sync equalization */ BOOL g_bUseGCR= FALSE; // Can't use GCR for EQ right now due to IP concerns int DoEqualize(NDSPState *pState) { /* Do equalization based on either GCR acquisition, or Nabsync acquisition */ /* Preference is given to GCR, but only if g_bUseGCR is TRUE */ if (g_bUseGCR) { if (0 == DoEqualizeFromGCRs(pState)) { #ifdef DEBUG debug_printf(("GCR equalize ")); print_filter(&pState->filter); debug_printf(("\n")); #endif //DEBUG return 0; } else { debug_printf(("GCR equ failed\n")); } } /* GCR failed or was disabled. Try NABTS sync */ /* toggle default filtering */ if (0 == DoEqualizeFromNabsync(pState)) { #ifdef DEBUG debug_printf(("Nabsync equalize ")); print_filter(&pState->filter); debug_printf(("\n")); #endif //DEBUG return 0; } else { debug_printf(("Nabsync equ failed\n")); } /* All adaptive equalization failed; use default fixed filter */ debug_printf(("Using default filter\n")); NDSPStateSetFilter(pState, g_filterDefault, sizeof(*g_filterDefault)); #ifdef DEBUG print_filter(&(pState->filter)); #endif //DEBUG return 1; } #define NABTS_RATE KS_VBIDATARATE_NABTS #define NABTS_SAMPLE_RATE (NABTS_RATE * 5) #define NABTS_US_PER_CLOCK_TIMES_100000 (1000000000 / (NABTS_SAMPLE_RATE/100)) /* ComputeOffsets takes a VBIINFOHEADER and determines the correct offset into the samples for the DSP code. The sample offset is returned in both pnOffsetData and pnOffsetSamples. Only one is now required; we should remove one of the two arguments at earliest convenience. Our goal is to have the nabts sync start around sample 52, or 1.82 us into the block. NABTS sync is specified to be inserted at 10.48 +- .34 us after leading edge of HSYNC. Therefore, our desired sample start time is 10.48 - 1.82 = 8.66 us after leading edge of HSYNC */ #define DESIRED_START_TIME_US100 866 #if 0 int ComputeOffsets(KS_VBIINFOHEADER *pVBIINFO, int *pnOffsetData, int *pnOffsetSamples) { int nOffsetUS100; int nOffsetData; int nSamples; if (!pVBIINFO) { return NDSP_ERROR_ILLEGAL_VBIINFOHEADER; } nSamples= pVBIINFO->SamplesPerLine; /* Actual start time must be less than or equal to desired start time */ /* This results in a positive offset into the input data */ nOffsetUS100= (DESIRED_START_TIME_US100 - pVBIINFO->ActualLineStartTime); nOffsetData= nOffsetUS100*1000/NABTS_US_PER_CLOCK_TIMES_100000; if (nOffsetData < 0) nOffsetData= 0; if (nOffsetData + 1536 > nSamples) { nOffsetData= nSamples-1536; } nSamples -= nOffsetData; SASSERT(nSamples >= 1536); *pnOffsetData= nOffsetData; *pnOffsetSamples= nOffsetData; return 0; } #endif /* Public function: also see nabtsapi.h. This function is the key to the automatic equalization code. Although its name suggests that it only does GCR-based equalization, this function in fact coordinates all dynamic equalization, including that done on the basis of the NABTS sync pattern. Therefore, be certain to call this once per field even if you set g_bUseGCR to FALSE, disabling GCR-based equalization and ghost cancellation. If this function is never called, the DSP will simply use the default fixed filter. This function maintains the pState->nRetrainState value. This value counts down each field, and determines the current state of the equalization: From g_nNabtsRetrainDelay down to 0: Idle, waiting to start retrain. From 0 down to -g_nNabtsRetrainDuration: Acquire appropriate GCR and NABTS sync signals -g_nNabtsRetrainDuration: Perform equalization and ghost cancellation based on acquired signal. Go back to state g_nNabtsRetrainDelay afterwards. Note that NDSPStartRetrain resets nRetrainState to zero, causing immediate signal acquisition and retrain after g_nNabtsRetrainDuration fields. */ int NDSPProcessGCRLine(NDSPGCRStats *pLineStats, unsigned char *pbSamples, NDSPState *pState, int nFieldNumber, int nLineNumber, KS_VBIINFOHEADER *pVBIINFO) { NDSPGCRStats gcrStats; gcrStats.nSize= sizeof(gcrStats); if (!pState || pState->uMagic != NDSP_STATE_MAGIC) return NDSP_ERROR_ILLEGAL_NDSP_STATE; pState->nRetrainState--; gcrStats.bUsed= FALSE; if (pState->nRetrainState < -g_nNabtsRetrainDuration) { /* Signal acquired; perform equalization/ghost cancellation */ gcrStats.bUsed= TRUE; DoEqualize(pState); pState->nRetrainState= g_nNabtsRetrainDelay; } else if (pState->nRetrainState < 0) { /* Try to find best GCR line for later equalization */ /* NABTS sync is acquired by NDSPDecodeLine */ if (pState->nRetrainState == -1) { /* Starting the search for a GCR line */ NDSPResetGCRAcquisition(pState); NDSPResetNabsyncAcquisition(pState); } NDSPAcquireGCR(pState, pbSamples); } if (pLineStats) { if (pLineStats->nSize < sizeof(NDSPGCRStats)) return NDSP_ERROR_ILLEGAL_STATS; memcpy((void*)pLineStats, (void*)&gcrStats, sizeof(NDSPGCRStats)); } return 0; } void NDSPResetGCRAcquisition(NDSPState *pState) { int i; for (i= 0; i< NDSP_SIGS_TO_ACQUIRE; i++) { pState->psmPosGCRs[i].dMaxval= 0.0; pState->psmNegGCRs[i].dMaxval= 0.0; } } void NDSPResetNabsyncAcquisition(NDSPState *pState) { int i; for (i= 0; i< NDSP_SIGS_TO_ACQUIRE; i++) { pState->psmSyncs[i].dMaxval= 0.0; } } /* Average together multiple signals. Use offsets computed by earlier convolution with target signal so that we correctly align the signals before averaging */ /* 1 if error */ /* 0 if OK */ int NDSPAvgSigs( NDSPState *pState, Double *pdDest, NDSPSigMatch *psm, Double *dConvRet) { int i,j; Double dConv= 0.0;; for (i= 0; i< pState->SamplesPerLine; i++) pdDest[i]= 0.0; if (psm[NDSP_SIGS_TO_ACQUIRE-1].dMaxval == 0.0) return 1; for (i= 0; i< pState->SamplesPerLine; i++) { int nSum= 0; for (j= 0; j< NDSP_SIGS_TO_ACQUIRE; j++) { int from_index= i + psm[j].nOffset-20;; if (from_index < 0) from_index= 0; if (from_index > (pState->SamplesPerLine-1)) from_index= (pState->SamplesPerLine-1); nSum += psm[j].pbSamples[from_index]; } pdDest[i]= ((Double)nSum) / NDSP_SIGS_TO_ACQUIRE; } for (j= 0; j< NDSP_SIGS_TO_ACQUIRE; j++) { dConv += psm[j].dMaxval; } dConv /= NDSP_SIGS_TO_ACQUIRE; if (dConvRet) *dConvRet= dConv; return 0; } /* We're trying to find the best representative matching signal during the "Acquire appropriate GCR and NABTS sync signals" stage (see NDSPProcessGCRLine). Maintain an array of the N best signals see so far */ void NDSPTryToInsertSig(NDSPState *pState, NDSPSigMatch *psm, Double dMaxval, unsigned char *pbSamples, int nOffset) { int i; double dMaxvalToReplace= dMaxval; int nIndexToReplace= -1; for (i= 0; i< NDSP_SIGS_TO_ACQUIRE; i++) { if (psm[i].dMaxval < dMaxvalToReplace) { nIndexToReplace= i; dMaxvalToReplace= psm[i].dMaxval; } } if (nIndexToReplace >= 0) { psm[nIndexToReplace].dMaxval= dMaxval; psm[nIndexToReplace].nOffset= nOffset; memcpy(psm[nIndexToReplace].pbSamples, pbSamples, pState->SamplesPerLine); } } void NDSPAcquireNabsync(NDSPState *pState, unsigned char *pbSamples) { Double dMaxval = 0.0; int nOffset = 0; MatchWithEqualizeSignal(pState, pbSamples, &eqmatchNabtsSync, &nOffset, &dMaxval, FALSE); NDSPTryToInsertSig(pState, pState->psmSyncs, dMaxval, pbSamples, nOffset); } void NDSPAcquireGCR(NDSPState *pState, unsigned char *pbSamples) { Double dMaxval = 0.0; int nOffset = 0; MatchWithEqualizeSignal(pState, pbSamples, &eqmatchGCR1, &nOffset, &dMaxval, TRUE); if (dMaxval > 0.0) { NDSPTryToInsertSig(pState, pState->psmPosGCRs, dMaxval, pbSamples, nOffset); } else { NDSPTryToInsertSig(pState, pState->psmNegGCRs, -dMaxval, pbSamples, nOffset); } } /* This table is approximately given by: 100 - (Chance of false positive / 3.2%) * 49. However, we encode what would otherwise be all 100% with numbers close to 100% to preserve some quality information, and what would otherwise be off the scale in the negative direction, we encode with numbers close to zero for the same reason */ int nConfidenceTable[25]= { 100, /* 0 */ 99, /* 1 */ 95, /* 2 */ 90, /* 3 */ 75, /* 4 */ 49, /* 5 */ 45, /* 6 */ 40, /* 7 */ 16, /* 8 */ 15, /* 9 */ 14, /* 10 */ 13, /* 11 */ 12, /* 12 */ 11, /* 13 */ 10, /* 14 */ 9, /* 15 */ 8, /* 16 */ 7, /* 17 */ 6, /* 18 */ 5, /* 19 */ 4, /* 20 */ 3, /* 21 */ 2, /* 22 */ 1, /* 23 */ 0, /* 24 */ }; int NDSPSyncBytesToConfidence(unsigned char *pbSyncBytes) { int nReal= (pbSyncBytes[0]<<16) + (pbSyncBytes[1]<<8) + (pbSyncBytes[2]<<0); int nIdeal= 0x5555e7; int nError= nReal ^ nIdeal; int nErrors= 0; /* Calculate the number of bit errors */ while (nError) { nError &= (nError-1); /* Remove the least significant bit of nError */ nErrors++; } /* What are the chances that random data would match the sync bytes? (2^24 / (24 choose nErrors)) #err % chance from random data ---- ------------------------- 0 0.00001% 1 0.00014% 2 0.00165% 3 0.01206% 4 0.06334% 5 0.25334% 6 0.80225% 7 2.06294% 8 4.38375% 9 7.79333% 10 11.69000% (These numbers aren't completely correct, since we found a spot in the input line that convolves most closely with what we're expecting; so the actual numbers depend a lot of the types of noise we get when receiving non-NABTS lines.) This code uses a cutoff of 4 bit errors. This was determined empirically, because, in practice, the convolution finds a fair number "reasonable" bits sometimes in random noise. */ SASSERT(nErrors >= 0 && nErrors <= 24); return nConfidenceTable[nErrors]; } /* Some sample locations at which a "positive" GCR signal should be quite positive */ /* ++++ Do these need to change on different sample rates? */ /* Compute confidence that we're looking at a GCR signal */ /* Do we think that pbSamples points at a GCR? Similar implementation to NDSPSyncBytesToConfidence, above */ int NDSPDetectGCRConfidence(unsigned char *pbSamples) { int i; int nTotalBits= 0; int nGoodBits= 0; int nCutoff= 30; int nConfidence; int nDC= float2long(Mean_8(pbSamples, g_nNabtsGcrSize * GCR_SAMPLE_RATE)); for (i= 0; i< sizeof(pnGCRPositiveIndices)/sizeof(int); i++) { nTotalBits++; nGoodBits += pbSamples[pnGCRPositiveIndices[i]] > nDC; } for (i= 0; i< sizeof(pnGCRNegativeIndices)/sizeof(int); i++) { nTotalBits++; nGoodBits += pbSamples[pnGCRNegativeIndices[i]] < nDC; } /* Cutoff is at 30 bits, based on statistics taken from samples. */ /* The actual confidence number here doesn't mean much except as a diagnostic */ if (nGoodBits >= nCutoff) { /* Good */ nConfidence= 51 + (nGoodBits - nCutoff) * 49 / (nTotalBits - nCutoff); } else { /* Bad */ nConfidence= nGoodBits * 49 / nCutoff; } debug_printf(("GCR: %d good bits, %d total bits, conf %d\n", nGoodBits, nTotalBits, nConfidence)); return nConfidence; } /* API for a possibly more efficient partial decode line. This might be useful for determining group addresses before decoding the rest of the line. Currently, this API is no more efficient than the full NDSPDecodeLine Public function: also see nabtsapi.h */ int NDSPPartialDecodeLine(unsigned char *pbDest, NDSPLineStats *pLineStats, unsigned char *pbSamples, NDSPState *pState, int nFECType, int nFieldNumber, int nLineNumber, int nStart, int nEnd, KS_VBIINFOHEADER *pVBIINFO) { debug_printf(("Warning: NDSPPartialDecodeLine uses slow implementation\n")); return NDSPDecodeLine(pbDest, pLineStats, pbSamples, pState, nFECType, nFieldNumber, nLineNumber, pVBIINFO); } /* Main API for decoding a NABTS line. * * Inputs: * pbSamples: pointer to 8-bit raw NABTS samples * pState: NDSPState to use for decoding * nFECType: Can be set to: * NDSP_NO_FEC (don't use FEC information) * NDSP_BUNDLE_FEC_1 (use Norpak-style bundle FEC info) * NDSP_BUNDLE_FEC_2 (use Wavephore-style bundle FEC info) * nFieldNumber: * A number that increments by one for each successive field. * "Odd" fields (as defined by NTSC) must be odd numbers * "Even" fields must be even numbers. * nLineNumber: * The NTSC line (starting from the top of the field) * from which this sample was taken. * * Outputs: * pbDest: decoded data ("NABTS_BYTES_PER_LINE" (36) bytes long) * pLineStats: stats on decoded data * * Errors: * * Returns 0 if no error * Returns NDSP_ERROR_ILLEGAL_NDSP_STATE if state is illegal or uses * unsupported settings * Returns NDSP_ERROR_ILLEGAL_STATS if stats is passed incorrectly * * Notes: * pbDest must point to a buffer at least 36 bytes long * pLineStats->nSize must be set to sizeof(*pLineStats) prior to call * (to maintain backwards compatibility should we add fields in the future) * pLineStats->nSize will be set to the size actually filled in by * the call (the number will stay the same or get smaller) * Currently the routine only supports a pState with an FIR filter * that has 5 taps * nFECType is currently unused, but would potentially be used to give * FEC feedback to the DSP decode, for possible tuning and/or retry Public function: also see nabtsapi.h */ #ifdef DEBUG extern void BPCplot(unsigned char *buf, unsigned long offs, unsigned long len); extern void BPCplotInd(unsigned char *buf, unsigned long offs, unsigned long len, long index); unsigned short NDSPplotSync = 0; unsigned short NDSPplotBreak = 0; unsigned short NDSPplotLen = 75; unsigned short NDSPplotBits = 0; unsigned short NDSPplotBitsSkip = 0; #endif //DEBUG int NDSPDecodeLine(unsigned char *pbDest, NDSPLineStats *pLineStats, unsigned char *pbSamples, NDSPState *pState, int nFECType, int nFieldNumber, int nLineNumber, KS_VBIINFOHEADER *pVBIINFO) { Double dSyncStart; Double dConvConfidence; int nConfidence; Double dDC, dAC; int ret; if (!pState) return NDSP_ERROR_ILLEGAL_NDSP_STATE; /* These args aren't yet used */ (void)nFieldNumber; (void)nLineNumber; /* Save number of samples */ pState->SamplesPerLine = pVBIINFO->SamplesPerLine; /* Locate the NABTS sync so that we know the correct offset from which to decode the signal */ NDSPDecodeFindSync(pbSamples, &dConvConfidence, &dSyncStart, &dDC, &dAC, pState, pVBIINFO); #ifdef DEBUG if (NDSPplotSync) { unsigned long lSyncStart; long index; lSyncStart = float2long(dSyncStart); DbgPrint("NDSPDecodeLine: SyncStart at %s\n", flPrintf(dSyncStart, 4)); index = lSyncStart - NDSPplotLen / 2; if (index < 0) index = 0; BPCplotInd(pbSamples, index, NDSPplotLen, lSyncStart - index); if (NDSPplotBreak) debug_breakpoint(); } #endif //DEBUG /* Perform the DSP decode */ ret= NDSPGetBits(pbDest, pbSamples, dSyncStart, dDC, pState, nFECType); /* Do the sync bytes look reasonable? */ nConfidence= NDSPSyncBytesToConfidence(pbDest); if (nConfidence >= 50 && pState->nRetrainState < 0) { /* If the sync bytes look reasonable, and we're in the middle of the retrain stage, possibly store this signal away for later equalization. (See NDSPProcessGCRLine for more information) */ NDSPAcquireNabsync(pState, pbSamples); } #ifdef DEBUG_VERBOSE if (g_bDebugSyncBytes) { debug_printf(("Sync found at %s with conv=%s, conf= %d, DC=%s, AC=%s\n", flPrintf(dSyncStart, 2), flPrintf(dConvConfidence, 6), nConfidence, flPrintf(dDC, 6), flPrintf(dAC, 6))); } #endif if (ret != 0) return ret; #ifdef DEBUG_VERBOSE if (g_bDebugSyncBytes) { debug_printf(("Sync bytes %x %x %x\n", pbDest[0], pbDest[1], pbDest[2])); } #endif /* Fill in the line stats structure */ if (pLineStats) { /* FUTURE: If we add fields, we'll need to change this logic */ if (pLineStats->nSize < sizeof(NDSPLineStats)) return NDSP_ERROR_ILLEGAL_STATS; pLineStats->nSize= sizeof(pLineStats); pLineStats->nConfidence= nConfidence; pLineStats->nFirstBit= float2long(dSyncStart); pLineStats->dDC= (double) dDC; } return 0; } /*********************/ /* PRIVATE FUNCTIONS */ /*********************/ /* Set the filter for the given DSP state */ int NDSPStateSetFilter(NDSPState *pState, FIRFilter *filter, int nFilterSize) { if (nFilterSize != sizeof(FIRFilter) || filter->nTaps < 1 || filter->nTaps > NDSP_MAX_FIR_COEFFS) { return NDSP_ERROR_ILLEGAL_FILTER; } if (!filter->bVariableTaps) { if ( !(filter->nTaps == 21 && filter->dTapSpacing == 2) && !(filter->nTaps == 5 && filter->dTapSpacing == 5) && !(filter->nTaps == 5 && filter->dTapSpacing == 4) && !(filter->nTaps == 5 && fltCmp(filter->dTapSpacing, KS_47NABTS_SCALER)) ) /* ===== add cases as needed ===== */ return NDSP_ERROR_ILLEGAL_FILTER; } // NormalizeFilter(&g_filterDefault); NormalizeFilter(filter); memcpy((void*) &pState->filter, (void*) filter, sizeof(FIRFilter)); return 0; } /* Verify that the "DC" content of the filter is 1. That is, the taps of the filter should add to 1. This way, when we convolve the filter with a signal, we don't change the DC content of the signal */ void NormalizeFilter(FIRFilter *pFilter) { Double dFactor= 1.0 / Sum_d(pFilter->pdTaps, pFilter->nTaps); Mult_d(pFilter->pdTaps, pFilter->pdTaps, pFilter->nTaps, dFactor); } /* Find the NABTS sync signal. Algorithm: Convolve a window around where the sync is expected with the actual transmitted sync waveform. This will give a very robust location for the sync (including the exact phase for best sampling). The typical disadvantage to convolution is that it is slow, but, for this particular waveform, it was possible to hardcode an algorithm that takes approximately 11 additive operations per location tested, rather than a more typical 24 multiplies and 72 additions per location tested. # of additive operations ~= (11 * (g_nNabtsDecodeBeginMax - g_nNabtsDecodeBeginMin)) The smaller the window between g_nNabtsDecodeBeginMin and g_nNabtsDecodeBeginMax, the faster this routine will run. */ #ifdef DEBUG int DSPsyncShowFineTune = 0; #endif //DEBUG void NDSPDecodeFindSync(unsigned char *pbSamples, Double *pdSyncConfidence, Double *pdSyncStart, Double *pdDC, Double *pdAC, NDSPState* pState, KS_VBIINFOHEADER *pVBIINFO) { Double dBestMatch= -1000000; Double dBestPos= 0; Double dStart; /* compute byte-based offsets from time-based offsets: */ #define NABTS_BITS_SEC (1.0/(double)NABTS_RATE) #define BEGIN_MIN_TIME (9.36e-6 - (pVBIINFO->ActualLineStartTime/1e8)) #define BEGIN_MAX_TIME (11.63e-6 - (pVBIINFO->ActualLineStartTime/1e8)) /* some shorthand... */ #define ITR_AR(arr, idx) _InterpUCharArr(arr, idx) Double dBegin_min_offset = BEGIN_MIN_TIME * (NABTS_RATE * pState->dSampleRate); Double dBegin_max_offset = BEGIN_MAX_TIME * (NABTS_RATE * pState->dSampleRate); /* +++++++ check that dBegin_min_offset >= 0, and dBegin_max_offset is not so high that decoding will go off the end of the array */ Double dSamp = pState->dSampleRate; for (dStart= dBegin_min_offset; dStart< dBegin_min_offset+dSamp; dStart += 1) { Double d= dStart; Double dMatch; Double dMatch1= ITR_AR(pbSamples,d+ 0*dSamp) - ITR_AR(pbSamples,d+ 1*dSamp) /* 0-1 */ + ITR_AR(pbSamples,d+ 2*dSamp) - ITR_AR(pbSamples,d+ 3*dSamp) /* 2-3 */ + ITR_AR(pbSamples,d+ 4*dSamp) - ITR_AR(pbSamples,d+ 5*dSamp) /* 4-5 */ + ITR_AR(pbSamples,d+ 6*dSamp) - ITR_AR(pbSamples,d+ 7*dSamp) /* 6-7 */ + ITR_AR(pbSamples,d+ 8*dSamp) - ITR_AR(pbSamples,d+ 9*dSamp) /* 8-9 */ + ITR_AR(pbSamples,d+10*dSamp) - ITR_AR(pbSamples,d+11*dSamp) /* 10-11 */ + ITR_AR(pbSamples,d+12*dSamp) - ITR_AR(pbSamples,d+13*dSamp) /* 12-13 */ + ITR_AR(pbSamples,d+14*dSamp) - ITR_AR(pbSamples,d+15*dSamp);/* 14-15 */ Double dMatch2= ITR_AR(pbSamples,d+16*dSamp) + ITR_AR(pbSamples,d+17*dSamp) + ITR_AR(pbSamples,d+18*dSamp) /* 0-2 */ - ITR_AR(pbSamples,d+19*dSamp) - ITR_AR(pbSamples,d+20*dSamp); /* 3-4 */ /* Note that we skip bits 5-7 of the third byte because they're not necessary for locating the sync, and this speeds us up a bit */ while (d < dBegin_max_offset) { dMatch= dMatch1 + dMatch2; if (dMatch > dBestMatch) { dBestMatch= dMatch; dBestPos= d; } /* Move dMatch1 forward by dSamp */ dMatch1 -= _InterpUCharArr(pbSamples,d+0*dSamp); dMatch1 += _InterpUCharArr(pbSamples,d+16*dSamp); dMatch1 = (-dMatch1); /* Move dMatch2 forward by dSamp */ dMatch2 -= _InterpUCharArr(pbSamples,d+21*dSamp); dMatch2 += (2 * _InterpUCharArr(pbSamples,d+19*dSamp)); dMatch2 -= _InterpUCharArr(pbSamples,d+16*dSamp); d += dSamp; } } #ifdef FINETUNE // Now try to fine-tune dBestPos by choosing the offset with the maximum // five-bit amplitude sum { Double d, dNewBest, dPeakSum, dNewSum; // Start by choosing dBestPos as the 'best' dNewBest = d = dBestPos; dPeakSum = /*ITR_AR(pbSamples,d+ 0*dSamp) - ITR_AR(pbSamples,d+ 1*dSamp) /* 0-1 */ + ITR_AR(pbSamples,d+ 2*dSamp) - ITR_AR(pbSamples,d+ 3*dSamp) /* 2-3 */ + ITR_AR(pbSamples,d+ 4*dSamp) - ITR_AR(pbSamples,d+ 5*dSamp) /* 4-5 */ + ITR_AR(pbSamples,d+ 6*dSamp) - ITR_AR(pbSamples,d+ 7*dSamp) /* 6-7 */ + ITR_AR(pbSamples,d+ 8*dSamp) - ITR_AR(pbSamples,d+ 9*dSamp) /* 8-9 */ + ITR_AR(pbSamples,d+10*dSamp) - ITR_AR(pbSamples,d+11*dSamp) /* 10-11 */ + ITR_AR(pbSamples,d+12*dSamp) - ITR_AR(pbSamples,d+13*dSamp) /* 12-13 */ + ITR_AR(pbSamples,d+14*dSamp) - ITR_AR(pbSamples,d+15*dSamp) /* 14-15 */ #if 0 + ITR_AR(pbSamples,d+16*dSamp) + ITR_AR(pbSamples,d+17*dSamp) + ITR_AR(pbSamples,d+18*dSamp) /* 0-2 */ - ITR_AR(pbSamples,d+19*dSamp) - ITR_AR(pbSamples,d+20*dSamp) /* 3-4 */ #endif ; for (d = dBestPos - 1; d < dBestPos + 2; d += 0.1) { dNewSum = /*ITR_AR(pbSamples,d+ 0*dSamp) - ITR_AR(pbSamples,d+ 1*dSamp); /* 0-1 */ + ITR_AR(pbSamples,d+ 2*dSamp) - ITR_AR(pbSamples,d+ 3*dSamp) /* 2-3 */ + ITR_AR(pbSamples,d+ 4*dSamp) - ITR_AR(pbSamples,d+ 5*dSamp) /* 4-5 */ + ITR_AR(pbSamples,d+ 6*dSamp) - ITR_AR(pbSamples,d+ 7*dSamp) /* 6-7 */ + ITR_AR(pbSamples,d+ 8*dSamp) - ITR_AR(pbSamples,d+ 9*dSamp) /* 8-9 */ + ITR_AR(pbSamples,d+10*dSamp) - ITR_AR(pbSamples,d+11*dSamp) /* 10-11 */ + ITR_AR(pbSamples,d+12*dSamp) - ITR_AR(pbSamples,d+13*dSamp) /* 12-13 */ + ITR_AR(pbSamples,d+14*dSamp) - ITR_AR(pbSamples,d+15*dSamp) /* 14-15 */ #if 0 + ITR_AR(pbSamples,d+16*dSamp) + ITR_AR(pbSamples,d+17*dSamp) + ITR_AR(pbSamples,d+18*dSamp) /* 0-2 */ - ITR_AR(pbSamples,d+19*dSamp) - ITR_AR(pbSamples,d+20*dSamp) /* 3-4 */ #endif ; if (dNewSum > dPeakSum) { dPeakSum = dNewSum; dNewBest = d; } } if (dNewBest != dBestPos) { if (DSPsyncShowFineTune) debug_printf(("NDSPDecodeFindSync: fine-tuned dBestPos from %s to %s\n", flPrintf(dBestPos,6), flPrintf(dNewBest,6))); dBestPos = dNewBest; } } #endif //FINETUNE *pdSyncStart= dBestPos; { /* Calculate DC offset */ Double dDC= 0, dAC= 0; Double d; for (d= dBestPos; d < dBestPos + (16*dSamp); d += dSamp) { dDC += _InterpUCharArr(pbSamples,d); } dDC /= 16.0; /* Calculate AC amplitude */ for (d= dBestPos; d < dBestPos + (16*dSamp); d += dSamp) { Double tmp = _InterpUCharArr(pbSamples,d)-dDC; dAC += (tmp < 0)? -tmp : tmp; } dAC /= 16.0; if (dAC < 1.0) dAC= 1.0; /* Prevent divide by 0 later */ *pdDC= dDC; *pdAC= dAC; /* Confidence is from 0 to approximately 96 (4 * 24) */ /* Subtract out 1*nDC since we had one more high bit than low in our convolution */ *pdSyncConfidence= 2 * (dBestMatch - dDC) / dAC; /* If dAC < 20, reduce the confidence */ *pdSyncConfidence = (*pdSyncConfidence * dAC / 20); } } /* Perform the DSP to get the NABTS bits from the raw sample line. The basic algorithm is to convolve the input signal with the current filter, and detect for each bit whether the bit is above or below the DC level as determined by looking at the NABTS sync, which is a known signal. This is where we spend most of our cycles, so there are lots of specially-cased hard-coded routines to handle the different filter configurations we currently use. */ int NDSPGetBits(unsigned char *pbDest, unsigned char *pbSamples, Double dSyncStart, double dDC, NDSPState *pState, int nFECType) { int ret= 0; if (pState->filter.bVariableTaps) { /* Variable-tap filter */ ret= NDSPGetBits_var(pbDest, pbSamples, float2long(dSyncStart), dDC, pState, nFECType); } else { /* change to multiple else if's */ if ( pState->filter.nTaps == 21 && pState->filter.dTapSpacing == 2) { /* Fixed taps {-20, -18, ... 18, 20} */ ret= NDSPGetBits_21_2(pbDest, pbSamples, float2long(dSyncStart), dDC, pState, nFECType); } else if ( pState->filter.nTaps == 5 && fltCmp(pState->filter.dTapSpacing, 5)) { /* Fixed taps {-10, -5, 0, 5, 10} */ ret= NDSPGetBits_5_5(pbDest, pbSamples, float2long(dSyncStart), dDC, pState, nFECType); } else if ( pState->filter.nTaps == 5 && fltCmp(pState->filter.dTapSpacing, 4)) { ret= NDSPGetBits_5_4(pbDest, pbSamples, float2long(dSyncStart), dDC, pState, nFECType); } else if ( pState->filter.nTaps == 5 && fltCmp(pState->filter.dTapSpacing,KS_47NABTS_SCALER) ) { ret= NDSPGetBits_5_47(pbDest, pbSamples, dSyncStart, dDC, pState, nFECType); } else { ret= NDSP_ERROR_ILLEGAL_NDSP_STATE; } } return ret; } /* Get bits for fixed taps {-10, -5, 0, 5, 10} See NDSPGetBits for more information */ int NDSPGetBits_5_5(unsigned char *pbDest, unsigned char *pbSamples, int nSyncStart, double dDC, NDSPState *pState, int nFECType) { int nSamplePos= nSyncStart; int i; Double a,b,c,d,e; #ifdef UseDoubleCoeffs Double dCoeffA= pState->filter.pdTaps[0]; Double dCoeffB= pState->filter.pdTaps[1]; Double dCoeffC= pState->filter.pdTaps[2]; Double dCoeffD= pState->filter.pdTaps[3]; Double dCoeffE= pState->filter.pdTaps[4]; dDC *= (dCoeffA + dCoeffB + dCoeffC + dCoeffD + dCoeffE); #else //UseDoubleCoeffs Double *pdTaps = pState->filter.pdTaps; int dCoeffA= float2long(pdTaps[0]); int dCoeffB= float2long(pdTaps[1]); int dCoeffC= float2long(pdTaps[2]); int dCoeffD= float2long(pdTaps[3]); int dCoeffE= float2long(pdTaps[4]); dDC *= (dCoeffA + dCoeffB + dCoeffC + dCoeffD + dCoeffE); //dDC *= (pdTaps[0] + pdTaps[1] + pdTaps[2] + pdTaps[3] + pdTaps[4]); #endif //UseDoubleCoeffs (void)nFECType; SASSERT(pState); SASSERT(pState->filter.nTaps == 5); SASSERT(pState->filter.dTapSpacing == 5); dDC *= (dCoeffA + dCoeffB + dCoeffC + dCoeffD + dCoeffE); if (nSamplePos-10 >= 0) { a= (Double) pbSamples[nSamplePos-10]; } else { a= 0.0; } if (nSamplePos-5 >= 0) { b= (Double) pbSamples[nSamplePos- 5]; } else { b= 0.0; } c= (Double) pbSamples[nSamplePos+ 0]; d= (Double) pbSamples[nSamplePos+ 5]; e= (Double) pbSamples[nSamplePos+10]; #ifdef DEBUG NDSPplotBitsSkip = 0; #endif //DEBUG for (i= 0; i< NABTS_BYTES_PER_LINE; i++) { int j; int nByte= 0; for (j= 0; j< 8; j++) { #ifdef DEBUG int bit = 0; #endif //DEBUG nByte >>= 1; if (a*dCoeffA + b*dCoeffB + c*dCoeffC + d*dCoeffD + e*dCoeffE > dDC) { nByte |= 128; #ifdef DEBUG bit = 1; #endif //DEBUG } #ifdef DEBUG if (NDSPplotBits && !NDSPplotBitsSkip) { long index; DbgPrint("NDSPGetBits_5_5: nSamplePos = %d, dDC = %s, bit = %d\n", nSamplePos, flPrintf(dDC, 4), bit); index = nSamplePos - NDSPplotLen / 2; if (index < 0) index = 0; if (index + NDSPplotLen > pState->SamplesPerLine) index = pState->SamplesPerLine - NDSPplotLen; BPCplotInd(pbSamples, index, NDSPplotLen, nSamplePos - index); if (NDSPplotBreak) debug_breakpoint(); } #endif //DEBUG a=b; b=c; c=d; d=e; nSamplePos += 5; if (nSamplePos+10 < pState->SamplesPerLine ) { e= (Double) pbSamples[nSamplePos+10]; } else { e= 0.0; } } pbDest[i]= nByte; } return 0; } /* Get bits for fixed taps {-9.428, -4.714, 0, 4.714, 9.428} //FIXME See NDSPGetBits for more information */ int NDSPGetBits_5_47(unsigned char *pbDest, unsigned char *pbSamples, Double dSyncStart, double dDC, NDSPState *pState, int nFECType) { double dSamplePos= dSyncStart; int i; Double dSpacing; #ifdef UseMultiTaps Double a,b,c,d,e; # ifdef UseDoubleCoeffs Double dCoeffA= pState->filter.pdTaps[0]; Double dCoeffB= pState->filter.pdTaps[1]; Double dCoeffC= pState->filter.pdTaps[2]; Double dCoeffD= pState->filter.pdTaps[3]; Double dCoeffE= pState->filter.pdTaps[4]; dDC *= (dCoeffA + dCoeffB + dCoeffC + dCoeffD + dCoeffE); # else //UseDoubleCoeffs Double *pdTaps = pState->filter.pdTaps; int dCoeffA= float2long(pdTaps[0]); int dCoeffB= float2long(pdTaps[1]); int dCoeffC= float2long(pdTaps[2]); int dCoeffD= float2long(pdTaps[3]); int dCoeffE= float2long(pdTaps[4]); dDC *= (dCoeffA + dCoeffB + dCoeffC + dCoeffD + dCoeffE); //dDC *= (pdTaps[0] + pdTaps[1] + pdTaps[2] + pdTaps[3] + pdTaps[4]); # endif //UseDoubleCoeffs #else //UseMultiTaps #endif //UseMultiTaps (void)nFECType; SASSERT(pState); SASSERT(pState->filter.nTaps == 5); SASSERT(fltCmp(pState->filter.dTapSpacing, KS_47NABTS_SCALER)); dSpacing = pState->filter.dTapSpacing; #ifdef UseMultiTaps if (dSamplePos-(2*KS_47NABTS_SCALER) >= 0) { a= (Double) _InterpUCharArr(pbSamples, dSamplePos-2*dSpacing); } else { a= 0.0; } if (dSamplePos-KS_47NABTS_SCALER >= 0) { b= (Double) _InterpUCharArr(pbSamples, dSamplePos-dSpacing); } else { b= 0.0; } c= (Double) _InterpUCharArr(pbSamples, dSamplePos + 0 ); d= (Double) _InterpUCharArr(pbSamples, dSamplePos + dSpacing ); e= (Double) _InterpUCharArr(pbSamples, dSamplePos + 2*dSpacing ); #endif //UseMultiTaps #ifdef DEBUG NDSPplotBitsSkip = 0; #endif //DEBUG for (i= 0; i< NABTS_BYTES_PER_LINE; i++) { int j; int nByte= 0; for (j= 0; j< 8; j++) { #ifdef DEBUG int bit = 0; #endif //DEBUG nByte >>= 1; #ifdef UseMultiTaps if (a*dCoeffA + b*dCoeffB + c*dCoeffC + d*dCoeffD + e*dCoeffE > dDC) #else //UseMultiTaps if (_InterpUCharArr(pbSamples, dSamplePos) > dDC) #endif //UseMultiTaps { nByte |= 128; #ifdef DEBUG bit = 1; #endif //DEBUG } #ifdef DEBUG if (NDSPplotBits && !NDSPplotBitsSkip) { unsigned long lSPint; long index; lSPint = float2long(dSamplePos); DbgPrint("NDSPGetBits_5_47: dSamplePos = %s, dDC = %s, bit = %d\n", flPrintf(dSamplePos, 4), flPrintf(dDC, 4), bit); index -= lSPint - NDSPplotLen / 2; if (index < 0) index = 0; BPCplotInd(pbSamples, index, NDSPplotLen, lSPint - index); if (NDSPplotBreak) debug_breakpoint(); } #endif //DEBUG dSamplePos += dSpacing; #ifdef UseMultiTaps a=b; b=c; c=d; d=e; if ( dSamplePos+(2*dSpacing) < pState->SamplesPerLine ) { e= _InterpUCharArr(pbSamples, dSamplePos+2*dSpacing); } else { e= 0.0; } #endif //UseMultiTaps } pbDest[i]= nByte; } return 0; } /* Get bits for fixed taps {-8, -4, 0, 4, 8} See NDSPGetBits for more information */ int NDSPGetBits_5_4(unsigned char *pbDest, unsigned char *pbSamples, int nSyncStart, double dDC, NDSPState *pState, int nFECType) { int nSamplePos= nSyncStart; int i; Double a,b,c,d,e; #ifdef UseDoubleCoeffs Double dCoeffA= pState->filter.pdTaps[0]; Double dCoeffB= pState->filter.pdTaps[1]; Double dCoeffC= pState->filter.pdTaps[2]; Double dCoeffD= pState->filter.pdTaps[3]; Double dCoeffE= pState->filter.pdTaps[4]; dDC *= (dCoeffA + dCoeffB + dCoeffC + dCoeffD + dCoeffE); #else //UseDoubleCoeffs Double *pdTaps = pState->filter.pdTaps; int dCoeffA= float2long(pdTaps[0]); int dCoeffB= float2long(pdTaps[1]); int dCoeffC= float2long(pdTaps[2]); int dCoeffD= float2long(pdTaps[3]); int dCoeffE= float2long(pdTaps[4]); dDC *= (dCoeffA + dCoeffB + dCoeffC + dCoeffD + dCoeffE); //dDC *= (pdTaps[0] + pdTaps[1] + pdTaps[2] + pdTaps[3] + pdTaps[4]); #endif //UseDoubleCoeffs (void)nFECType; SASSERT(pState); SASSERT(pState->filter.nTaps == 5); SASSERT(pState->filter.dTapSpacing == 4); if (nSamplePos-8 >= 0) { a= (Double) pbSamples[nSamplePos-8]; } else { a= 0.0; } if (nSamplePos-4 >= 0) { b= (Double) pbSamples[nSamplePos- 4]; } else { b= 0.0; } c= (Double) pbSamples[nSamplePos+ 0]; d= (Double) pbSamples[nSamplePos+ 4]; e= (Double) pbSamples[nSamplePos+8]; for (i= 0; i< NABTS_BYTES_PER_LINE; i++) { int j; int nByte= 0; for (j= 0; j< 8; j++) { nByte >>= 1; if (a*dCoeffA + b*dCoeffB + c*dCoeffC + d*dCoeffD + e*dCoeffE > dDC) nByte |= 128; a=b; b=c; c=d; d=e; nSamplePos += 4; if (nSamplePos+8 < pState->SamplesPerLine ) { e= (Double) pbSamples[nSamplePos+8]; } else { e= 0.0; } } pbDest[i]= nByte; } return 0; } #if 0 int NDSPGetBits_no_filter(unsigned char *pbDest, unsigned char *pbSamples, int nSyncStart, double dDC, NDSPState *pState, int nFECType) { int nSamplePos= nSyncStart; int i; (void)nFECType; SASSERT(pState); for (i= 0; i< NABTS_BYTES_PER_LINE; i++) { int j; int nByte= 0; for (j= 0; j< 8; j++) { nByte >>= 1; if (pbSamples[nSamplePos] > dDC) nByte |= 128; nSamplePos += float2long(pState->dSampleRate); // dSR should be made non-floating point. } pbDest[i]= nByte; } return 0; } #endif //0 Double Sum_d(Double *a, int a_size) { Double sum= 0.0; int i; SASSERT(a_size>=0); for (i= 0; i< a_size; i++) sum += a[i]; return sum; } /* Get bits for fixed taps {-20, -18, ... 18, 20} See NDSPGetBits for more information */ int NDSPGetBits_21_2(unsigned char *pbDest, unsigned char *pbSamples, int nSyncStart, double dDC, NDSPState *pState, int nFECType) { int nSamplePos= nSyncStart; int i; Double *pdLine; Double *pdTaps; Double dDCa, dDCb; int nTaps; int nTapSpacing; int nTail; int index; unsigned char pbBuf[NABTS_BYTES_PER_LINE]; /*unsigned char pbBufa[NABTS_BYTES_PER_LINE];*/ /*unsigned char pbBufb[NABTS_BYTES_PER_LINE];*/ SASSERT(pState); SASSERT(pState->filter.nTaps == 21); SASSERT(pState->filter.dTapSpacing == 2); SASSERT(!pState->bUsingScratch1); pState->bUsingScratch1= __LINE__; pdLine= pState->pdScratch1; SASSERT(sizeof(pState->pdScratch1)/sizeof(Double) >= pState->SamplesPerLine+(21-1)*2); pdTaps= pState->filter.pdTaps; nTaps= pState->filter.nTaps; nTapSpacing= float2long(pState->filter.dTapSpacing); nTail= (nTaps-1)/2; dDCa= dDC-1.0; dDCb= dDC+1.0; dDC *= Sum_d(pdTaps, nTaps); dDCa *= Sum_d(pdTaps, nTaps); dDCb *= Sum_d(pdTaps, nTaps); for (i= 0; i< pState->SamplesPerLine; i++) { index= float2long(i+nTail*nTapSpacing); SASSERT(index >= 0 && index < sizeof(pState->pdScratch1)/sizeof(Double)); pdLine[index]= (Double) pbSamples[i]; } for (i= 0; i< nTail*nTapSpacing; i++) { index= i; SASSERT(index >= 0 && index < sizeof(pState->pdScratch1)/sizeof(Double)); pdLine[index]= dDC; index= float2long(i + pState->SamplesPerLine + nTail * nTapSpacing); SASSERT(index >= 0 && index < sizeof(pState->pdScratch1)/sizeof(Double)); pdLine[index]= dDC; } nSamplePos += 20; for (i= 0; i< NABTS_BYTES_PER_LINE; i++) { int j; int nByte= 0; /*int nBytea= 0;*/ /*int nByteb= 0;*/ for (j= 0; j< 8; j++) { Double dSum; nByte >>= 1; #if 0 nBytea >>= 1; nByteb >>= 1; #endif SASSERT(nSamplePos-20 >= 0); SASSERT(nSamplePos+20 < sizeof(pState->pdScratch1)/sizeof(Double)); dSum= pdTaps[ 0] * pdLine[nSamplePos-20] + pdTaps[ 1] * pdLine[nSamplePos-18] + pdTaps[ 2] * pdLine[nSamplePos-16] + pdTaps[ 3] * pdLine[nSamplePos-14] + pdTaps[ 4] * pdLine[nSamplePos-12] + pdTaps[ 5] * pdLine[nSamplePos-10] + pdTaps[ 6] * pdLine[nSamplePos- 8] + pdTaps[ 7] * pdLine[nSamplePos- 6] + pdTaps[ 8] * pdLine[nSamplePos- 4] + pdTaps[ 9] * pdLine[nSamplePos- 2] + pdTaps[10] * pdLine[nSamplePos+ 0] + pdTaps[11] * pdLine[nSamplePos+ 2] + pdTaps[12] * pdLine[nSamplePos+ 4] + pdTaps[13] * pdLine[nSamplePos+ 6] + pdTaps[14] * pdLine[nSamplePos+ 8] + pdTaps[15] * pdLine[nSamplePos+10] + pdTaps[16] * pdLine[nSamplePos+12] + pdTaps[17] * pdLine[nSamplePos+14] + pdTaps[18] * pdLine[nSamplePos+16] + pdTaps[19] * pdLine[nSamplePos+18] + pdTaps[20] * pdLine[nSamplePos+20]; if (dSum > dDC) nByte |= 128; #if 0 if (dSum > dDCa) nBytea |= 128; if (dSum > dDCb) nByteb |= 128; #endif nSamplePos += 5; } pbBuf[i]= nByte; #if 0 pbBufa[i]= nBytea; pbBufb[i]= nByteb; #endif } { /*fec_error_class std, a, b;*/ unsigned char *pbWinner; #if 0 if (nFECType == NDSP_BUNDLE_FEC_1) { std= check_fec(pbBuf); a= check_fec(pbBufa); b= check_fec(pbBufb); if (std <= a && std <= b) { pbWinner= pbBuf; } else if (aSamplesPerLine; i++) { pbDest[i]= pbWinner[i]; } } pdLine= NULL; pState->bUsingScratch1= FALSE; return 0; } /* Get bits for variable fixed taps. Since in the general case this is inefficient, we check for the various variable-tap filter configurations we use. See NDSPGetBits for more information */ int NDSPGetBits_var(unsigned char *pbDest, unsigned char *pbSamples, int nSyncStart, double dDC, NDSPState *pState, int nFECType) { int nSamplePos= nSyncStart; int i; Double *pdLine; Double *pdTaps; int *pnTapLocs; int nTaps; int nFirstTap; int nLastTap; int index; int iSampRate = float2long(pState->dSampleRate); SASSERT(pState); if (FilterIsVar(&pState->filter, 3, 11, 9, 0)) { /* Taps are -33, -30 ... 24, 27 */ return NDSPGetBits_var_3_11_9_0(pbDest, pbSamples, nSyncStart, dDC, pState, nFECType); } if (FilterIsVar(&pState->filter, 3, 11, 9, 2)) { /* Taps are -33, -30 ... 24, 27, X, Y where X and Y are any number */ return NDSPGetBits_var_3_11_9_2(pbDest, pbSamples, nSyncStart, dDC, pState, nFECType); } if (FilterIsVar(&pState->filter, 2, 11, 9, 2)) { return NDSPGetBits_var_2_11_9_2(pbDest, pbSamples, nSyncStart, dDC, pState, nFECType); } if (FilterIsVar(&pState->filter, 5, 2, 2, 0)) { /* Taps are -10, -5, 0, 5, 10 */ return NDSPGetBits_var_5_2_2_0(pbDest, pbSamples, nSyncStart, dDC, pState, nFECType); } /* ===== add another "case" for speedy filter for 4x here ===== */ /* If we get this far in the field, we're probably doing the wrong thing! See NDSPGetBits for the general algorithm followed */ // TODO - BOTH 4x and 4.7x #ifdef DEBUG { static int nWarning= 0; nWarning++; if ((nWarning % 1024) == 0) { debug_printf(("Warning: doing slow convolution\n")); } } #endif //DEBUG nFirstTap= pState->filter.nMinTap; nLastTap= pState->filter.nMaxTap; SASSERT(!pState->bUsingScratch1); pState->bUsingScratch1= __LINE__; pdLine= pState->pdScratch1; SASSERT(sizeof(pState->pdScratch1)/sizeof(Double) >= pState->SamplesPerLine-nFirstTap+nLastTap); pdTaps= pState->filter.pdTaps; pnTapLocs= pState->filter.pnTapLocs; nTaps= pState->filter.nTaps; dDC *= Sum_d(pdTaps, nTaps); for (i= 0; i< pState->SamplesPerLine; i++) { index= i-nFirstTap; SASSERT(index >= 0 && index < sizeof(pState->pdScratch1)/sizeof(Double)); pdLine[index]= (Double) pbSamples[i]; } for (i= 0; i< -nFirstTap; i++) { index= i; SASSERT(index >= 0 && index < sizeof(pState->pdScratch1)/sizeof(Double)); pdLine[index]= dDC; } for (i= 0; i< nLastTap; i++) { index= i + pState->SamplesPerLine -nFirstTap; SASSERT(index >= 0 && index < sizeof(pState->pdScratch1)/sizeof(Double)); pdLine[index]= dDC; } nSamplePos += (-nFirstTap); for (i= 0; i< NABTS_BYTES_PER_LINE; i++) { int j,k; int nByte= 0; for (j= 0; j< 8; j++) { Double dSum= 0.0; nByte >>= 1; SASSERT(nSamplePos+nFirstTap >= 0); SASSERT(nSamplePos+nLastTap < sizeof(pState->pdScratch1)/sizeof(Double)); for (k= 0; k< nTaps; k++) { dSum += pdTaps[k] * pdLine[nSamplePos + pnTapLocs[k]]; } if (dSum > dDC) nByte |= 128; nSamplePos += iSampRate; } pbDest[i]= nByte; } pdLine= NULL; pState->bUsingScratch1= FALSE; return 0; } /* Check to see if the variable-tap filter matches the configuration we're looking for. nSpacing is the spacing between the "main" taps. nLeft is the number of "main" taps less than zero. nRight is the number of "main" taps greater than zero. nExtra is the number of "extra" variable taps at the end which don't necessarily fit in with nSpacing spacing. We want to match taps looking like: -nLeft * nSpacing, -(nLeft-1) * nSpacing, ... 0, ... (nRight-1) * nSpacing, nRight * nSpacing, X-sub-1, X-sub-2, ... X-sub-nExtra Where X-sub-N can be any number */ BOOL FilterIsVar(FIRFilter *pFilter, int nSpacing, int nLeft, int nRight, int nExtra) { int i; if (!pFilter->bVariableTaps) return FALSE; if (nLeft + nRight + 1 + nExtra != pFilter->nTaps) return FALSE; for (i= 0; i< nLeft + nRight + 1; i++) { if (pFilter->pnTapLocs[i] != (i-nLeft)*nSpacing) return FALSE; } return TRUE; } /* Special case DSP for variable-tap filter having taps -10, -5, 0, 5, 10. See NDSPGetBits for information on the algorithm, and NDSPGetBits_var for more details */ int NDSPGetBits_var_5_2_2_0(unsigned char *pbDest, unsigned char *pbSamples, int nSyncStart, double dDC, NDSPState *pState, int nFECType) { int nSamplePos= nSyncStart; int i; Double *pdLine; Double *pdTaps; int *pnTapLocs; int nTaps; int nFirstTap; int nLastTap; int index; int nSampleInc = float2long(pState->dSampleRate); SASSERT(pState); SASSERT(FilterIsVar(&pState->filter,5,2,2,0)); nFirstTap= pState->filter.nMinTap; nLastTap= pState->filter.nMaxTap; SASSERT(!pState->bUsingScratch1); pState->bUsingScratch1= __LINE__; pdLine= pState->pdScratch1; SASSERT(sizeof(pState->pdScratch1)/sizeof(Double) >= pState->SamplesPerLine-nFirstTap+nLastTap); pdTaps= pState->filter.pdTaps; pnTapLocs= pState->filter.pnTapLocs; nTaps= pState->filter.nTaps; dDC *= Sum_d(pdTaps, nTaps); for (i= 0; i< pState->SamplesPerLine; i++) { index= i-nFirstTap; SASSERT(index >= 0 && index < sizeof(pState->pdScratch1)/sizeof(Double)); pdLine[index]= (Double) pbSamples[i]; } for (i= 0; i< -nFirstTap; i++) { index= i; SASSERT(index >= 0 && index < sizeof(pState->pdScratch1)/sizeof(Double)); pdLine[index]= dDC; } for (i= 0; i< nLastTap; i++) { index= i + pState->SamplesPerLine -nFirstTap; SASSERT(index >= 0 && index < sizeof(pState->pdScratch1)/sizeof(Double)); pdLine[index]= dDC; } nSamplePos += (-nFirstTap); for (i= 0; i< NABTS_BYTES_PER_LINE; i++) { int j; int nByte= 0; for (j= 0; j< 8; j++) { Double dSum= 0.0; nByte >>= 1; SASSERT(nSamplePos+nFirstTap >= 0); SASSERT(nSamplePos+nLastTap < sizeof(pState->pdScratch1)/sizeof(Double)); dSum= pdTaps[0] * pdLine[nSamplePos - 10] + pdTaps[1] * pdLine[nSamplePos - 5] + pdTaps[2] * pdLine[nSamplePos + 0] + pdTaps[3] * pdLine[nSamplePos + 5] + pdTaps[4] * pdLine[nSamplePos + 10]; if (dSum > dDC) nByte |= 128; nSamplePos += nSampleInc; } pbDest[i]= nByte; } pdLine= NULL; pState->bUsingScratch1= FALSE; return 0; } /* Special case DSP for variable-tap filter having taps -33, -30 ... 24, 27 See NDSPGetBits for information on the algorithm, and NDSPGetBits_var for more details */ int NDSPGetBits_var_3_11_9_0(unsigned char *pbDest, unsigned char *pbSamples, int nSyncStart, double dDC, NDSPState *pState, int nFECType) { int nSamplePos= nSyncStart; int i; Double *pdLine; Double *pdTaps; int *pnTapLocs; int nTaps; int nFirstTap; int nLastTap; int index; int nSampleInc = float2long(pState->dSampleRate); SASSERT(pState); SASSERT(FilterIsVar(&pState->filter,3,11,9,0)); nFirstTap= pState->filter.nMinTap; nLastTap= pState->filter.nMaxTap; SASSERT(!pState->bUsingScratch1); pState->bUsingScratch1= __LINE__; pdLine= pState->pdScratch1; SASSERT(sizeof(pState->pdScratch1)/sizeof(Double) >= pState->SamplesPerLine-nFirstTap+nLastTap); pdTaps= pState->filter.pdTaps; pnTapLocs= pState->filter.pnTapLocs; nTaps= pState->filter.nTaps; dDC *= Sum_d(pdTaps, nTaps); for (i= 0; i< pState->SamplesPerLine; i++) { index= i-nFirstTap; SASSERT(index >= 0 && index < sizeof(pState->pdScratch1)/sizeof(Double)); pdLine[index]= (Double) pbSamples[i]; } for (i= 0; i< -nFirstTap; i++) { index= i; SASSERT(index >= 0 && index < sizeof(pState->pdScratch1)/sizeof(Double)); pdLine[index]= dDC; } for (i= 0; i< nLastTap; i++) { index= i + pState->SamplesPerLine -nFirstTap; SASSERT(index >= 0 && index < sizeof(pState->pdScratch1)/sizeof(Double)); pdLine[index]= dDC; } nSamplePos += (-nFirstTap); for (i= 0; i< NABTS_BYTES_PER_LINE; i++) { int j; int nByte= 0; for (j= 0; j< 8; j++) { Double dSum= 0.0; nByte >>= 1; SASSERT(nSamplePos+nFirstTap >= 0); SASSERT(nSamplePos+nLastTap < sizeof(pState->pdScratch1)/sizeof(Double)); dSum= pdTaps[0] * pdLine[nSamplePos - 33] + pdTaps[1] * pdLine[nSamplePos - 30] + pdTaps[2] * pdLine[nSamplePos - 27] + pdTaps[3] * pdLine[nSamplePos - 24] + pdTaps[4] * pdLine[nSamplePos - 21] + pdTaps[5] * pdLine[nSamplePos - 18] + pdTaps[6] * pdLine[nSamplePos - 15] + pdTaps[7] * pdLine[nSamplePos - 12] + pdTaps[8] * pdLine[nSamplePos - 9] + pdTaps[9] * pdLine[nSamplePos - 6] + pdTaps[10] * pdLine[nSamplePos - 3] + pdTaps[11] * pdLine[nSamplePos + 0] + pdTaps[12] * pdLine[nSamplePos + 3] + pdTaps[13] * pdLine[nSamplePos + 6] + pdTaps[14] * pdLine[nSamplePos + 9] + pdTaps[15] * pdLine[nSamplePos + 12] + pdTaps[16] * pdLine[nSamplePos + 15] + pdTaps[17] * pdLine[nSamplePos + 18] + pdTaps[18] * pdLine[nSamplePos + 21] + pdTaps[19] * pdLine[nSamplePos + 24] + pdTaps[20] * pdLine[nSamplePos + 27]; if (dSum > dDC) nByte |= 128; nSamplePos += nSampleInc; } pbDest[i]= nByte; } pdLine= NULL; pState->bUsingScratch1= FALSE; return 0; } /* Special case DSP for variable-tap filter having taps -33, -30 ... 24, 27, X, Y where X and Y are any number See NDSPGetBits for information on the algorithm, and NDSPGetBits_var for more details */ int NDSPGetBits_var_3_11_9_2(unsigned char *pbDest, unsigned char *pbSamples, int nSyncStart, double dDC, NDSPState *pState, int nFECType) { int nSamplePos= nSyncStart; int i; Double *pdLine; Double *pdTaps; int *pnTapLocs; int nTaps; int nFirstTap; int nLastTap; int index; int nSampleInc = float2long(pState->dSampleRate); SASSERT(pState); SASSERT(FilterIsVar(&pState->filter,3,11,9,2)); nFirstTap= pState->filter.nMinTap; nLastTap= pState->filter.nMaxTap; SASSERT(!pState->bUsingScratch1); pState->bUsingScratch1= __LINE__; pdLine= pState->pdScratch1; SASSERT(sizeof(pState->pdScratch1)/sizeof(Double) >= pState->SamplesPerLine-nFirstTap+nLastTap); pdTaps= pState->filter.pdTaps; pnTapLocs= pState->filter.pnTapLocs; nTaps= pState->filter.nTaps; dDC *= Sum_d(pdTaps, nTaps); for (i= 0; i< pState->SamplesPerLine; i++) { index= i-nFirstTap; SASSERT(index >= 0 && index < sizeof(pState->pdScratch1)/sizeof(Double)); pdLine[index]= (Double) pbSamples[i]; } for (i= 0; i< -nFirstTap; i++) { index= i; SASSERT(index >= 0 && index < sizeof(pState->pdScratch1)/sizeof(Double)); pdLine[index]= dDC; } for (i= 0; i< nLastTap; i++) { index= i + pState->SamplesPerLine -nFirstTap; SASSERT(index >= 0 && index < sizeof(pState->pdScratch1)/sizeof(Double)); pdLine[index]= dDC; } nSamplePos += (-nFirstTap); for (i= 0; i< NABTS_BYTES_PER_LINE; i++) { int j; int nByte= 0; for (j= 0; j< 8; j++) { Double dSum= 0.0; nByte >>= 1; SASSERT(nSamplePos+nFirstTap >= 0); SASSERT(nSamplePos+nLastTap < sizeof(pState->pdScratch1)/sizeof(Double)); dSum= pdTaps[0] * pdLine[nSamplePos - 33] + pdTaps[1] * pdLine[nSamplePos - 30] + pdTaps[2] * pdLine[nSamplePos - 27] + pdTaps[3] * pdLine[nSamplePos - 24] + pdTaps[4] * pdLine[nSamplePos - 21] + pdTaps[5] * pdLine[nSamplePos - 18] + pdTaps[6] * pdLine[nSamplePos - 15] + pdTaps[7] * pdLine[nSamplePos - 12] + pdTaps[8] * pdLine[nSamplePos - 9] + pdTaps[9] * pdLine[nSamplePos - 6] + pdTaps[10] * pdLine[nSamplePos - 3] + pdTaps[11] * pdLine[nSamplePos + 0] + pdTaps[12] * pdLine[nSamplePos + 3] + pdTaps[13] * pdLine[nSamplePos + 6] + pdTaps[14] * pdLine[nSamplePos + 9] + pdTaps[15] * pdLine[nSamplePos + 12] + pdTaps[16] * pdLine[nSamplePos + 15] + pdTaps[17] * pdLine[nSamplePos + 18] + pdTaps[18] * pdLine[nSamplePos + 21] + pdTaps[19] * pdLine[nSamplePos + 24] + pdTaps[20] * pdLine[nSamplePos + 27] + pdTaps[21] * pdLine[nSamplePos + pnTapLocs[21]] + pdTaps[22] * pdLine[nSamplePos + pnTapLocs[22]]; if (dSum > dDC) nByte |= 128; nSamplePos += nSampleInc; } pbDest[i]= nByte; } pdLine= NULL; pState->bUsingScratch1= FALSE; return 0; } int NDSPGetBits_var_2_11_9_2(unsigned char *pbDest, unsigned char *pbSamples, int nSyncStart, double dDC, NDSPState *pState, int nFECType) { int nSamplePos= nSyncStart; int i; Double *pdLine; Double *pdTaps; int *pnTapLocs; int nTaps; int nFirstTap; int nLastTap; int index; int nSampleInc = float2long(pState->dSampleRate); SASSERT(pState); SASSERT(FilterIsVar(&pState->filter,2,11,9,2)); nFirstTap= pState->filter.nMinTap; nLastTap= pState->filter.nMaxTap; SASSERT(!pState->bUsingScratch1); pState->bUsingScratch1= __LINE__; pdLine= pState->pdScratch1; SASSERT(sizeof(pState->pdScratch1)/sizeof(Double) >= pState->SamplesPerLine-nFirstTap+nLastTap); pdTaps= pState->filter.pdTaps; pnTapLocs= pState->filter.pnTapLocs; nTaps= pState->filter.nTaps; dDC *= Sum_d(pdTaps, nTaps); for (i= 0; i< pState->SamplesPerLine; i++) { index= i-nFirstTap; SASSERT(index >= 0 && index < sizeof(pState->pdScratch1)/sizeof(Double)); pdLine[index]= (Double) pbSamples[i]; } for (i= 0; i< -nFirstTap; i++) { index= i; SASSERT(index >= 0 && index < sizeof(pState->pdScratch1)/sizeof(Double)); pdLine[index]= dDC; } for (i= 0; i< nLastTap; i++) { index= i + pState->SamplesPerLine -nFirstTap; SASSERT(index >= 0 && index < sizeof(pState->pdScratch1)/sizeof(Double)); pdLine[index]= dDC; } nSamplePos += (-nFirstTap); for (i= 0; i< NABTS_BYTES_PER_LINE; i++) { int j; int nByte= 0; for (j= 0; j< 8; j++) { Double dSum= 0.0; nByte >>= 1; SASSERT(nSamplePos+nFirstTap >= 0); SASSERT(nSamplePos+nLastTap < sizeof(pState->pdScratch1)/sizeof(Double)); dSum= pdTaps[0] * pdLine[nSamplePos - 22] + pdTaps[1] * pdLine[nSamplePos - 20] + pdTaps[2] * pdLine[nSamplePos - 18] + pdTaps[3] * pdLine[nSamplePos - 16] + pdTaps[4] * pdLine[nSamplePos - 14] + pdTaps[5] * pdLine[nSamplePos - 12] + pdTaps[6] * pdLine[nSamplePos - 10] + pdTaps[7] * pdLine[nSamplePos - 8] + pdTaps[8] * pdLine[nSamplePos - 6] + pdTaps[9] * pdLine[nSamplePos - 4] + pdTaps[10] * pdLine[nSamplePos - 2] + pdTaps[11] * pdLine[nSamplePos + 0] + pdTaps[12] * pdLine[nSamplePos + 2] + pdTaps[13] * pdLine[nSamplePos + 4] + pdTaps[14] * pdLine[nSamplePos + 6] + pdTaps[15] * pdLine[nSamplePos + 8] + pdTaps[16] * pdLine[nSamplePos + 10] + pdTaps[17] * pdLine[nSamplePos + 12] + pdTaps[18] * pdLine[nSamplePos + 14] + pdTaps[19] * pdLine[nSamplePos + 16] + pdTaps[20] * pdLine[nSamplePos + 18] + pdTaps[21] * pdLine[nSamplePos + pnTapLocs[21]] + pdTaps[22] * pdLine[nSamplePos + pnTapLocs[22]]; if (dSum > dDC) nByte |= 128; nSamplePos += nSampleInc; } pbDest[i]= nByte; } pdLine= NULL; pState->bUsingScratch1= FALSE; return 0; } #ifdef DEBUG int debug_equalize= 0; void print_filter(FIRFilter *pFilt) { if (pFilt->bVariableTaps) { int i; debug_printf(("[FIR %dV [", pFilt->nTaps)); for (i= 0; i< pFilt->nTaps; i++) { if (i) debug_printf((" ")); debug_printf(("%d:%s", pFilt->pnTapLocs[i], flPrintf(pFilt->pdTaps[i],4))); } debug_printf(("]]")); } else { debug_printf(("[FIR %dx%s ", pFilt->nTaps, flPrintf(pFilt->dTapSpacing,3))); printarray_d1(pFilt->pdTaps, pFilt->nTaps); debug_printf(("]")); } } void printarray_d1(Double *arr, int n) { int i; debug_printf(("[")); for (i= 0; i< n; i++) { if (i) debug_printf((" ")); debug_printf(("%s", flPrintf(arr[i], 2))); } debug_printf(("]")); } void printarray_ed1(Double *arr, int n) { int i; debug_printf(("[")); for (i= 0; i< n; i++) { if (i) debug_printf((" ")); debug_printf(("%s", flPrintf(arr[i], 4))); } debug_printf(("]")); } void printarray_d2(Double *arr, int a_size, int b_size) { int a,b; for (a= 0; a< a_size; a++) { debug_printf(("|")); for (b= 0; b< b_size; b++) { if (b) debug_printf((" ")); debug_printf(("%s", flPrintf(arr[a*b_size+b], 4))); } debug_printf(("|\n")); } debug_printf(("\n")); } void printarray_ed2(Double *arr, int a_size, int b_size) { int a,b; for (a= 0; a< a_size; a++) { debug_printf(("|")); for (b= 0; b< b_size; b++) { if (b) debug_printf((" ")); debug_printf(("%s", flPrintf(arr[a*b_size+b], 4))); } debug_printf(("|\n")); } debug_printf(("\n")); } #endif //DEBUG #ifdef DEBUG_AREF #define a2d(arr,a,b,abound,bbound) (EASSERT(0<=(a)&&(a)<(abound)), \ EASSERT(0<=(b)&&(b)<(bbound)), \ (arr)[(a)*(bbound)+(b)]) #else #define a2d(arr,a,b,abound,bbound) ((arr)[(a)*(bbound)+(b)]) #endif #ifdef DEBUG_AREF #define a1d(arr,a,abound) (EASSERT(0<=(a)&&(a)<(abound)), \ (arr)[a]) #else #define a1d(arr,a,abound) ((arr)[a]) #endif #define aI(a,b) a2d(I,a,b,nFilterLength,nDesiredOutputLength) #define aItI(a,b) a2d(ItI,a,b,nFilterLength,nFilterLength) #define aIto(a) a1d(Ito,a,nFilterLength) #define ao(a) a1d(o,a,nDesiredOutputLength) #ifdef DEBUG_AREF #define aInput(a) (EASSERT(nMinInputIndex<=(a)&&(a)<=nMaxInputIndex), \ pfInput[a]) #else #define aInput(a) (pfInput[a]) #endif #define EQ_TOL .000001 void SubtractMean_d(Double *pfOutput, Double *pfInput, int nLen) { int i; Double dMean; if (!nLen) return; dMean= Mean_d(pfInput, nLen); for (i= 0; i< nLen; i++) pfOutput[i]= pfInput[i] - dMean; } /* Modification of above: Filter now has a variable number of taps */ void FillGCRSignals() { int i; SubtractMean_d(g_pdGCRSignal1, g_pdGCRSignal1, g_nNabtsGcrSize); for (i= 0; i< g_nNabtsGcrSize; i++) g_pdGCRSignal1[i] *= 100; for (i= 0; i< g_nNabtsGcrSize; i++) g_pdGCRSignal2[i]= -g_pdGCRSignal1[i]; } void NormalizeSyncSignal() { SubtractMean_d(g_pdSync, g_pdSync, g_nNabtsSyncSize); } Double Mean_d(Double *a, int a_size) { Double sum= 0.0; int i; SASSERT(a_size>0); for (i= 0; i< a_size; i++) sum += a[i]; return sum / a_size; } Double Mean_8(unsigned char *a, int a_size) { int sum= 0.0; int i; SASSERT(a_size>0); for (i= 0; i< a_size; i++) sum += a[i]; return ((Double) sum) / a_size; } void Mult_d(Double *dest, Double *src, int size, Double factor) { int i; for (i= 0; i< size; i++) dest[i]= src[i]*factor; } int Sum_16(short *a, int a_size) { int ret= 0; int i; for (i= 0; i< a_size; i++) ret += a[i]; return ret; } /* Try to match the input signal with a given EqualizeMatch desired signal template. If there is an apparent match, return offset at which match is found. */ int MatchWithEqualizeSignal(NDSPState *pState, unsigned char *pbSamples, EqualizeMatch *eqm, int *pnOffset, Double *pdMaxval, BOOL bNegativeOK) { Double dMaxval= 0.0; Double dMinval= 0.0; Double *pdSamples; Double *pdConv; int nMaxindex= 0; int nMinindex= 0; int i; int nStatus= 0; if (!pState || pState->uMagic != NDSP_STATE_MAGIC) return NDSP_ERROR_ILLEGAL_NDSP_STATE; SASSERT(!pState->bUsingScratch1); SASSERT(sizeof(pState->pdScratch1)/sizeof(Double) >= pState->SamplesPerLine); pdSamples= pState->pdScratch1; pState->bUsingScratch1= __LINE__; SASSERT(!pState->bUsingScratch2); SASSERT(sizeof(pState->pdScratch2)/sizeof(Double) >= pState->SamplesPerLine); pdConv= pState->pdScratch2; pState->bUsingScratch2= __LINE__; Copy_d_8(pdSamples, pbSamples, pState->SamplesPerLine); Convolve_d_d_d(pdConv, eqm->nSignalStartConv, eqm->nSignalEndConv, pdSamples, 0, pState->SamplesPerLine-1, eqm->nSignalSampleRate, eqm->pdSignal, 0, eqm->nSignalSize, 1, TRUE); for (i= eqm->nSignalStartConv; i<= eqm->nSignalEndConv; i++) { if (pdConv[i] > dMaxval) { dMaxval= pdConv[i]; nMaxindex= i; } if (pdConv[i] < dMinval) { dMinval= pdConv[i]; nMinindex= i; } } if ((-dMinval) > dMaxval && bNegativeOK) { dMaxval= dMinval; nMaxindex= nMinindex; } pdConv= NULL; pState->bUsingScratch2= FALSE; if (pnOffset) { (*pnOffset)= nMaxindex; } if (pdMaxval) { (*pdMaxval)= dMaxval; } pdSamples= NULL; pState->bUsingScratch1= FALSE; return nStatus; } /* Adaptive equalizer. Set the coefficients on a simple FIR filter such that the input waveform matches most closely the output (minimizing the sum of the squares of the error terms). i= input signal (vector) o= desirect output signal (vector) c= coefficients of FIR (vector) e= error (scalar) e= |convolution(i,c) - o|^2 We construct a matrix, I, such that I*c = convolution(i,c) Now we attempt to minimize e: e= |I*c - o|^2 This can be solved as the simple system of linear equations: (transpose(I)*I)*c = (transpose(I)*o) */ /* pfDesiredOutput must be defined over 0 ... nDesiredOutputLength-1 */ /* pfInput must be defined over -(nFilterLength-1)/2 ... nDesiredOutputLength-1 + (nFilterLength-1)/2 */ BOOL EqualizeVar(NDSPState *pState, Double *pfInput, int nMinInputIndex, int nMaxInputIndex, Double *pfDesiredOutput, int nDesiredOutputLength, int nOutputSpacing, FIRFilter *pFilter) { int nFilterLength= pFilter->nTaps; /* We spend most of our time in the n^3 multiplication of ItI, so we choose the following order of indices */ /* ItI[nFilterLength][nFilterLength] */ /* (transpose(I) * I) */ Double *ItI; /* Ito[nFilterLength] */ /* (transpose(I) * o) */ Double *Ito; /* o[nDesiredOutputLength] */ Double *o; BOOL bRet=FALSE; int x,y,i; SASSERT(!pState->bUsingScratch3); SASSERT(sizeof(pState->pdScratch3) >= nFilterLength * nFilterLength * sizeof(Double)); ItI= pState->pdScratch3; pState->bUsingScratch3= __LINE__; SASSERT(!pState->bUsingScratch4); SASSERT(sizeof(pState->pdScratch4) >= nFilterLength * sizeof(Double)); Ito= pState->pdScratch4; pState->bUsingScratch4= __LINE__; SASSERT(!pState->bUsingScratch5); SASSERT(sizeof(pState->pdScratch5) >= nDesiredOutputLength * sizeof(Double)); o= pState->pdScratch5; pState->bUsingScratch5= __LINE__; for (i= 0; i< nDesiredOutputLength; i++) { o[i]= pfDesiredOutput[i]; } /*memcpy(o, pfDesiredOutput, nDesiredOutputLength * sizeof(Double));*/ SASSERT(nFilterLength >= 1); /* Create (It)I */ /* Since ItI is symmetric, we only have to do a little over half the multiplies */ for (x= 0; x< nFilterLength; x++) { for (y= 0; y<=x; y++) { Double fSum= 0.0; for (i= 0; i< nDesiredOutputLength; i++) { fSum += aInput(pFilter->pnTapLocs[x]+nOutputSpacing*i) * aInput(pFilter->pnTapLocs[y]+nOutputSpacing*i); } aItI(x,y)= aItI(y,x)= fSum; } } /* Create (It)o */ for (x= 0; x< nFilterLength; x++) { Double fSum= 0.0; for (i= 0; i< nDesiredOutputLength; i++) { fSum += aInput(pFilter->pnTapLocs[x]+nOutputSpacing*i) * ao(i); } aIto(x)= fSum; } #ifdef DEBUG_VERBOSE if (debug_equalize) { debug_printf(("ItI:\n")); printarray_ed2(ItI, nFilterLength, nFilterLength); debug_printf(("Ito:\n")); printarray_ed1(Ito, nFilterLength); debug_printf(("\n")); } #endif /* Solve (ItI)c = Ito Since Gaussian elimination is the simplest, we've implemented it first. We can state a priori that no two coefficients are even closely dependent in the solution, given that the input and the output are related enough to pass the original confidence test. Therefore, if we find ItI to be to be close to singular, we can just reject it the sample, claiming that the GCR locater didn't do it's job. This should in fact never happen. */ /* We treat ItI as having indices [y][x] to speed the following. */ for (y= 0; y< nFilterLength; y++) { Double fMax= 0.0; int nMax= 0; /* Find the largest magnitude value in this column to swap with */ for (i= y; i< nFilterLength; i++) { Double fVal= aItI(i,y); if (fVal < 0) fVal = -fVal; if (fVal > fMax) { fMax= fVal; nMax= i; } } /* Swap the rows */ if (fMax < EQ_TOL) { #ifdef DEBUG_VERBOSE debug_printf(("Near-singular matrix in Equalize\n")); debug_printf(("Sync correlator must be broken\n")); #endif bRet= FALSE; goto exit; } if (nMax != y) { Double fTmp; for (x= y; x< nFilterLength; x++) { fTmp= aItI(y,x); aItI(y,x)= aItI(nMax,x); aItI(nMax,x)= fTmp; } fTmp= aIto(y); aIto(y)= aIto(nMax); aIto(nMax)= fTmp; } for (i= y+1; i< nFilterLength; i++) { Double fDependence= aItI(i,y) / aItI(y,y); aItI(i,y)= 0.0; for (x= y+1; x< nFilterLength; x++) { aItI(i,x) -= aItI(y,x) * fDependence; } aIto(i) -= aIto(y) * fDependence; } } /* Now ItI is upper diagonal */ for (y= nFilterLength - 1; y >= 0; y--) { for (i= 0; i< y; i++) { Double fDependence= aItI(i,y) / aItI(y,y); aItI(i,y)= 0.0; aIto(i) -= aIto(y) * fDependence; } } /* Now divide Ito by the diagonals */ for (i= 0; i< nFilterLength; i++) { aIto(i) /= aItI(i,i); } /* Ito now contains the answer */ /*memcpy(pfFilter, Ito, nFilterLength * sizeof(Double));*/ for (i= 0; i< nFilterLength; i++) { pFilter->pdTaps[i]= Ito[i]; } bRet= 1; exit: pState->bUsingScratch3= FALSE; pState->bUsingScratch4= FALSE; pState->bUsingScratch5= FALSE; return bRet; } int get_sync_samples(unsigned long newHZ) { debug_printf(("get_sync_samples(%lu) entered\n", newHZ)); memset(g_pdSync, 0, MAX_NABTS_SAMPLES_PER_LINE); switch (newHZ) { case KS_VBISAMPLINGRATE_4X_NABTS: memcpy(g_pdSync, g_pdSync4, NABSYNC_SIZE); break; case KS_VBISAMPLINGRATE_47X_NABTS: memcpy(g_pdSync, g_pdSync47, NABSYNC_SIZE); break; case KS_VBISAMPLINGRATE_5X_NABTS: memcpy(g_pdSync, g_pdSync5, NABSYNC_SIZE); break; default: // Unknown sampling rate debug_printf(("get_sync_samples: unknown sampling rate %lu\n", newHZ)); debug_breakpoint(); break; } return g_nNabtsSyncSize; } int get_gcr_samples(unsigned long newHZ) { int i; debug_printf(("get_gcr_samples(%lu) entered\n", newHZ)); switch (newHZ) { case KS_VBISAMPLINGRATE_4X_NABTS: for (i=0; i